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, }, }, };