From 5e64eafbb6cf013649293d65d81d22a05e0f58e1 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 26 Nov 2024 02:39:24 +0530 Subject: [PATCH 01/25] add the migrated modal --- src/NAVIGATORS.ts | 1 + src/ROUTES.ts | 1 + src/SCREENS.ts | 4 ++ src/components/FeatureTrainingModal.tsx | 5 ++ src/components/MigratedUserWelcomeModal.tsx | 63 +++++++++++++++++++ src/hooks/useOnboardingFlow.ts | 18 +++++- .../Navigation/AppNavigator/AuthScreens.tsx | 6 ++ .../MigratedUserWelcomeModalNavigator.tsx | 28 +++++++++ src/libs/Navigation/linkingConfig/config.ts | 10 +++ .../linkingConfig/getAdaptedStateFromPath.ts | 7 ++- src/libs/Navigation/types.ts | 6 ++ src/types/onyx/TryNewDot.ts | 7 +++ 12 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/components/MigratedUserWelcomeModal.tsx create mode 100644 src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index 0b4a86c99247..b7c7a71c2828 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -11,5 +11,6 @@ export default { FEATURE_TRANING_MODAL_NAVIGATOR: 'FeatureTrainingModalNavigator', WELCOME_VIDEO_MODAL_NAVIGATOR: 'WelcomeVideoModalNavigator', EXPLANATION_MODAL_NAVIGATOR: 'ExplanationModalNavigator', + MIGRATED_USER_MODAL_NAVIGATOR: 'MigratedUserModalNavigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', } as const; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index d8f8b0f91105..944516f1bdfe 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1365,6 +1365,7 @@ const ROUTES = { }, WELCOME_VIDEO_ROOT: 'onboarding/welcome-video', EXPLANATION_MODAL_ROOT: 'onboarding/explanation', + MIGRATED_USER_WELCOME_MODAL: 'onboarding/migrated-user-welcome', TRANSACTION_RECEIPT: { route: 'r/:reportID/transaction/:transactionID/receipt', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5fd64b0fc0d0..dc21b2e42d66 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -583,6 +583,10 @@ const SCREENS = { ROOT: 'Explanation_Modal_Root', }, + MIGRATED_USER_WELCOME_MODAL: { + ROOT: 'MigratedUserWelcomeModal_Root', + }, + I_KNOW_A_TEACHER: 'I_Know_A_Teacher', INTRO_SCHOOL_PRINCIPAL: 'Intro_School_Principal', I_AM_A_TEACHER: 'I_Am_A_Teacher', diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index 804fd69ee742..a491fad21a8e 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -70,6 +70,9 @@ type FeatureTrainingModalProps = { /** Link to navigate to when user wants to learn more */ onHelp?: () => void; + + /** Children to render */ + children?: React.ReactNode; }; function FeatureTrainingModal({ @@ -85,6 +88,7 @@ function FeatureTrainingModal({ onClose = () => {}, helpText = '', onHelp = () => {}, + children, }: FeatureTrainingModalProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -209,6 +213,7 @@ function FeatureTrainingModal({ {title} {description} {secondaryDescription.length > 0 && {secondaryDescription}} + {children} )} {shouldShowDismissModalOption && ( diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx new file mode 100644 index 000000000000..49b425b9488b --- /dev/null +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import {View} from 'react-native'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; +import type {FeatureListItem} from './FeatureList'; +import FeatureTrainingModal from './FeatureTrainingModal'; +import * as Illustrations from './Icon/Illustrations'; +import MenuItem from './MenuItem'; + +const ExpensifyFeatures: FeatureListItem[] = [ + { + icon: Illustrations.MoneyReceipts, + translationKey: 'workspace.emptyWorkspace.features.trackAndCollect', + }, + { + icon: Illustrations.CreditCardsNew, + translationKey: 'workspace.emptyWorkspace.features.companyCards', + }, + { + icon: Illustrations.MoneyWings, + translationKey: 'workspace.emptyWorkspace.features.reimbursements', + }, +]; + +function OnboardingWelcomeVideo() { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + return ( + + + {ExpensifyFeatures.map(({translationKey, icon}) => ( + + + + ))} + + + ); +} + +OnboardingWelcomeVideo.displayName = 'OnboardingWelcomeVideo'; +export default OnboardingWelcomeVideo; diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 66ef088d0e4f..be93d1064ca2 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -21,6 +21,9 @@ function useOnboardingFlowRouter() { selector: hasCompletedHybridAppOnboardingFlowSelector, }); + const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); + const hasBeenAddedToNudgeMigration = !!tryNewDot?.nudgeMigration?.timestamp; + const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY); useEffect(() => { @@ -32,6 +35,11 @@ function useOnboardingFlowRouter() { return; } + if (hasBeenAddedToNudgeMigration) { + Navigation.navigate(ROUTES.MIGRATED_USER_WELCOME_MODAL); + return; + } + if (NativeModules.HybridAppModule) { // For single entries, such as using the Travel feature from OldDot, we don't want to show onboarding if (isSingleNewDotEntry) { @@ -54,7 +62,15 @@ function useOnboardingFlowRouter() { if (!NativeModules.HybridAppModule && isOnboardingCompleted === false) { OnboardingFlow.startOnboardingFlow(); } - }, [isOnboardingCompleted, isHybridAppOnboardingCompleted, isOnboardingCompletedMetadata, isHybridAppOnboardingCompletedMetadata, isSingleNewDotEntryMetadata, isSingleNewDotEntry]); + }, [ + isOnboardingCompleted, + isHybridAppOnboardingCompleted, + isOnboardingCompletedMetadata, + isHybridAppOnboardingCompletedMetadata, + isSingleNewDotEntryMetadata, + isSingleNewDotEntry, + hasBeenAddedToNudgeMigration, + ]); return {isOnboardingCompleted, isHybridAppOnboardingCompleted}; } diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 40b4742ca5de..96d0c08d9f12 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -66,6 +66,7 @@ import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator'; import FeatureTrainingModalNavigator from './Navigators/FeatureTrainingModalNavigator'; import FullScreenNavigator from './Navigators/FullScreenNavigator'; import LeftModalNavigator from './Navigators/LeftModalNavigator'; +import MigratedUserWelcomeModalNavigator from './Navigators/MigratedUserWelcomeModalNavigator'; import OnboardingModalNavigator from './Navigators/OnboardingModalNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; import WelcomeVideoModalNavigator from './Navigators/WelcomeVideoModalNavigator'; @@ -515,6 +516,11 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie options={onboardingModalScreenOptions} component={ExplanationModalNavigator} /> + (); + +function MigratedUserWelcomeModalNavigator() { + return ( + + + + + + + + ); +} + +MigratedUserWelcomeModalNavigator.displayName = 'MigratedUserWelcomeModalNavigator'; + +export default MigratedUserWelcomeModalNavigator; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 476711c7c116..fa1e40cd7c75 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -105,6 +105,16 @@ const config: LinkingOptions['config'] = { }, }, }, + + [NAVIGATORS.MIGRATED_USER_MODAL_NAVIGATOR]: { + screens: { + [SCREENS.MIGRATED_USER_WELCOME_MODAL.ROOT]: { + path: ROUTES.MIGRATED_USER_WELCOME_MODAL, + exact: true, + }, + }, + }, + [NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: { // Don't set the initialRouteName, because when the user continues from the last visited onboarding page, // the onboarding purpose page will be briefly visible. diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 518d27da8feb..b0fba017b367 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -180,6 +180,7 @@ function getAdaptedState(state: PartialState const lhpNavigator = state.routes.find((route) => route.name === NAVIGATORS.LEFT_MODAL_NAVIGATOR); const onboardingModalNavigator = state.routes.find((route) => route.name === NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR); const welcomeVideoModalNavigator = state.routes.find((route) => route.name === NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR); + const migratedUserModalNavigator = state.routes.find((route) => route.name === NAVIGATORS.MIGRATED_USER_MODAL_NAVIGATOR); const attachmentsScreen = state.routes.find((route) => route.name === SCREENS.ATTACHMENTS); const featureTrainingModalNavigator = state.routes.find((route) => route.name === NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR); @@ -224,7 +225,7 @@ function getAdaptedState(state: PartialState metainfo, }; } - if (lhpNavigator ?? onboardingModalNavigator ?? welcomeVideoModalNavigator ?? featureTrainingModalNavigator) { + if (lhpNavigator ?? onboardingModalNavigator ?? welcomeVideoModalNavigator ?? featureTrainingModalNavigator ?? migratedUserModalNavigator) { // Routes // - default bottom tab // - default central pane on desktop layout @@ -269,6 +270,10 @@ function getAdaptedState(state: PartialState routes.push(welcomeVideoModalNavigator); } + if (migratedUserModalNavigator) { + routes.push(migratedUserModalNavigator); + } + if (featureTrainingModalNavigator) { routes.push(featureTrainingModalNavigator); } diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5877c9c10218..3a954b436714 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1509,6 +1509,10 @@ type ExplanationModalNavigatorParamList = { [SCREENS.EXPLANATION_MODAL.ROOT]: undefined; }; +type MigratedUserModalNavigatorParamList = { + [SCREENS.MIGRATED_USER_WELCOME_MODAL.ROOT]: undefined; +}; + type BottomTabNavigatorParamList = { [SCREENS.HOME]: {policyID?: string}; [SCREENS.SEARCH.BOTTOM_TAB]: undefined; @@ -1582,6 +1586,7 @@ type AuthScreensParamList = CentralPaneScreensParamList & [NAVIGATORS.FEATURE_TRANING_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.WELCOME_VIDEO_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.MIGRATED_USER_MODAL_NAVIGATOR]: NavigatorScreenParams; [SCREENS.DESKTOP_SIGN_IN_REDIRECT]: undefined; [SCREENS.TRANSACTION_RECEIPT]: { reportID: string; @@ -1726,4 +1731,5 @@ export type { RestrictedActionParamList, MissingPersonalDetailsParamList, DebugParamList, + MigratedUserModalNavigatorParamList, }; diff --git a/src/types/onyx/TryNewDot.ts b/src/types/onyx/TryNewDot.ts index 67ea66d255d8..427a5486aeb5 100644 --- a/src/types/onyx/TryNewDot.ts +++ b/src/types/onyx/TryNewDot.ts @@ -20,6 +20,13 @@ type TryNewDot = { */ completedHybridAppOnboarding: boolean; }; + /** + * This key is added when user is migrated from OldDot to NewDot with nudge migration as part of a cohort. + */ + nudgeMigration: { + /** Indicates timestamp of an action. */ + timestamp: Date; + }; }; export default TryNewDot; From 78ecc3877c3f3210c0e7659d20a29ee273ea0c8e Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 27 Nov 2024 23:46:17 +0530 Subject: [PATCH 02/25] migrate-modal --- .../simple-illustration__flash.svg | 52 +++++++++++++++++ .../simple-illustration__mobileapp.svg | 54 ++++++++++++++++++ src/ONYXKEYS.ts | 4 ++ src/components/FeatureTrainingModal.tsx | 56 ++++++++++++++----- src/components/Icon/Illustrations.ts | 4 ++ src/components/MigratedUserWelcomeModal.tsx | 41 ++++++++++---- src/hooks/useOnboardingFlow.ts | 21 ++++--- src/languages/en.ts | 10 ++++ src/languages/es.ts | 10 ++++ src/libs/actions/Welcome/index.ts | 23 ++++++++ src/libs/onboardingSelectors.ts | 13 +++-- src/types/onyx/DismissedProductTraining.ts | 11 ++++ src/types/onyx/index.ts | 2 + 13 files changed, 261 insertions(+), 40 deletions(-) create mode 100644 assets/images/simple-illustrations/simple-illustration__flash.svg create mode 100644 assets/images/simple-illustrations/simple-illustration__mobileapp.svg create mode 100644 src/types/onyx/DismissedProductTraining.ts diff --git a/assets/images/simple-illustrations/simple-illustration__flash.svg b/assets/images/simple-illustrations/simple-illustration__flash.svg new file mode 100644 index 000000000000..be8daf296aa1 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__flash.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/simple-illustrations/simple-illustration__mobileapp.svg b/assets/images/simple-illustrations/simple-illustration__mobileapp.svg new file mode 100644 index 000000000000..80682c942f81 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__mobileapp.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index f97edbd744eb..c00c6870c418 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -359,6 +359,9 @@ const ONYXKEYS = { // Stores onboarding last visited path ONBOARDING_LAST_VISITED_PATH: 'onboardingLastVisitedPath', + // Whether the user has dismissed the product training element (Modal, Tooltip, etc.) + NVP_DISMISSED_PRODUCT_TRAINING: 'nvp_dismissedProductTraining', + // Max width supported for HTML element MAX_CANVAS_WIDTH: 'maxCanvasWidth', @@ -1023,6 +1026,7 @@ type OnyxValuesMapping = { [ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; [ONYXKEYS.CONCIERGE_REPORT_ID]: string; + [ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING]: OnyxTypes.DismissedProductTraining; }; type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping; diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index a491fad21a8e..004ec20e438b 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -1,6 +1,7 @@ import type {VideoReadyForDisplayEvent} from 'expo-av'; import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -39,8 +40,17 @@ type FeatureTrainingModalProps = { /** Animation to show when video is unavailable. Useful when app is offline */ animation?: DotLottieAnimation; + /** Style for the animation container */ + animationContainerStyle?: StyleProp; + + /** Whether to render the animation instead of the video */ + animationStyle?: StyleProp; + + /** Whether to render the animation instead of the video */ + shouldRenderAnimation?: boolean; + /** URL for the video */ - videoURL: string; + videoURL?: string; videoAspectRatio?: number; @@ -77,11 +87,14 @@ type FeatureTrainingModalProps = { function FeatureTrainingModal({ animation, + animationStyle, + animationContainerStyle, videoURL, videoAspectRatio: videoAspectRatioProp, title = '', description = '', secondaryDescription = '', + shouldRenderAnimation = false, shouldShowDismissModalOption = false, confirmText = '', onConfirm = () => {}, @@ -137,13 +150,13 @@ function FeatureTrainingModal({ // for the video until it loads. Also, when // videoStatus === 'animation' it will // set the same aspect ratio as the video would. - {aspectRatio}, + !shouldRenderAnimation && {aspectRatio}, ]} > - {videoStatus === 'video' ? ( + {!shouldRenderAnimation && videoStatus === 'video' ? ( ) : ( - - + + + + )} ); - }, [animation, videoURL, videoAspectRatio, videoStatus, shouldUseNarrowLayout, styles]); + }, [ + videoAspectRatio, + styles.w100, + styles.onboardingVideoPlayer, + styles.flex1, + styles.alignItemsCenter, + styles.justifyContentCenter, + styles.h100, + shouldRenderAnimation, + videoStatus, + videoURL, + animationContainerStyle, + animationStyle, + animation, + shouldUseNarrowLayout, + ]); const toggleWillShowAgain = useCallback(() => setWillShowAgain((prevWillShowAgain) => !prevWillShowAgain), []); diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 0068fd30ed60..4379142619ff 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -90,6 +90,7 @@ import EmailAddress from '@assets/images/simple-illustrations/simple-illustratio import EmptyState from '@assets/images/simple-illustrations/simple-illustration__empty-state.svg'; import EnvelopeReceipt from '@assets/images/simple-illustrations/simple-illustration__envelopereceipt.svg'; import Filters from '@assets/images/simple-illustrations/simple-illustration__filters.svg'; +import Flash from '@assets/images/simple-illustrations/simple-illustration__flash.svg'; import FolderOpen from '@assets/images/simple-illustrations/simple-illustration__folder-open.svg'; import Gears from '@assets/images/simple-illustrations/simple-illustration__gears.svg'; import HandCard from '@assets/images/simple-illustrations/simple-illustration__handcard.svg'; @@ -105,6 +106,7 @@ import LockOpen from '@assets/images/simple-illustrations/simple-illustration__l import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg'; import MagnifyingGlassMoney from '@assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg'; import Mailbox from '@assets/images/simple-illustrations/simple-illustration__mailbox.svg'; +import ExpensifyMobileApp from '@assets/images/simple-illustrations/simple-illustration__mobileapp.svg'; import MoneyReceipts from '@assets/images/simple-illustrations/simple-illustration__money-receipts.svg'; import MoneyBadge from '@assets/images/simple-illustrations/simple-illustration__moneybadge.svg'; import MoneyIntoWallet from '@assets/images/simple-illustrations/simple-illustration__moneyintowallet.svg'; @@ -290,4 +292,6 @@ export { StripeCompanyCardDetailLarge, VisaCompanyCardDetailLarge, WellsFargoCompanyCardDetailLarge, + Flash, + ExpensifyMobileApp, }; diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index 49b425b9488b..969fca4b14dc 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -1,41 +1,56 @@ import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as Welcome from '@libs/actions/Welcome'; import variables from '@styles/variables'; -import CONST from '@src/CONST'; import type {FeatureListItem} from './FeatureList'; import FeatureTrainingModal from './FeatureTrainingModal'; import * as Illustrations from './Icon/Illustrations'; +import LottieAnimations from './LottieAnimations'; import MenuItem from './MenuItem'; const ExpensifyFeatures: FeatureListItem[] = [ { - icon: Illustrations.MoneyReceipts, - translationKey: 'workspace.emptyWorkspace.features.trackAndCollect', + icon: Illustrations.ChatBubbles, + translationKey: 'migratedUserWelcomeModal.features.chat', }, { - icon: Illustrations.CreditCardsNew, - translationKey: 'workspace.emptyWorkspace.features.companyCards', + icon: Illustrations.Flash, + translationKey: 'migratedUserWelcomeModal.features.scanReceipt', }, { - icon: Illustrations.MoneyWings, - translationKey: 'workspace.emptyWorkspace.features.reimbursements', + icon: Illustrations.ExpensifyMobileApp, + translationKey: 'migratedUserWelcomeModal.features.crossPlatform', }, ]; function OnboardingWelcomeVideo() { const {translate} = useLocalize(); const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const theme = useTheme(); return ( { + Welcome.dismissProductTrainingElement('nudgeMigrationWelcomeModal'); + }} + shouldRenderAnimation + animationStyle={[styles.emptyWorkspaceIllustrationStyle]} + animationContainerStyle={[ + StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor ?? theme.appBG), + StyleUtils.getBorderRadiusStyle(variables.componentBorderRadiusLarge), + styles.pv4, + ]} > - + {ExpensifyFeatures.map(({translationKey, icon}) => ( diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index be93d1064ca2..607513e2543f 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -2,7 +2,7 @@ import {useEffect} from 'react'; import {NativeModules} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Navigation from '@libs/Navigation/Navigation'; -import {hasCompletedGuidedSetupFlowSelector, hasCompletedHybridAppOnboardingFlowSelector} from '@libs/onboardingSelectors'; +import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector} from '@libs/onboardingSelectors'; import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -17,25 +17,25 @@ function useOnboardingFlowRouter() { const [isOnboardingCompleted, isOnboardingCompletedMetadata] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { selector: hasCompletedGuidedSetupFlowSelector, }); - const [isHybridAppOnboardingCompleted, isHybridAppOnboardingCompletedMetadata] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT, { - selector: hasCompletedHybridAppOnboardingFlowSelector, + const [tryNewDot, tryNewDotdMetadata] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT, { + selector: tryNewDotOnyxSelector, }); + const {isHybridAppOnboardingCompleted, hasBeenAddedToNudgeMigration} = tryNewDot ?? {}; - const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT); - const hasBeenAddedToNudgeMigration = !!tryNewDot?.nudgeMigration?.timestamp; + const [dismissedProductTraining, dismissedProductTrainingMetadata] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING); const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY); useEffect(() => { - if (isLoadingOnyxValue(isOnboardingCompletedMetadata)) { + if (isLoadingOnyxValue(isOnboardingCompletedMetadata, tryNewDotdMetadata, dismissedProductTrainingMetadata)) { return; } - if (NativeModules.HybridAppModule && isLoadingOnyxValue(isHybridAppOnboardingCompletedMetadata, isSingleNewDotEntryMetadata)) { + if (NativeModules.HybridAppModule && isLoadingOnyxValue(isSingleNewDotEntryMetadata)) { return; } - if (hasBeenAddedToNudgeMigration) { + if (hasBeenAddedToNudgeMigration && !dismissedProductTraining?.nudgeMigrationWelcomeModal) { Navigation.navigate(ROUTES.MIGRATED_USER_WELCOME_MODAL); return; } @@ -66,10 +66,13 @@ function useOnboardingFlowRouter() { isOnboardingCompleted, isHybridAppOnboardingCompleted, isOnboardingCompletedMetadata, - isHybridAppOnboardingCompletedMetadata, + tryNewDotdMetadata, isSingleNewDotEntryMetadata, isSingleNewDotEntry, hasBeenAddedToNudgeMigration, + dismissedProductTrainingMetadata, + dismissedProductTraining?.nudgeMigrationWelcomeModal, + dismissedProductTraining, ]); return {isOnboardingCompleted, isHybridAppOnboardingCompleted}; diff --git a/src/languages/en.ts b/src/languages/en.ts index 0548fdef5c56..53a2fb708b59 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5393,6 +5393,16 @@ const translations = { takeATwoMinuteTour: 'Take a 2-minute tour', exploreExpensify: 'Explore everything Expensify has to offer', }, + migratedUserWelcomeModal: { + title: 'Travel and expense at the speed of chat!', + subtitle: 'New Expensify has the same great automation, but now with amazing collaboration:', + confirmText: "Let's go!", + features: { + chat: 'Chat directly on any expense, report, or workspace', + scanReceipt: 'Scan receipts and get paid back', + crossPlatform: 'Do everything from your phone or browser', + }, + }, }; export default translations satisfies TranslationDeepObject; diff --git a/src/languages/es.ts b/src/languages/es.ts index 165d30ea7115..ac1c50aca458 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5913,6 +5913,16 @@ const translations = { takeATwoMinuteTour: 'Haz un tour de 2 minutos', exploreExpensify: 'Explora todo lo que Expensify tiene para ofrecer', }, + migratedUserWelcomeModal: { + title: 'Travel and expense at the speed of chat!', + subtitle: 'New Expensify has the same great automation, but now with amazing collaboration:', + confirmText: "Let's go!", + features: { + chat: 'Chat directly on any expense, report, or workspace', + scanReceipt: 'Scan receipts and get paid back', + crossPlatform: 'Do everything from your phone or browser', + }, + }, }; export default translations satisfies TranslationDeepObject; diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 607370ae3b5e..54eece44d418 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -3,6 +3,7 @@ import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import DateUtils from '@libs/DateUtils'; import Log from '@libs/Log'; import type {OnboardingCompanySize} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -204,9 +205,31 @@ function setSelfTourViewed(shouldUpdateOnyxDataOnlyLocally = false) { API.write(WRITE_COMMANDS.SELF_TOUR_VIEWED, null, {optimisticData}); } +function dismissProductTrainingElement(elementName: string) { + // const optimisticData = [ + // { + // onyxMethod: Onyx.METHOD.MERGE, + // key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, + // value: { + // dismissedProductTrainingElements: { + // [elementName]: Date.now(), + // }, + // }, + // }, + // ]; + + const date = new Date(); + Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { + [elementName]: DateUtils.getDBTime(date.valueOf()), + }); + + // API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING_ELEMENT, {elementName}, {optimisticData}); +} + export { onServerDataReady, isOnboardingFlowCompleted, + dismissProductTrainingElement, setOnboardingCustomChoices, setOnboardingPurposeSelected, updateOnboardingLastVisitedPath, diff --git a/src/libs/onboardingSelectors.ts b/src/libs/onboardingSelectors.ts index 35b1f1b4208c..ef209a736010 100644 --- a/src/libs/onboardingSelectors.ts +++ b/src/libs/onboardingSelectors.ts @@ -29,15 +29,16 @@ function hasCompletedGuidedSetupFlowSelector(onboarding: OnyxValue): boolean | undefined { - let completedHybridAppOnboarding = tryNewDotData?.classicRedirect?.completedHybridAppOnboarding; +function tryNewDotOnyxSelector(tryNewDotData: OnyxValue): {isHybridAppOnboardingCompleted: boolean | undefined; hasBeenAddedToNudgeMigration: boolean} { + let isHybridAppOnboardingCompleted = tryNewDotData?.classicRedirect?.completedHybridAppOnboarding; + const hasBeenAddedToNudgeMigration = !!tryNewDotData?.nudgeMigration?.timestamp; // Backend might return strings instead of booleans - if (typeof completedHybridAppOnboarding === 'string') { - completedHybridAppOnboarding = completedHybridAppOnboarding === 'true'; + if (typeof isHybridAppOnboardingCompleted === 'string') { + isHybridAppOnboardingCompleted = isHybridAppOnboardingCompleted === 'true'; } - return completedHybridAppOnboarding; + return {isHybridAppOnboardingCompleted, hasBeenAddedToNudgeMigration}; } /** @@ -55,4 +56,4 @@ function hasSeenTourSelector(onboarding: OnyxValue Date: Fri, 29 Nov 2024 00:45:40 +0530 Subject: [PATCH 03/25] refactor: update animation handling and improve MenuItem rendering --- src/components/FeatureTrainingModal.tsx | 3 ++- src/components/MenuItem.tsx | 4 ++-- src/components/MigratedUserWelcomeModal.tsx | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index 004ec20e438b..864dfcf6ff4d 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -150,6 +150,7 @@ function FeatureTrainingModal({ // for the video until it loads. Also, when // videoStatus === 'animation' it will // set the same aspect ratio as the video would. + animationContainerStyle, !shouldRenderAnimation && {aspectRatio}, ]} > @@ -166,7 +167,7 @@ function FeatureTrainingModal({ /> ) : ( - + {!!title && (shouldRenderAsHTML || (shouldParseTitle && !!html.length)) && ( - + - + )} {!shouldRenderAsHTML && !shouldParseTitle && !!title && ( - + {ExpensifyFeatures.map(({translationKey, icon}) => ( ))} From c9a2d9aeb44f40670414c7ca1d8b94f9ed74b404 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 29 Nov 2024 00:48:46 +0530 Subject: [PATCH 04/25] feat: add contentContainerStyles prop to FeatureTrainingModal and update animation handling in MigratedUserWelcomeModal --- src/components/FeatureTrainingModal.tsx | 24 +++++++++++---------- src/components/MigratedUserWelcomeModal.tsx | 7 ++++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index 864dfcf6ff4d..9d50b3f4b4af 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -83,6 +83,9 @@ type FeatureTrainingModalProps = { /** Children to render */ children?: React.ReactNode; + + /** Styles for the content container */ + contentContainerStyles?: StyleProp; }; function FeatureTrainingModal({ @@ -102,6 +105,7 @@ function FeatureTrainingModal({ helpText = '', onHelp = () => {}, children, + contentContainerStyles, }: FeatureTrainingModalProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -167,16 +171,14 @@ function FeatureTrainingModal({ /> ) : ( - - - - + + )} @@ -240,7 +242,7 @@ function FeatureTrainingModal({ {renderIllustration()} {!!title && !!description && ( - + {title} {description} {secondaryDescription.length > 0 && {secondaryDescription}} diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index c7201d9e317f..c8879bc0a821 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -29,6 +30,7 @@ const ExpensifyFeatures: FeatureListItem[] = [ function OnboardingWelcomeVideo() { const {translate} = useLocalize(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const theme = useTheme(); @@ -43,13 +45,14 @@ function OnboardingWelcomeVideo() { Welcome.dismissProductTrainingElement('nudgeMigrationWelcomeModal'); }} shouldRenderAnimation - animationStyle={[styles.emptyWorkspaceIllustrationStyle, styles.cardSectionIllustration]} + animationStyle={[styles.emptyWorkspaceIllustrationStyle]} animationContainerStyle={[ StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor ?? theme.appBG), StyleUtils.getBorderRadiusStyle(variables.componentBorderRadiusLarge), styles.pv4, - styles.cardSectionIllustration, + shouldUseNarrowLayout && styles.cardSectionIllustration, ]} + contentContainerStyles={styles.mb4} > {ExpensifyFeatures.map(({translationKey, icon}) => ( From 349253c3a784d4312f1ec16e39c856221783a159 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 3 Dec 2024 23:54:00 +0530 Subject: [PATCH 05/25] Add modalInnerContainerStyle prop to FeatureTrainingModal and update styles --- src/components/FeatureTrainingModal.tsx | 11 ++++++++--- src/components/MigratedUserWelcomeModal.tsx | 10 ++++------ src/styles/index.ts | 5 +++++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index 9d50b3f4b4af..b5a3acee6985 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -86,6 +86,9 @@ type FeatureTrainingModalProps = { /** Styles for the content container */ contentContainerStyles?: StyleProp; + + /** Styles for the modal inner container */ + modalInnerContainerStyle?: ViewStyle; }; function FeatureTrainingModal({ @@ -106,6 +109,7 @@ function FeatureTrainingModal({ onHelp = () => {}, children, contentContainerStyles, + modalInnerContainerStyle, }: FeatureTrainingModalProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -154,7 +158,6 @@ function FeatureTrainingModal({ // for the video until it loads. Also, when // videoStatus === 'animation' it will // set the same aspect ratio as the video would. - animationContainerStyle, !shouldRenderAnimation && {aspectRatio}, ]} > @@ -194,7 +197,6 @@ function FeatureTrainingModal({ shouldRenderAnimation, videoStatus, videoURL, - animationContainerStyle, animationStyle, animation, shouldUseNarrowLayout, @@ -236,10 +238,13 @@ function FeatureTrainingModal({ width: 'auto', } : {}), + ...modalInnerContainerStyle, }} > - {renderIllustration()} + + {renderIllustration()} + {!!title && !!description && ( diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index c8879bc0a821..d00a6ec42178 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -3,7 +3,6 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Welcome from '@libs/actions/Welcome'; import variables from '@styles/variables'; @@ -33,7 +32,6 @@ function OnboardingWelcomeVideo() { const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const theme = useTheme(); return ( {ExpensifyFeatures.map(({translationKey, icon}) => ( diff --git a/src/styles/index.ts b/src/styles/index.ts index 3508cbacdcff..dd045176040a 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5340,6 +5340,11 @@ const styles = (theme: ThemeColors) => marginHorizontal: 8, alignSelf: 'center', }, + + migratedUserModalIllustration: { + width: 'auto', + height: variables.modalTopBigIconHeight, + }, } satisfies Styles); type ThemeStyles = ReturnType; From a1f2ea1f074672a3195e86b443abdd2dbf137497 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 4 Dec 2024 00:14:37 +0530 Subject: [PATCH 06/25] Add animationInnerContainerStyle and animationOuterContainerStyle props to FeatureTrainingModal --- src/components/FeatureTrainingModal.tsx | 14 ++++++++++---- src/components/MigratedUserWelcomeModal.tsx | 7 ++----- src/styles/index.ts | 5 ----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index b5a3acee6985..85219e51cd98 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -40,8 +40,11 @@ type FeatureTrainingModalProps = { /** Animation to show when video is unavailable. Useful when app is offline */ animation?: DotLottieAnimation; - /** Style for the animation container */ - animationContainerStyle?: StyleProp; + /** Style for the animation inner container */ + animationInnerContainerStyle?: StyleProp; + + /** Style for the animation outer container */ + animationOuterContainerStyle?: StyleProp; /** Whether to render the animation instead of the video */ animationStyle?: StyleProp; @@ -94,7 +97,8 @@ type FeatureTrainingModalProps = { function FeatureTrainingModal({ animation, animationStyle, - animationContainerStyle, + animationInnerContainerStyle, + animationOuterContainerStyle, videoURL, videoAspectRatio: videoAspectRatioProp, title = '', @@ -158,6 +162,7 @@ function FeatureTrainingModal({ // for the video until it loads. Also, when // videoStatus === 'animation' it will // set the same aspect ratio as the video would. + animationInnerContainerStyle, !shouldRenderAnimation && {aspectRatio}, ]} > @@ -200,6 +205,7 @@ function FeatureTrainingModal({ animationStyle, animation, shouldUseNarrowLayout, + animationInnerContainerStyle, ]); const toggleWillShowAgain = useCallback(() => setWillShowAgain((prevWillShowAgain) => !prevWillShowAgain), []); @@ -242,7 +248,7 @@ function FeatureTrainingModal({ }} > - + {renderIllustration()} diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index d00a6ec42178..4842ab99153e 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -44,11 +44,8 @@ function OnboardingWelcomeVideo() { }} shouldRenderAnimation animationStyle={[styles.emptyWorkspaceIllustrationStyle]} - animationContainerStyle={[ - StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), - shouldUseNarrowLayout && styles.migratedUserModalIllustration, - styles.ph0, - ]} + animationInnerContainerStyle={[StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), shouldUseNarrowLayout && styles.cardSectionIllustration]} + animationOuterContainerStyle={styles.p0} contentContainerStyles={styles.mb4} modalInnerContainerStyle={styles.pt0} > diff --git a/src/styles/index.ts b/src/styles/index.ts index dd045176040a..3508cbacdcff 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5340,11 +5340,6 @@ const styles = (theme: ThemeColors) => marginHorizontal: 8, alignSelf: 'center', }, - - migratedUserModalIllustration: { - width: 'auto', - height: variables.modalTopBigIconHeight, - }, } satisfies Styles); type ThemeStyles = ReturnType; From 4e937b6e3d759f477c9bb93e31a639d061c360ec Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 4 Dec 2024 00:25:10 +0530 Subject: [PATCH 07/25] resolve conflict --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 4266e9362313..2bfa02bb5ca4 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -525,7 +525,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie /> Date: Wed, 4 Dec 2024 00:42:03 +0530 Subject: [PATCH 08/25] resolve conflict after merge --- .../Navigators/MigratedUserWelcomeModalNavigator.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx index 6b76526ba2fa..94e396283e2b 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx @@ -1,18 +1,19 @@ -import {createStackNavigator} from '@react-navigation/stack'; import React from 'react'; import {View} from 'react-native'; import NoDropZone from '@components/DragAndDrop/NoDropZone'; import MigratedUserWelcomeModal from '@components/MigratedUserWelcomeModal'; +import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; +import Animations from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation'; import type {MigratedUserModalNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; -const Stack = createStackNavigator(); +const Stack = createPlatformStackNavigator(); function MigratedUserWelcomeModalNavigator() { return ( - + Date: Wed, 4 Dec 2024 00:43:52 +0530 Subject: [PATCH 09/25] resolve conflict after merge --- .../Navigators/MigratedUserWelcomeModalNavigator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx index 94e396283e2b..1320b876d576 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx @@ -13,7 +13,7 @@ function MigratedUserWelcomeModalNavigator() { return ( - + Date: Wed, 4 Dec 2024 02:01:43 +0530 Subject: [PATCH 10/25] fix animations --- src/components/FeatureTrainingModal.tsx | 14 ++++++++++---- .../MigratedUserWelcomeModalNavigator.tsx | 1 - 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index 85219e51cd98..df39abe8f50a 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -1,6 +1,6 @@ import type {VideoReadyForDisplayEvent} from 'expo-av'; import React, {useCallback, useEffect, useState} from 'react'; -import {View} from 'react-native'; +import {InteractionManager, View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import useLocalize from '@hooks/useLocalize'; @@ -118,7 +118,7 @@ function FeatureTrainingModal({ const styles = useThemeStyles(); const {translate} = useLocalize(); const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout(); - const [isModalVisible, setIsModalVisible] = useState(true); + const [isModalVisible, setIsModalVisible] = useState(false); const [willShowAgain, setWillShowAgain] = useState(true); const [videoStatus, setVideoStatus] = useState('video'); const [isVideoStatusLocked, setIsVideoStatusLocked] = useState(false); @@ -126,6 +126,10 @@ function FeatureTrainingModal({ const {shouldUseNarrowLayout} = useResponsiveLayout(); const {isOffline} = useNetwork(); + useEffect(() => { + InteractionManager.runAfterInteractions(() => setIsModalVisible(true)); + }, []); + useEffect(() => { if (isVideoStatusLocked) { return; @@ -215,8 +219,10 @@ function FeatureTrainingModal({ User.dismissTrackTrainingModal(); } setIsModalVisible(false); - Navigation.goBack(); - onClose?.(); + InteractionManager.runAfterInteractions(() => { + Navigation.goBack(); + onClose?.(); + }); }, [onClose, willShowAgain]); const closeAndConfirmModal = useCallback(() => { diff --git a/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx index 1320b876d576..12cf4583b3dc 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/MigratedUserWelcomeModalNavigator.tsx @@ -3,7 +3,6 @@ import {View} from 'react-native'; import NoDropZone from '@components/DragAndDrop/NoDropZone'; import MigratedUserWelcomeModal from '@components/MigratedUserWelcomeModal'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; -import Animations from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation'; import type {MigratedUserModalNavigatorParamList} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; From 839f0b8a7e8d3e6495f5f7e00a0d54cff6d9cb81 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 4 Dec 2024 21:58:06 +0530 Subject: [PATCH 11/25] refactor MigratedUserWelcomeModal: remove unused responsive layout hook and adjust styles --- src/components/MigratedUserWelcomeModal.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index 4842ab99153e..51d7c75e1546 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Welcome from '@libs/actions/Welcome'; @@ -29,7 +28,6 @@ const ExpensifyFeatures: FeatureListItem[] = [ function OnboardingWelcomeVideo() { const {translate} = useLocalize(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -44,12 +42,12 @@ function OnboardingWelcomeVideo() { }} shouldRenderAnimation animationStyle={[styles.emptyWorkspaceIllustrationStyle]} - animationInnerContainerStyle={[StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), shouldUseNarrowLayout && styles.cardSectionIllustration]} + animationInnerContainerStyle={[StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), styles.cardSectionIllustration]} animationOuterContainerStyle={styles.p0} - contentContainerStyles={styles.mb4} + contentContainerStyles={[styles.mb5, styles.gap2]} modalInnerContainerStyle={styles.pt0} > - + {ExpensifyFeatures.map(({translationKey, icon}) => ( Date: Thu, 5 Dec 2024 00:44:46 +0530 Subject: [PATCH 12/25] refactor FeatureTrainingModal: remove shouldRenderAnimation prop and update videoURL handling --- src/components/FeatureTrainingModal.tsx | 21 ++++++++------------- src/components/MigratedUserWelcomeModal.tsx | 3 ++- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index df39abe8f50a..06e979881614 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -40,20 +40,17 @@ type FeatureTrainingModalProps = { /** Animation to show when video is unavailable. Useful when app is offline */ animation?: DotLottieAnimation; - /** Style for the animation inner container */ + /** Style for the inner container of the animation */ animationInnerContainerStyle?: StyleProp; - /** Style for the animation outer container */ + /** Style for the outer container of the animation */ animationOuterContainerStyle?: StyleProp; - /** Whether to render the animation instead of the video */ + /** Additional styles for the animation */ animationStyle?: StyleProp; - /** Whether to render the animation instead of the video */ - shouldRenderAnimation?: boolean; - /** URL for the video */ - videoURL?: string; + videoURL: string; videoAspectRatio?: number; @@ -104,7 +101,6 @@ function FeatureTrainingModal({ title = '', description = '', secondaryDescription = '', - shouldRenderAnimation = false, shouldShowDismissModalOption = false, confirmText = '', onConfirm = () => {}, @@ -167,13 +163,13 @@ function FeatureTrainingModal({ // videoStatus === 'animation' it will // set the same aspect ratio as the video would. animationInnerContainerStyle, - !shouldRenderAnimation && {aspectRatio}, + !!videoURL && {aspectRatio}, ]} > - {!shouldRenderAnimation && videoStatus === 'video' ? ( + {!!videoURL && videoStatus === 'video' ? ( ) : ( - + { Welcome.dismissProductTrainingElement('nudgeMigrationWelcomeModal'); }} - shouldRenderAnimation animationStyle={[styles.emptyWorkspaceIllustrationStyle]} animationInnerContainerStyle={[StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), styles.cardSectionIllustration]} animationOuterContainerStyle={styles.p0} From 4860fca9a994217325248aaf91015252d30fd763 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 6 Dec 2024 01:17:15 +0530 Subject: [PATCH 13/25] put this behind beta --- src/CONST.ts | 1 + src/hooks/useOnboardingFlow.ts | 9 ++++++--- src/libs/Permissions.ts | 6 ++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ea9955600d9a..279647925b88 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -680,6 +680,7 @@ const CONST = { COMBINED_TRACK_SUBMIT: 'combinedTrackSubmit', CATEGORY_AND_TAG_APPROVERS: 'categoryAndTagApprovers', PER_DIEM: 'newDotPerDiem', + PRODCUCT_TRAINING: 'productTraining', }, BUTTON_STATES: { DEFAULT: 'default', diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 607513e2543f..533eb25e73d9 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -3,6 +3,7 @@ import {NativeModules} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Navigation from '@libs/Navigation/Navigation'; import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector} from '@libs/onboardingSelectors'; +import Permissions from '@libs/Permissions'; import * as OnboardingFlow from '@userActions/Welcome/OnboardingFlow'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -25,9 +26,9 @@ function useOnboardingFlowRouter() { const [dismissedProductTraining, dismissedProductTrainingMetadata] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING); const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY); - + const [allBetas, allBetasMetadata] = useOnyx(ONYXKEYS.BETAS); useEffect(() => { - if (isLoadingOnyxValue(isOnboardingCompletedMetadata, tryNewDotdMetadata, dismissedProductTrainingMetadata)) { + if (isLoadingOnyxValue(isOnboardingCompletedMetadata, tryNewDotdMetadata, dismissedProductTrainingMetadata, allBetasMetadata)) { return; } @@ -35,7 +36,7 @@ function useOnboardingFlowRouter() { return; } - if (hasBeenAddedToNudgeMigration && !dismissedProductTraining?.nudgeMigrationWelcomeModal) { + if (hasBeenAddedToNudgeMigration && !dismissedProductTraining?.nudgeMigrationWelcomeModal && Permissions.shouldShowProductTrainingElements(allBetas)) { Navigation.navigate(ROUTES.MIGRATED_USER_WELCOME_MODAL); return; } @@ -73,6 +74,8 @@ function useOnboardingFlowRouter() { dismissedProductTrainingMetadata, dismissedProductTraining?.nudgeMigrationWelcomeModal, dismissedProductTraining, + allBetasMetadata, + allBetas, ]); return {isOnboardingCompleted, isHybridAppOnboardingCompleted}; diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 1d3428dfcfce..0a9ebc54e419 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -39,6 +39,11 @@ function canUsePerDiem(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.PER_DIEM) || canUseAllBetas(betas); } +// TEMPORARY BETA TO HIDE PRODUCT TRAINING TOOLTIP AND MIGRATE USER WELCOME MODAL +function shouldShowProductTrainingElements(betas: OnyxEntry): boolean { + return !!betas?.includes(CONST.BETAS.PRODCUCT_TRAINING) || canUseAllBetas(betas); +} + /** * Link previews are temporarily disabled. */ @@ -55,4 +60,5 @@ export default { canUseCombinedTrackSubmit, canUseCategoryAndTagApprovers, canUsePerDiem, + shouldShowProductTrainingElements, }; From 13dba9a0f655fd137a6e4ac9e988298fc00eb55b Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 6 Dec 2024 01:20:56 +0530 Subject: [PATCH 14/25] refactor dismissProductTrainingElement --- src/libs/actions/Welcome/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 54eece44d418..787c2ef1e6ad 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -206,24 +206,23 @@ function setSelfTourViewed(shouldUpdateOnyxDataOnlyLocally = false) { } function dismissProductTrainingElement(elementName: string) { + const date = new Date(); // const optimisticData = [ // { // onyxMethod: Onyx.METHOD.MERGE, // key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, // value: { // dismissedProductTrainingElements: { - // [elementName]: Date.now(), + // [elementName]: DateUtils.getDBTime(date.valueOf()), // }, // }, // }, // ]; + // API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING_ELEMENT, {name: elementName}, {optimisticData}); - const date = new Date(); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { [elementName]: DateUtils.getDBTime(date.valueOf()), }); - - // API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING_ELEMENT, {elementName}, {optimisticData}); } export { From 835e8d1b9c61abe2806c890b7ea92c52de08eca7 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 6 Dec 2024 01:23:17 +0530 Subject: [PATCH 15/25] fix typo --- src/CONST.ts | 2 +- src/libs/Permissions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 279647925b88..ec159cb368bc 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -680,7 +680,7 @@ const CONST = { COMBINED_TRACK_SUBMIT: 'combinedTrackSubmit', CATEGORY_AND_TAG_APPROVERS: 'categoryAndTagApprovers', PER_DIEM: 'newDotPerDiem', - PRODCUCT_TRAINING: 'productTraining', + PRODUCT_TRAINING: 'productTraining', }, BUTTON_STATES: { DEFAULT: 'default', diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 0a9ebc54e419..9a0e69c1baf6 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -41,7 +41,7 @@ function canUsePerDiem(betas: OnyxEntry): boolean { // TEMPORARY BETA TO HIDE PRODUCT TRAINING TOOLTIP AND MIGRATE USER WELCOME MODAL function shouldShowProductTrainingElements(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.PRODCUCT_TRAINING) || canUseAllBetas(betas); + return !!betas?.includes(CONST.BETAS.PRODUCT_TRAINING) || canUseAllBetas(betas); } /** From a1eaba1de87e4228dba072e4b122e7f82a76dd8f Mon Sep 17 00:00:00 2001 From: Ishpaul Singh <104348397+ishpaul777@users.noreply.github.com> Date: Fri, 6 Dec 2024 03:26:04 +0530 Subject: [PATCH 16/25] Update src/ONYXKEYS.ts Co-authored-by: Puneet Lath --- src/ONYXKEYS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index c00c6870c418..754b39cfcea4 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -359,7 +359,7 @@ const ONYXKEYS = { // Stores onboarding last visited path ONBOARDING_LAST_VISITED_PATH: 'onboardingLastVisitedPath', - // Whether the user has dismissed the product training element (Modal, Tooltip, etc.) + // Object containing names/timestamps of dismissed product training elements (Modal, Tooltip, etc.) NVP_DISMISSED_PRODUCT_TRAINING: 'nvp_dismissedProductTraining', // Max width supported for HTML element From 63e04a78aac527a7a2d00629ae7ccdc5bb5c450e Mon Sep 17 00:00:00 2001 From: Ishpaul Singh <104348397+ishpaul777@users.noreply.github.com> Date: Fri, 6 Dec 2024 03:28:10 +0530 Subject: [PATCH 17/25] Update src/libs/actions/Welcome/index.ts Co-authored-by: Puneet Lath --- src/libs/actions/Welcome/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 787c2ef1e6ad..55ed7cbe16db 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -212,9 +212,7 @@ function dismissProductTrainingElement(elementName: string) { // onyxMethod: Onyx.METHOD.MERGE, // key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, // value: { - // dismissedProductTrainingElements: { // [elementName]: DateUtils.getDBTime(date.valueOf()), - // }, // }, // }, // ]; From d5a67d5f5d7031742ffaa6707c524f2aa1b84ead Mon Sep 17 00:00:00 2001 From: Ishpaul Singh <104348397+ishpaul777@users.noreply.github.com> Date: Fri, 6 Dec 2024 03:29:16 +0530 Subject: [PATCH 18/25] Update src/libs/actions/Welcome/index.ts Co-authored-by: Puneet Lath --- src/libs/actions/Welcome/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 55ed7cbe16db..a059158ba0eb 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -216,7 +216,7 @@ function dismissProductTrainingElement(elementName: string) { // }, // }, // ]; - // API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING_ELEMENT, {name: elementName}, {optimisticData}); + // API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: elementName}, {optimisticData}); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { [elementName]: DateUtils.getDBTime(date.valueOf()), From 5db4193d968b817dbba423758687d1c606a7f402 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 6 Dec 2024 03:32:15 +0530 Subject: [PATCH 19/25] Rename nudgeMigrationWelcomeModal to migratedUserWelcomeModal --- src/components/MigratedUserWelcomeModal.tsx | 2 +- src/hooks/useOnboardingFlow.ts | 4 ++-- src/libs/actions/Welcome/index.ts | 4 ++-- src/types/onyx/DismissedProductTraining.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index 84c8215b9820..366f1bd0d239 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -40,7 +40,7 @@ function OnboardingWelcomeVideo() { confirmText={translate('migratedUserWelcomeModal.confirmText')} animation={LottieAnimations.WorkspacePlanet} onClose={() => { - Welcome.dismissProductTrainingElement('nudgeMigrationWelcomeModal'); + Welcome.dismissProductTraining('migratedUserWelcomeModal'); }} animationStyle={[styles.emptyWorkspaceIllustrationStyle]} animationInnerContainerStyle={[StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), styles.cardSectionIllustration]} diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 533eb25e73d9..3a821dc44caf 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -36,7 +36,7 @@ function useOnboardingFlowRouter() { return; } - if (hasBeenAddedToNudgeMigration && !dismissedProductTraining?.nudgeMigrationWelcomeModal && Permissions.shouldShowProductTrainingElements(allBetas)) { + if (hasBeenAddedToNudgeMigration && !dismissedProductTraining?.migratedUserWelcomeModal && Permissions.shouldShowProductTrainingElements(allBetas)) { Navigation.navigate(ROUTES.MIGRATED_USER_WELCOME_MODAL); return; } @@ -72,7 +72,7 @@ function useOnboardingFlowRouter() { isSingleNewDotEntry, hasBeenAddedToNudgeMigration, dismissedProductTrainingMetadata, - dismissedProductTraining?.nudgeMigrationWelcomeModal, + dismissedProductTraining?.migratedUserWelcomeModal, dismissedProductTraining, allBetasMetadata, allBetas, diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index a059158ba0eb..b306daf444ba 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -205,7 +205,7 @@ function setSelfTourViewed(shouldUpdateOnyxDataOnlyLocally = false) { API.write(WRITE_COMMANDS.SELF_TOUR_VIEWED, null, {optimisticData}); } -function dismissProductTrainingElement(elementName: string) { +function dismissProductTraining(elementName: string) { const date = new Date(); // const optimisticData = [ // { @@ -226,7 +226,7 @@ function dismissProductTrainingElement(elementName: string) { export { onServerDataReady, isOnboardingFlowCompleted, - dismissProductTrainingElement, + dismissProductTraining, setOnboardingCustomChoices, setOnboardingPurposeSelected, updateOnboardingLastVisitedPath, diff --git a/src/types/onyx/DismissedProductTraining.ts b/src/types/onyx/DismissedProductTraining.ts index 644ba9db8f9b..9539bc9f0187 100644 --- a/src/types/onyx/DismissedProductTraining.ts +++ b/src/types/onyx/DismissedProductTraining.ts @@ -5,7 +5,7 @@ type DismissedProductTraining = { /** * When user dismisses the nudgeMigration Welcome Modal, we store the timestamp here. */ - nudgeMigrationWelcomeModal: Date; + migratedUserWelcomeModal: Date; }; export default DismissedProductTraining; From 69e555933761d52546a2384dc75efbe1c4a6d062 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 6 Dec 2024 03:38:23 +0530 Subject: [PATCH 20/25] Add CONST migratedUserWelcomeModal --- src/CONST.ts | 2 ++ src/components/MigratedUserWelcomeModal.tsx | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index ec159cb368bc..77ba49754269 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6373,6 +6373,8 @@ const CONST = { HYBRID_APP: { REORDERING_REACT_NATIVE_ACTIVITY_TO_FRONT: 'reorderingReactNativeActivityToFront', }, + + MIGRTED_USER_WELCOME_MODAL: 'migratedUserWelcomeModal', } as const; type Country = keyof typeof CONST.ALL_COUNTRIES; diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index 366f1bd0d239..db7c650c61b2 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -5,6 +5,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Welcome from '@libs/actions/Welcome'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import type {FeatureListItem} from './FeatureList'; import FeatureTrainingModal from './FeatureTrainingModal'; import * as Illustrations from './Icon/Illustrations'; @@ -40,7 +41,7 @@ function OnboardingWelcomeVideo() { confirmText={translate('migratedUserWelcomeModal.confirmText')} animation={LottieAnimations.WorkspacePlanet} onClose={() => { - Welcome.dismissProductTraining('migratedUserWelcomeModal'); + Welcome.dismissProductTraining(CONST.MIGRTED_USER_WELCOME_MODAL); }} animationStyle={[styles.emptyWorkspaceIllustrationStyle]} animationInnerContainerStyle={[StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), styles.cardSectionIllustration]} From 041deab823f92c4f195ec424b5127b8b0c743134 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 6 Dec 2024 03:50:33 +0530 Subject: [PATCH 21/25] Add DismissProductTrainingParams type and update API parameters --- src/libs/API/parameters/DismissProductTraining.ts | 5 +++++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 4 ++++ 3 files changed, 10 insertions(+) create mode 100644 src/libs/API/parameters/DismissProductTraining.ts diff --git a/src/libs/API/parameters/DismissProductTraining.ts b/src/libs/API/parameters/DismissProductTraining.ts new file mode 100644 index 000000000000..6a82ad995294 --- /dev/null +++ b/src/libs/API/parameters/DismissProductTraining.ts @@ -0,0 +1,5 @@ +type DismissProductTrainingParams = { + name: string; +}; + +export default DismissProductTrainingParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 6a510d074f98..837fc9189e56 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -352,3 +352,4 @@ export type {default as OpenPolicyPerDiemRatesPageParams} from './OpenPolicyPerD export type {default as TogglePlatformMuteParams} from './TogglePlatformMuteParams'; export type {default as ImportPerDiemRatesParams} from './ImportPerDiemRatesParams'; export type {default as ExportPerDiemCSVParams} from './ExportPerDiemCSVParams'; +export type {default as DismissProductTrainingParams} from './DismissProductTraining'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index d31da53304f6..18cb83a56525 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -439,6 +439,7 @@ const WRITE_COMMANDS = { SELF_TOUR_VIEWED: 'SelfTourViewed', UPDATE_INVOICE_COMPANY_NAME: 'UpdateInvoiceCompanyName', UPDATE_INVOICE_COMPANY_WEBSITE: 'UpdateInvoiceCompanyWebsite', + DISMISS_PRODUCT_TRAINING: 'DismissProductTraining', } as const; type WriteCommand = ValueOf; @@ -889,6 +890,9 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT]: Parameters.SetInvoicingTransferBankAccountParams; [WRITE_COMMANDS.UPDATE_INVOICE_COMPANY_NAME]: Parameters.UpdateInvoiceCompanyNameParams; [WRITE_COMMANDS.UPDATE_INVOICE_COMPANY_WEBSITE]: Parameters.UpdateInvoiceCompanyWebsiteParams; + + // Dismis Product Training + [WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING]: Parameters.DismissProductTrainingParams; }; const READ_COMMANDS = { From 0b3ef8d7bedb8b366dc18e2c1d75d8c64a6dd41f Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 6 Dec 2024 18:30:15 +0530 Subject: [PATCH 22/25] add padding to modal container --- src/components/FeatureTrainingModal.tsx | 12 ++++++++---- src/components/MigratedUserWelcomeModal.tsx | 7 +++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/FeatureTrainingModal.tsx b/src/components/FeatureTrainingModal.tsx index 06e979881614..1ae76f72ccef 100644 --- a/src/components/FeatureTrainingModal.tsx +++ b/src/components/FeatureTrainingModal.tsx @@ -85,7 +85,10 @@ type FeatureTrainingModalProps = { children?: React.ReactNode; /** Styles for the content container */ - contentContainerStyles?: StyleProp; + contentInnerContainerStyles?: StyleProp; + + /** Styles for the content outer container */ + contentOuterContainerStyles?: StyleProp; /** Styles for the modal inner container */ modalInnerContainerStyle?: ViewStyle; @@ -108,7 +111,8 @@ function FeatureTrainingModal({ helpText = '', onHelp = () => {}, children, - contentContainerStyles, + contentInnerContainerStyles, + contentOuterContainerStyles, modalInnerContainerStyle, }: FeatureTrainingModalProps) { const styles = useThemeStyles(); @@ -252,9 +256,9 @@ function FeatureTrainingModal({ {renderIllustration()} - + {!!title && !!description && ( - + {title} {description} {secondaryDescription.length > 0 && {secondaryDescription}} diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index db7c650c61b2..57156de3b47f 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Welcome from '@libs/actions/Welcome'; @@ -31,6 +32,7 @@ function OnboardingWelcomeVideo() { const {translate} = useLocalize(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); return ( {ExpensifyFeatures.map(({translationKey, icon}) => ( From 2e67ea4107be3cd590adfec5c622aca6e212630e Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 7 Dec 2024 00:29:58 +0530 Subject: [PATCH 23/25] Refactor MenuItem and MigratedUserWelcomeModal components to use Icon and RenderHTML for improved rendering and layout --- src/components/MenuItem.tsx | 4 ++-- src/components/MigratedUserWelcomeModal.tsx | 24 ++++++++++----------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 1badf39d40ad..259ae3170825 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -759,9 +759,9 @@ function MenuItem( {(!!title || !!shouldShowTitleIcon) && ( {!!title && (shouldRenderAsHTML || (shouldParseTitle && !!html.length)) && ( - + - + )} {!shouldRenderAsHTML && !shouldParseTitle && !!title && ( ( - + + ${convertToLTR(translate(translationKey))}`} /> + ))} From ba2f51b39b15fc4c9b58382b2254c410bd6348dc Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sat, 7 Dec 2024 01:48:03 +0530 Subject: [PATCH 24/25] translations fix --- src/languages/en.ts | 2 +- src/languages/es.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 9278f0d13ccd..3aed4aba3fec 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5428,7 +5428,7 @@ const translations = { exploreExpensify: 'Explore everything Expensify has to offer', }, migratedUserWelcomeModal: { - title: 'Travel and expense at the speed of chat!', + title: 'Travel and expense, at the speed of chat', subtitle: 'New Expensify has the same great automation, but now with amazing collaboration:', confirmText: "Let's go!", features: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 0d3426d21eda..44a89a38e382 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5948,13 +5948,13 @@ const translations = { exploreExpensify: 'Explora todo lo que Expensify tiene para ofrecer', }, migratedUserWelcomeModal: { - title: 'Travel and expense at the speed of chat!', - subtitle: 'New Expensify has the same great automation, but now with amazing collaboration:', - confirmText: "Let's go!", + title: 'Viajes y gastos, a la velocidad del chat', + subtitle: 'New Expensify tiene la misma excelente automatización, pero ahora con una colaboración increíble:', + confirmText: 'Vamos!', features: { - chat: 'Chat directly on any expense, report, or workspace', - scanReceipt: 'Scan receipts and get paid back', - crossPlatform: 'Do everything from your phone or browser', + chat: 'Chatea directamente en cualquier gasto, informe o espacio de trabajo', + scanReceipt: 'Escanea recibos y obtén reembolsos', + crossPlatform: 'Haz todo desde tu teléfono o navegador', }, }, }; From 457582949098f29d3019a2e3eabd092eb22e0256 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 8 Dec 2024 23:21:13 +0530 Subject: [PATCH 25/25] Fix typo in CONST --- src/CONST.ts | 2 +- src/components/MigratedUserWelcomeModal.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 77ba49754269..0f712c2b5ed2 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6374,7 +6374,7 @@ const CONST = { REORDERING_REACT_NATIVE_ACTIVITY_TO_FRONT: 'reorderingReactNativeActivityToFront', }, - MIGRTED_USER_WELCOME_MODAL: 'migratedUserWelcomeModal', + MIGRATED_USER_WELCOME_MODAL: 'migratedUserWelcomeModal', } as const; type Country = keyof typeof CONST.ALL_COUNTRIES; diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index e9f8646277c0..d097e3095298 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -45,7 +45,7 @@ function OnboardingWelcomeVideo() { confirmText={translate('migratedUserWelcomeModal.confirmText')} animation={LottieAnimations.WorkspacePlanet} onClose={() => { - Welcome.dismissProductTraining(CONST.MIGRTED_USER_WELCOME_MODAL); + Welcome.dismissProductTraining(CONST.MIGRATED_USER_WELCOME_MODAL); }} animationStyle={[styles.emptyWorkspaceIllustrationStyle]} animationInnerContainerStyle={[StyleUtils.getBackgroundColorStyle(LottieAnimations.WorkspacePlanet.backgroundColor), styles.cardSectionIllustration]}