From ada5cb2866b537564f07ef28b15d240eacd7d0e5 Mon Sep 17 00:00:00 2001 From: mitra Date: Thu, 16 May 2024 13:49:42 +0800 Subject: [PATCH] Revert "feat: :fire: remove tour guide from tradershub" This reverts commit fed965304d58965d7fab09d7da9af4f8add7430d. --- .../src/constants/tour-steps-config.tsx | 121 ++++++++++++++++++ .../src/constants/tour-steps-styles.ts | 44 +++++++ .../src/modules/onboarding/onboarding.tsx | 22 +++- .../src/modules/tour-guide/tour-guide.tsx | 88 +++++++++++++ .../traders-hub/__tests__/index.spec.tsx | 1 + .../src/modules/traders-hub/index.tsx | 22 +++- .../Layout/header/default-mobile-links.tsx | 5 + .../Layout/header/traders-hub-header.tsx | 4 + .../Layout/header/traders-hub-onboarding.tsx | 18 ++- .../src/App/Containers/Modals/app-modals.jsx | 4 +- .../RealAccountSignup/real-account-signup.jsx | 7 +- packages/core/src/Stores/traders-hub-store.js | 7 + .../_common/layout/traders-hub-header.scss | 1 - packages/stores/src/mockStore.ts | 2 + packages/stores/types.ts | 2 + 15 files changed, 333 insertions(+), 15 deletions(-) create mode 100644 packages/appstore/src/constants/tour-steps-config.tsx create mode 100644 packages/appstore/src/constants/tour-steps-styles.ts create mode 100644 packages/appstore/src/modules/tour-guide/tour-guide.tsx diff --git a/packages/appstore/src/constants/tour-steps-config.tsx b/packages/appstore/src/constants/tour-steps-config.tsx new file mode 100644 index 000000000000..465df8a5112c --- /dev/null +++ b/packages/appstore/src/constants/tour-steps-config.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import { Step, Locale } from 'react-joyride'; +import { Text, SpanButton, Icon } from '@deriv/components'; +import { Localize, localize } from '@deriv/translations'; +import { isMobile } from '@deriv/shared'; +import 'Components/toggle-account-type/toggle-account-type.scss'; + +const stepProps = { + disableBeacon: true, + disableOverlayClose: true, +}; + +export const tour_step_config: Step[] = [ + { + title: ( + + + + +
+ + ), + content: ( + + + + ), + target: '.account-type-dropdown--parent', + ...stepProps, + }, + { + title: ( + + + + +
+ + ), + content: ( + + + + , + ]} + /> + + ), + target: isMobile() ? '.main-title-bar-mobile--regulator' : '.regulators-switcher__container', + ...stepProps, + }, + { + title: ( + + + + +
+ + ), + content: ( + + + + ), + target: '.traders-hub-header__tradershub--onboarding--logo', + ...stepProps, + }, +]; + +export const tour_step_config_high_risk: Step[] = [ + { + title: ( + + + + +
+ + ), + content: ( + + + + ), + target: '.account-type-dropdown--parent', + ...stepProps, + }, + { + title: ( + + + + +
+ + ), + content: ( + + + + ), + target: '.traders-hub-header__tradershub--onboarding--logo', + ...stepProps, + }, +]; + +export const getHighRiskTourStepLocale = (): Locale => ({ + back: , + last: , + next: , +}); + +export const getTourStepLocale = (): Locale => ({ + back: , + last: , + next: , +}); diff --git a/packages/appstore/src/constants/tour-steps-styles.ts b/packages/appstore/src/constants/tour-steps-styles.ts new file mode 100644 index 000000000000..6fbfbe1f121b --- /dev/null +++ b/packages/appstore/src/constants/tour-steps-styles.ts @@ -0,0 +1,44 @@ +import { Styles } from 'react-joyride'; + +type TJoyrideStyles = Pick; + +const tourStyles = { + options: { + width: 350, + }, + tooltipTitle: { + color: 'var(--brand-red-coral)', + textAlign: 'left', + }, + tooltipContent: { + textAlign: 'left', + fontSize: '1.6rem', + padding: '3rem 0 1.6rem 0', + wordBreak: 'break-word', + whiteSpace: 'pre-wrap', + }, + buttonNext: { + padding: '0.9rem', + fontSize: '1.5rem', + fontWeight: 'bold', + outline: 'none', + }, +} as const; + +export const tour_styles: TJoyrideStyles = { + options: tourStyles.options, + tooltipTitle: tourStyles.tooltipTitle, + tooltipContent: tourStyles.tooltipContent, + buttonNext: tourStyles.buttonNext, +}; + +export const tour_styles_dark_mode: TJoyrideStyles = { + options: { + ...tourStyles.options, + backgroundColor: 'var(--general-section-3)', + arrowColor: 'var(--general-section-3)', + }, + tooltipTitle: tourStyles.tooltipTitle, + tooltipContent: tourStyles.tooltipContent, + buttonNext: tourStyles.buttonNext, +}; diff --git a/packages/appstore/src/modules/onboarding/onboarding.tsx b/packages/appstore/src/modules/onboarding/onboarding.tsx index 97bdc80c1a3f..a12a87f9d1fc 100644 --- a/packages/appstore/src/modules/onboarding/onboarding.tsx +++ b/packages/appstore/src/modules/onboarding/onboarding.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import { useHistory } from 'react-router-dom'; -import { routes } from '@deriv/shared'; +import { routes, ContentFlag } from '@deriv/shared'; import { useStore, observer } from '@deriv/stores'; import OnboardingSkeleton from '../../components/loader'; import TradingPlatformIcon from 'Assets/svgs/trading-platform'; @@ -9,15 +9,29 @@ import './onboarding.scss'; const Onboarding = observer(() => { const history = useHistory(); - const { traders_hub, client } = useStore(); + const { traders_hub, client, ui } = useStore(); const { is_landing_company_loaded, is_logged_in, setPrevAccountType } = client; - const { is_demo_low_risk, selectAccountType } = traders_hub; + const { content_flag, is_demo_low_risk, selectAccountType, toggleIsTourOpen } = traders_hub; + const { is_from_signup_account } = ui; useEffect(() => { if (is_logged_in && is_landing_company_loaded) { history.push(routes.traders_hub); + if (is_from_signup_account && content_flag !== ContentFlag.EU_DEMO) { + toggleIsTourOpen(true); + } } - }, [is_logged_in, is_landing_company_loaded, is_demo_low_risk, history, selectAccountType, setPrevAccountType]); + }, [ + is_logged_in, + is_landing_company_loaded, + is_from_signup_account, + content_flag, + is_demo_low_risk, + history, + toggleIsTourOpen, + selectAccountType, + setPrevAccountType, + ]); if (is_logged_in && !is_landing_company_loaded) return ; diff --git a/packages/appstore/src/modules/tour-guide/tour-guide.tsx b/packages/appstore/src/modules/tour-guide/tour-guide.tsx new file mode 100644 index 000000000000..a2f951eb849c --- /dev/null +++ b/packages/appstore/src/modules/tour-guide/tour-guide.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import Joyride, { CallBackProps, STATUS, EVENTS, ACTIONS } from 'react-joyride'; +import { observer } from 'mobx-react-lite'; +import { + tour_step_config, + getTourStepLocale, + tour_step_config_high_risk, + getHighRiskTourStepLocale, +} from 'Constants/tour-steps-config'; +import { tour_styles, tour_styles_dark_mode } from 'Constants/tour-steps-styles'; +import { useStores } from 'Stores/index'; +import { ContentFlag } from '@deriv/shared'; +import { useTradersHubTracking } from 'Hooks/index'; +import { localize } from '@deriv/translations'; +import { SpanButton } from '@deriv/components'; + +const TourGuide = () => { + const { traders_hub, ui } = useStores(); + const { is_tour_open, toggleIsTourOpen, content_flag, setIsFirstTimeVisit } = traders_hub; + const { is_dark_mode_on, should_trigger_tour_guide, setShouldTriggerTourGuide } = ui; + + const [joyride_index, setJoyrideIndex] = React.useState(0); + const tour_step_locale = getTourStepLocale(); + const high_risk_tour_step_locale = getHighRiskTourStepLocale(); + const low_risk = content_flag === ContentFlag.LOW_RISK_CR_NON_EU || content_flag === ContentFlag.LOW_RISK_CR_EU; + + const { trackLastStep, trackStepForward, trackOnboardingRestart } = useTradersHubTracking(); + + const handleNextAction = (index: number) => { + trackStepForward(7); + setJoyrideIndex(index + 1); + }; + + const handlePrevAction = (index: number) => { + if (tour_step_config.length === joyride_index + 1) { + trackLastStep(); + trackOnboardingRestart(); + setIsFirstTimeVisit(false); + setJoyrideIndex(0); + } else { + setJoyrideIndex(index - 1); + } + }; + + const callbackHandle = (data: CallBackProps) => { + const { action, index, type, status } = data; + const finishedStatuses: string[] = [STATUS.FINISHED]; + const skipTypes: string[] = [EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND]; + + if (finishedStatuses.includes(status)) { + toggleIsTourOpen(false); + setShouldTriggerTourGuide(false); + setJoyrideIndex(0); + return; + } + + if (!skipTypes.includes(type)) { + return; + } + + if (action === ACTIONS.NEXT) { + handleNextAction(index); + } else if (action === ACTIONS.PREV) { + handlePrevAction(index); + } + }; + + if (tour_step_config.length === joyride_index + 1) { + tour_step_locale.back = ; + } + + return ( + + ); +}; +export default observer(TourGuide); diff --git a/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx b/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx index d5701b1439af..105a7c9e647f 100644 --- a/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx +++ b/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx @@ -7,6 +7,7 @@ jest.mock('Components/modals/modal-manager', () => jest.fn(() => 'mockedModalMan jest.mock('Components/main-title-bar', () => jest.fn(() => 'mockedMainTitleBar')); jest.mock('Components/cfds-listing', () => jest.fn(() => 'mockedCFDsListing')); jest.mock('Components/options-multipliers-listing', () => jest.fn(() => 'mocked')); +jest.mock('../../tour-guide/tour-guide', () => jest.fn(() => 'mocked')); describe('TradersHub', () => { const render_container = (mock_store_override = {}) => { diff --git a/packages/appstore/src/modules/traders-hub/index.tsx b/packages/appstore/src/modules/traders-hub/index.tsx index 776aebd84d6c..be461b1122a5 100644 --- a/packages/appstore/src/modules/traders-hub/index.tsx +++ b/packages/appstore/src/modules/traders-hub/index.tsx @@ -9,6 +9,7 @@ import MainTitleBar from 'Components/main-title-bar'; import OptionsAndMultipliersListing from 'Components/options-multipliers-listing'; import ButtonToggleLoader from 'Components/pre-loader/button-toggle-loader'; import classNames from 'classnames'; +import TourGuide from '../tour-guide/tour-guide'; import './traders-hub.scss'; import { useContentFlag, useGrowthbookFeatureFlag } from '@deriv/hooks'; @@ -33,13 +34,22 @@ const TradersHub = observer(() => { } = client; const { is_cr_demo, is_eu_demo, is_eu_real } = useContentFlag(); - const { selected_platform_type, setTogglePlatformType, is_eu_user } = traders_hub; + const { selected_platform_type, setTogglePlatformType, is_tour_open, is_eu_user } = traders_hub; const traders_hub_ref = React.useRef(null); const can_show_notify = (!is_switching && !is_logging_in && is_account_setting_loaded && is_landing_company_loaded) || checkServerMaintenance(website_status); + const [scrolled, setScrolled] = React.useState(false); + + const handleScroll = React.useCallback(() => { + const element = traders_hub_ref?.current; + if (element && is_tour_open) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }, [is_tour_open]); + const direct_to_real_account_creation = useGrowthbookFeatureFlag({ featureFlag: 'direct-real-account-creation-flow', defaultValue: false, @@ -73,7 +83,14 @@ const TradersHub = observer(() => { React.useEffect(() => { if (is_eu_user) setTogglePlatformType('cfd'); - }, [is_eu_user, setTogglePlatformType]); + const timer = setTimeout(() => { + handleScroll(); + setTimeout(() => { + setScrolled(true); + }, 200); + }, 100); + return () => clearTimeout(timer); + }, [handleScroll, is_eu_user, is_tour_open, setTogglePlatformType]); React.useLayoutEffect(() => { startPerformanceEventTimer('option_multiplier_section_loading_time'); @@ -164,6 +181,7 @@ const TradersHub = observer(() => { {getOrderedPlatformSections()} + {scrolled && }
{is_eu_user && ( diff --git a/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx b/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx index 9f299c157421..814a1f0a972e 100644 --- a/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx +++ b/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx @@ -5,7 +5,9 @@ import { routes } from '@deriv/shared'; import { Localize } from '@deriv/translations'; import { BinaryLink } from 'App/Components/Routes'; + import ShowNotifications from './show-notifications'; +import TradersHubOnboarding from './traders-hub-onboarding'; import { useFeatureFlags } from '@deriv/hooks'; type TDefaultMobileLinks = { @@ -16,6 +18,9 @@ const DefaultMobileLinks = React.memo(({ handleClickCashier }: TDefaultMobileLin const { is_next_wallet_enabled } = useFeatureFlags(); return ( +
+ +
diff --git a/packages/core/src/App/Containers/Layout/header/traders-hub-header.tsx b/packages/core/src/App/Containers/Layout/header/traders-hub-header.tsx index b2c4de85b1a5..c0247cb93e7f 100644 --- a/packages/core/src/App/Containers/Layout/header/traders-hub-header.tsx +++ b/packages/core/src/App/Containers/Layout/header/traders-hub-header.tsx @@ -13,6 +13,7 @@ import { BinaryLink } from 'App/Components/Routes'; import DerivBrandLogo from 'Assets/SvgComponents/header/deriv-rebranding-logo.svg'; import DefaultMobileLinks from './default-mobile-links'; import ShowNotifications from './show-notifications'; +import TradersHubOnboarding from './traders-hub-onboarding'; import TradersHubHomeButton from './traders-hub-home-button'; type TPlatformConfig = typeof platform_config; @@ -139,6 +140,9 @@ const TradersHubHeader = observer(() => {
+
+ +
diff --git a/packages/core/src/App/Containers/Layout/header/traders-hub-onboarding.tsx b/packages/core/src/App/Containers/Layout/header/traders-hub-onboarding.tsx index 63bea31ff66b..7fe5aadf5087 100644 --- a/packages/core/src/App/Containers/Layout/header/traders-hub-onboarding.tsx +++ b/packages/core/src/App/Containers/Layout/header/traders-hub-onboarding.tsx @@ -2,16 +2,26 @@ import React from 'react'; import { Icon, Popover } from '@deriv/components'; import { observer, useStore } from '@deriv/stores'; import { Localize } from '@deriv/translations'; +import { useFeatureFlags } from '@deriv/hooks'; import { useLocalStorage } from 'usehooks-ts'; const TradersHubOnboarding = observer(() => { - const { ui } = useStore(); + const { traders_hub, ui } = useStore(); + const { setIsFirstTimeVisit, toggleIsTourOpen, is_tour_open } = traders_hub; const { is_dark_mode_on, is_mobile } = ui; + const { is_next_wallet_enabled } = useFeatureFlags(); const [, setWalletsOnboarding] = useLocalStorage('walletsOnboarding', ''); - const onClickHandler = () => { - setWalletsOnboarding('started'); - }; + const onClickHandler = is_next_wallet_enabled + ? () => { + setWalletsOnboarding('started'); + } + : () => { + if (!is_tour_open) { + toggleIsTourOpen(true); + } + setIsFirstTimeVisit(false); + }; return (
diff --git a/packages/core/src/App/Containers/Modals/app-modals.jsx b/packages/core/src/App/Containers/Modals/app-modals.jsx index a3efea796f8f..b6c3f02d890f 100644 --- a/packages/core/src/App/Containers/Modals/app-modals.jsx +++ b/packages/core/src/App/Containers/Modals/app-modals.jsx @@ -95,7 +95,7 @@ const AppModals = observer(() => { mt5_login_list, should_show_effortless_login_modal, } = client; - const { content_flag } = traders_hub; + const { content_flag, is_tour_open } = traders_hub; const { is_from_derivgo } = common; const { is_account_needed_modal_on, @@ -218,7 +218,7 @@ const AppModals = observer(() => { ComponentToLoad = ; } - if (should_show_effortless_login_modal && !is_from_derivgo && !is_onboarding) { + if (should_show_effortless_login_modal && !is_tour_open && !is_from_derivgo && !is_onboarding) { ComponentToLoad = ; } diff --git a/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx b/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx index 5dca330f5605..7a0462ec7626 100644 --- a/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx +++ b/packages/core/src/App/Containers/RealAccountSignup/real-account-signup.jsx @@ -4,7 +4,7 @@ import { withRouter } from 'react-router-dom'; import classNames from 'classnames'; import { RiskToleranceWarningModal, TestWarningModal } from '@deriv/account'; import { Button, DesktopWrapper, MobileDialog, MobileWrapper, Modal, Text, UILoader } from '@deriv/components'; -import { WS, moduleLoader, routes } from '@deriv/shared'; +import { ContentFlag, WS, moduleLoader, routes } from '@deriv/shared'; import { Localize, localize } from '@deriv/translations'; import { observer, useStore } from '@deriv/stores'; import AddCurrency from './add-currency.jsx'; @@ -87,7 +87,7 @@ const RealAccountSignup = observer(({ history, state_index, is_trading_experienc real_account_signup: state_value, is_trading_assessment_for_new_user_enabled, } = ui; - const { show_eu_related_content } = traders_hub; + const { content_flag, show_eu_related_content, toggleIsTourOpen } = traders_hub; const deposit_target = modules.cashier.general_store.deposit_target; const setIsDeposit = modules.cashier.general_store.setIsDeposit; const should_show_all_available_currencies = modules.cashier.general_store.should_show_all_available_currencies; @@ -435,6 +435,9 @@ const RealAccountSignup = observer(({ history, state_index, is_trading_experienc if (modal_content[getActiveModalIndex()].action === 'signup') { setIsClosingCreateRealAccountModal(true); + if ([ContentFlag.EU_REAL, ContentFlag.EU_DEMO].includes(content_flag)) { + toggleIsTourOpen(true); + } return; } diff --git a/packages/core/src/Stores/traders-hub-store.js b/packages/core/src/Stores/traders-hub-store.js index 28da560f2b90..43efed6a53c1 100644 --- a/packages/core/src/Stores/traders-hub-store.js +++ b/packages/core/src/Stores/traders-hub-store.js @@ -19,6 +19,7 @@ export default class TradersHubStore extends BaseStore { is_failed_verification_modal_visible = false; is_regulators_compare_modal_visible = false; is_mt5_notification_modal_visible = false; + is_tour_open = false; account_type_card = ''; selected_platform_type = 'options'; mt5_existing_account = {}; @@ -49,6 +50,7 @@ export default class TradersHubStore extends BaseStore { is_regulators_compare_modal_visible: observable, is_mt5_notification_modal_visible: observable, is_failed_verification_modal_visible: observable, + is_tour_open: observable, modal_data: observable, is_onboarding_visited: observable, is_first_time_visit: observable, @@ -103,6 +105,7 @@ export default class TradersHubStore extends BaseStore { toggleFailedVerificationModalVisibility: action.bound, setMT5ExistingAccount: action.bound, openFailedVerificationModal: action.bound, + toggleIsTourOpen: action.bound, toggleRegulatorsCompareModal: action.bound, showTopUpModal: action.bound, toggleWalletsUpgrade: action.bound, @@ -252,6 +255,10 @@ export default class TradersHubStore extends BaseStore { this.selected_region = region; } + toggleIsTourOpen(is_tour_open) { + this.is_tour_open = is_tour_open; + } + get is_demo_low_risk() { const { is_landing_company_loaded } = this.root_store.client; if (is_landing_company_loaded) { diff --git a/packages/core/src/sass/app/_common/layout/traders-hub-header.scss b/packages/core/src/sass/app/_common/layout/traders-hub-header.scss index 14b97732e7d8..bfaf659b553b 100644 --- a/packages/core/src/sass/app/_common/layout/traders-hub-header.scss +++ b/packages/core/src/sass/app/_common/layout/traders-hub-header.scss @@ -166,7 +166,6 @@ } &__divider { - margin-right: 1rem; width: 0.1rem; height: 3rem; margin-left: 0.8rem; diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts index b8f821cebd66..d30c8020a83a 100644 --- a/packages/stores/src/mockStore.ts +++ b/packages/stores/src/mockStore.ts @@ -539,6 +539,8 @@ const mock = (): TStores & { is_mock: boolean } => { active_modal_wallet_id: '', setWalletModalActiveWalletID: jest.fn(), available_ctrader_accounts: [], + toggleIsTourOpen: jest.fn(), + is_tour_open: false, is_mt5_notification_modal_visible: false, setMT5NotificationModal: jest.fn(), has_any_real_account: false, diff --git a/packages/stores/types.ts b/packages/stores/types.ts index 6fab24de13dc..934d29ace552 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -1055,6 +1055,8 @@ type TTradersHubStore = { available_cfd_accounts: TAvailableCFDAccounts[]; available_dxtrade_accounts: TAvailableCFDAccounts[]; available_ctrader_accounts: TAvailableCFDAccounts[]; + toggleIsTourOpen: (is_tour_open: boolean) => void; + is_tour_open: boolean; is_demo_low_risk: boolean; is_mt5_notification_modal_visible: boolean; setMT5NotificationModal: (value: boolean) => void;