From 8863579e7658d7759dbba445637e54b1cc75951a Mon Sep 17 00:00:00 2001
From: George Usynin <103181646+heorhi-deriv@users.noreply.github.com>
Date: Mon, 30 Dec 2024 15:48:24 +0300
Subject: [PATCH] [WALL] george / WALL-5234 / Wallets force migration
integration with GrowthBook (#17819)
* feat(wallets): :sparkles: implement force migration on feature flag
* refactor: streamline client data retrieval and enhance analytics logging (#44)
* refactor: update getAccountInfo to handle residence country retrieval and enhance analytics logging
---
.../__tests__/wallets-upgrade-modal.spec.tsx | 51 +++++++++++++++----
.../wallets-upgrade-modal.tsx | 28 ++++++----
packages/core/src/Stores/client-store.js | 33 ++++++++----
packages/core/src/Utils/Analytics/index.ts | 5 +-
4 files changed, 87 insertions(+), 30 deletions(-)
diff --git a/packages/appstore/src/components/modals/wallets-upgrade-modal/__tests__/wallets-upgrade-modal.spec.tsx b/packages/appstore/src/components/modals/wallets-upgrade-modal/__tests__/wallets-upgrade-modal.spec.tsx
index 95d85602dc6c..aca9374abc22 100644
--- a/packages/appstore/src/components/modals/wallets-upgrade-modal/__tests__/wallets-upgrade-modal.spec.tsx
+++ b/packages/appstore/src/components/modals/wallets-upgrade-modal/__tests__/wallets-upgrade-modal.spec.tsx
@@ -1,8 +1,10 @@
import React from 'react';
+
+import { useGrowthbookGetFeatureValue, useWalletMigration } from '@deriv/hooks';
+import { mockStore, StoreProvider } from '@deriv/stores';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { useWalletMigration } from '@deriv/hooks';
-import { StoreProvider, mockStore } from '@deriv/stores';
+
import WalletsUpgradeModal from '../wallets-upgrade-modal';
const mockStartMigration = jest.fn();
@@ -11,8 +13,11 @@ jest.mock('@deriv/hooks', () => ({
...jest.requireActual('@deriv/hooks'),
useWalletMigration: jest.fn(() => ({
is_eligible: true,
+ is_in_progress: false,
+ is_migrating: false,
startMigration: mockStartMigration,
})),
+ useGrowthbookGetFeatureValue: jest.fn(() => [false]),
}));
jest.mock('@deriv/stores', () => ({
@@ -33,7 +38,7 @@ describe('', () => {
document.body.removeChild(modal_root_el);
});
- it('Should show modal if user is eligible for migration and is_wallet_modal_closed is not in sessionStorage', () => {
+ it('shows modal if user is eligible for migration and is_wallet_modal_closed is not in sessionStorage', () => {
const mock = mockStore({
traders_hub: {
toggleWalletsUpgrade: jest.fn(),
@@ -52,11 +57,39 @@ describe('', () => {
render(, { wrapper });
+ const close_button = screen.getByTestId('dt_modal_close_icon');
+
+ expect(screen.getByText('Introducing Wallets')).toBeInTheDocument();
+ expect(screen.getByText('Enable now')).toBeInTheDocument();
+ expect(close_button).toBeInTheDocument();
+ });
+
+ it('shows modal without close button for force migration if force migration is enabled and user is not migrating or in progress', () => {
+ (useGrowthbookGetFeatureValue as jest.Mock).mockReturnValueOnce([true]);
+ const mock = mockStore({
+ traders_hub: {
+ toggleWalletsUpgrade: jest.fn(),
+ },
+ ui: {
+ is_mobile: false,
+ is_desktop: true,
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+ {children}
+ );
+
+ render(, { wrapper });
+
+ const close_button = screen.queryByTestId('dt_modal_close_icon');
+
expect(screen.getByText('Introducing Wallets')).toBeInTheDocument();
expect(screen.getByText('Enable now')).toBeInTheDocument();
+ expect(close_button).not.toBeInTheDocument();
});
- it('Should not show modal if user is not eligible for migration', () => {
+ it('does not show modal if user is not eligible for migration', () => {
const mock = mockStore({
traders_hub: {
toggleWalletsUpgrade: jest.fn(),
@@ -82,7 +115,7 @@ describe('', () => {
expect(screen.queryByText('Enable now')).not.toBeInTheDocument();
});
- it('Should not show modal if is_wallet_modal_closed is in sessionStorage', () => {
+ it('does not show modal if is_wallet_modal_closed is in sessionStorage', () => {
const mock = mockStore({
traders_hub: {
toggleWalletsUpgrade: jest.fn(),
@@ -107,7 +140,7 @@ describe('', () => {
expect(screen.queryByText('Enable now')).not.toBeInTheDocument();
});
- it('Should close modal when close button is clicked', () => {
+ it('closes modal when close button is clicked', async () => {
const mock = mockStore({
traders_hub: {
toggleWalletsUpgrade: jest.fn(),
@@ -128,13 +161,13 @@ describe('', () => {
render(, { wrapper });
const close_button = screen.getByTestId('dt_modal_close_icon');
- userEvent.click(close_button);
+ await userEvent.click(close_button);
expect(setModalOpen).toHaveBeenCalledWith(false);
expect(sessionStorage.getItem('is_wallet_migration_modal_closed')).toBe('true');
});
- it('Should start migration when Enable now button is clicked', () => {
+ it('starts migration when Enable now button is clicked', async () => {
const mock = mockStore({
traders_hub: {
toggleWalletsUpgrade: jest.fn(),
@@ -154,7 +187,7 @@ describe('', () => {
render(, { wrapper });
const enable_now_button = screen.getByRole('button', { name: 'Enable now' });
- userEvent.click(enable_now_button);
+ await userEvent.click(enable_now_button);
expect(mockStartMigration).toHaveBeenCalled();
});
diff --git a/packages/appstore/src/components/modals/wallets-upgrade-modal/wallets-upgrade-modal.tsx b/packages/appstore/src/components/modals/wallets-upgrade-modal/wallets-upgrade-modal.tsx
index 6bbe3e7bd3a5..8a3ae2ecfa57 100644
--- a/packages/appstore/src/components/modals/wallets-upgrade-modal/wallets-upgrade-modal.tsx
+++ b/packages/appstore/src/components/modals/wallets-upgrade-modal/wallets-upgrade-modal.tsx
@@ -1,15 +1,18 @@
import React from 'react';
-import { Analytics, TEvents } from '@deriv-com/analytics';
import classNames from 'classnames';
-import { Button, Text, Modal, VideoPlayer } from '@deriv/components';
-import { localize, Localize } from '@deriv/translations';
+
+import { Button, Modal, Text, VideoPlayer } from '@deriv/components';
+import { useGrowthbookGetFeatureValue, useWalletMigration } from '@deriv/hooks';
import { observer, useStore } from '@deriv/stores';
-import { useWalletMigration } from '@deriv/hooks';
+import { Localize, localize } from '@deriv/translations';
+import { Analytics, TEvents } from '@deriv-com/analytics';
import { SectionMessage } from '@deriv-com/ui';
+
import {
getWalletMigrationVideoTranslations,
WALLET_MIGRATION_VIDEO_TRANSLATIONS,
} from 'Constants/wallet-migration-video-translations';
+
import './wallets-upgrade-modal.scss';
const trackAnalyticsEvent = (
@@ -30,21 +33,27 @@ const WalletsUpgradeModal = observer(() => {
const { current_language } = common;
const { is_demo, is_real_wallets_upgrade_on, toggleWalletsUpgrade } = traders_hub;
const { is_desktop, is_mobile } = ui;
- const { is_eligible, startMigration } = useWalletMigration();
+ const { is_eligible, is_migrating, is_in_progress, startMigration } = useWalletMigration();
+ const [is_wallet_force_migration_enabled] = useGrowthbookGetFeatureValue({
+ featureFlag: 'wallet_force_migration',
+ defaultValue: false,
+ });
const account_mode = is_demo ? 'demo' : 'real';
const isWalletMigrationModalClosed = localStorage.getItem('is_wallet_migration_modal_closed');
const [modalOpen, setModalOpen] = React.useState(!isWalletMigrationModalClosed);
- const is_open = (is_eligible && modalOpen) || is_real_wallets_upgrade_on;
+ const is_modal_open_for_force_migration = is_wallet_force_migration_enabled && !(is_in_progress || is_migrating);
+ const is_modal_open_for_non_force_migration = (is_eligible && modalOpen) || is_real_wallets_upgrade_on;
+ const is_modal_open = is_modal_open_for_force_migration || is_modal_open_for_non_force_migration;
const video_src = getWalletMigrationVideoTranslations(
current_language as keyof typeof WALLET_MIGRATION_VIDEO_TRANSLATIONS
);
React.useEffect(() => {
- if (is_open) {
+ if (is_modal_open) {
trackAnalyticsEvent('open', account_mode);
}
- }, [account_mode, is_open]);
+ }, [account_mode, is_modal_open]);
const closeModal = () => {
setModalOpen(false);
@@ -66,7 +75,8 @@ const WalletsUpgradeModal = observer(() => {
return (
{
+ let residence_country = '';
+ if (this.residence) {
+ residence_country = this.residence;
+ } else {
+ try {
+ const { country_code } = (await WS.authorized.cache.getSettings())?.get_settings || {
+ country_code: '',
+ };
+ residence_country = country_code;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error('Error getting residence country', error);
+ }
+ }
+
const analytics_config = {
+ loggedIn: this.is_logged_in,
account_type: broker === 'null' ? 'unlogged' : broker,
- residence_country: this.residence,
+ residence_country,
app_id: String(getAppId()),
device_type: isMobile() ? 'mobile' : 'desktop',
language: getLanguage(),
diff --git a/packages/core/src/Utils/Analytics/index.ts b/packages/core/src/Utils/Analytics/index.ts
index ebe4b4dc5253..411e15a12d1a 100644
--- a/packages/core/src/Utils/Analytics/index.ts
+++ b/packages/core/src/Utils/Analytics/index.ts
@@ -27,13 +27,15 @@ export const AnalyticsInitializer = async () => {
}
: Cookies.getJSON('utm_data');
+ const client_information = Cookies.getJSON('client_information');
+
const config = {
growthbookKey: flags.marketing_growthbook ? process.env.GROWTHBOOK_CLIENT_KEY : undefined,
growthbookDecryptionKey: flags.marketing_growthbook ? process.env.GROWTHBOOK_DECRYPTION_KEY : undefined,
rudderstackKey: process.env.RUDDERSTACK_KEY,
growthbookOptions: {
attributes: {
- loggedIn: !!Cookies.get('clients_information'),
+ loggedIn: !!client_information,
account_type: account_type === 'null' ? 'unlogged' : account_type,
app_id: String(getAppId()),
device_type: window.innerWidth <= MAX_MOBILE_WIDTH ? 'mobile' : 'desktop',
@@ -49,6 +51,7 @@ export const AnalyticsInitializer = async () => {
network_type: navigator.connection?.effectiveType,
network_rtt: navigator.connection?.rtt,
network_downlink: navigator.connection?.downlink,
+ residence_country: client_information?.residence,
},
},
};