From ccab1f77ce53ed62b2eb616b384f887dafea5000 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Sun, 23 Feb 2025 23:11:18 +0530 Subject: [PATCH 01/17] Add close button to product training tooltips and track dismissal method --- assets/images/x-letter.svg | 14 ++++ src/components/Icon/Expensicons.ts | 2 + .../ProductTrainingContext/TOOLTIPS.ts | 20 ++--- .../ProductTrainingContext/index.tsx | 73 ++++++++++++------- src/hooks/useOnboardingFlow.ts | 4 +- .../API/parameters/DismissProductTraining.ts | 1 + src/libs/TooltipUtils.ts | 16 ++++ src/libs/actions/Welcome/index.ts | 10 ++- src/types/onyx/DismissedProductTraining.ts | 37 ++++++---- .../ProductTrainingContextProvider.tsx | 36 ++++++--- 10 files changed, 150 insertions(+), 63 deletions(-) create mode 100644 assets/images/x-letter.svg create mode 100644 src/libs/TooltipUtils.ts diff --git a/assets/images/x-letter.svg b/assets/images/x-letter.svg new file mode 100644 index 000000000000..559b11671e41 --- /dev/null +++ b/assets/images/x-letter.svg @@ -0,0 +1,14 @@ + + + + diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index db5c81a0cfc3..0f42d1201be8 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -213,6 +213,7 @@ import Workflows from '@assets/images/workflows.svg'; import Workspace from '@assets/images/workspace-default-avatar.svg'; import Wrench from '@assets/images/wrench.svg'; import Clear from '@assets/images/x-circle.svg'; +import CrossX from '@assets/images/x-letter.svg'; import Zoom from '@assets/images/zoom.svg'; export { @@ -432,4 +433,5 @@ export { Train, boltSlash, MagnifyingGlassSpyMouthClosed, + CrossX, }; diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index 90e91166fba7..c8191386eca2 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -25,7 +25,7 @@ type ShouldShowConditionProps = { type TooltipData = { content: Array<{text: TranslationPaths; isBold: boolean}>; - onHideTooltip: () => void; + onHideTooltip: (isDismissedUsingX?: boolean) => void; name: ProductTrainingTooltipName; priority: number; shouldShow: (props: ShouldShowConditionProps) => boolean; @@ -38,17 +38,17 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.conciergeLHNGBR.part1', isBold: false}, {text: 'productTrainingTooltip.conciergeLHNGBR.part2', isBold: true}, ], - onHideTooltip: () => dismissProductTraining(CONCEIRGE_LHN_GBR), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(CONCEIRGE_LHN_GBR, isDismissedUsingX), name: CONCEIRGE_LHN_GBR, priority: 1300, - shouldShow: ({shouldUseNarrowLayout}) => !!shouldUseNarrowLayout, + shouldShow: () => true, }, [RENAME_SAVED_SEARCH]: { content: [ {text: 'productTrainingTooltip.saveSearchTooltip.part1', isBold: true}, {text: 'productTrainingTooltip.saveSearchTooltip.part2', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(RENAME_SAVED_SEARCH), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(RENAME_SAVED_SEARCH, isDismissedUsingX), name: RENAME_SAVED_SEARCH, priority: 1250, shouldShow: ({shouldUseNarrowLayout}) => !shouldUseNarrowLayout, @@ -59,7 +59,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.globalCreateTooltip.part2', isBold: false}, {text: 'productTrainingTooltip.globalCreateTooltip.part3', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(GLOBAL_CREATE_TOOLTIP), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(GLOBAL_CREATE_TOOLTIP, isDismissedUsingX), name: GLOBAL_CREATE_TOOLTIP, priority: 1200, shouldShow: () => true, @@ -69,7 +69,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.quickActionButton.part1', isBold: true}, {text: 'productTrainingTooltip.quickActionButton.part2', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(QUICK_ACTION_BUTTON), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(QUICK_ACTION_BUTTON, isDismissedUsingX), name: QUICK_ACTION_BUTTON, priority: 1150, shouldShow: () => true, @@ -80,7 +80,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.workspaceChatCreate.part2', isBold: true}, {text: 'productTrainingTooltip.workspaceChatCreate.part3', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(WORKSAPCE_CHAT_CREATE), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(WORKSAPCE_CHAT_CREATE, isDismissedUsingX), name: WORKSAPCE_CHAT_CREATE, priority: 1100, shouldShow: () => true, @@ -90,7 +90,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.searchFilterButtonTooltip.part1', isBold: true}, {text: 'productTrainingTooltip.searchFilterButtonTooltip.part2', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(SEARCH_FILTER_BUTTON_TOOLTIP), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(SEARCH_FILTER_BUTTON_TOOLTIP, isDismissedUsingX), name: SEARCH_FILTER_BUTTON_TOOLTIP, priority: 1000, shouldShow: () => true, @@ -101,7 +101,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.bottomNavInboxTooltip.part2', isBold: false}, {text: 'productTrainingTooltip.bottomNavInboxTooltip.part3', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP, isDismissedUsingX), name: BOTTOM_NAV_INBOX_TOOLTIP, priority: 900, shouldShow: () => true, @@ -112,7 +112,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.workspaceChatTooltip.part2', isBold: false}, {text: 'productTrainingTooltip.workspaceChatTooltip.part3', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(LHN_WORKSPACE_CHAT_TOOLTIP), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(LHN_WORKSPACE_CHAT_TOOLTIP, isDismissedUsingX), name: LHN_WORKSPACE_CHAT_TOOLTIP, priority: 800, shouldShow: () => true, diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 474741cbda42..6cec81377993 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -4,6 +4,7 @@ import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; +import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -12,6 +13,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {parseFSAttributes} from '@libs/Fullstory'; import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors'; +import {isProductTrainingElementDismissed} from '@libs/TooltipUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; @@ -98,7 +100,7 @@ function ProductTrainingContextProvider({children}: ChildrenProps) { return false; } - const isDismissed = !!dismissedProductTraining?.[tooltipName]; + const isDismissed = isProductTrainingElementDismissed(tooltipName, dismissedProductTraining); if (isDismissed) { return false; @@ -203,6 +205,22 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou */ useLayoutEffect(parseFSAttributes, []); + const shouldShowProductTrainingTooltip = useMemo(() => { + return shouldShow && shouldRenderTooltip(tooltipName) && !shouldHideToolTip; + }, [shouldRenderTooltip, tooltipName, shouldShow, shouldHideToolTip]); + + const hideProductTrainingTooltip = useCallback( + (isDismissedUsingX = false) => { + if (!shouldShowProductTrainingTooltip) { + return; + } + const tooltip = TOOLTIPS[tooltipName]; + tooltip.onHideTooltip(isDismissedUsingX); + unregisterTooltip(tooltipName); + }, + [tooltipName, shouldShowProductTrainingTooltip, unregisterTooltip], + ); + const renderProductTrainingTooltip = useCallback(() => { const tooltip = TOOLTIPS[tooltipName]; return ( @@ -239,6 +257,20 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou ); })} + { + hideProductTrainingTooltip(true); + }} + style={[styles.flexRow, styles.alignItemsCenter, styles.flex1]} + accessibilityLabel={translate('productTrainingTooltip.scanTestTooltip.noThanks')} + role={CONST.ROLE.BUTTON} + > + + {!!tooltip?.shouldRenderActionButtons && ( @@ -258,43 +290,32 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou ); }, [ - config.onConfirm, - config.onDismiss, + tooltipName, styles.alignItemsCenter, - styles.flex1, styles.flexRow, + styles.justifyContentStart, + styles.justifyContentCenter, styles.flexWrap, + styles.textAlignCenter, styles.gap3, - styles.justifyContentBetween, - styles.justifyContentCenter, - styles.mw100, styles.p2, styles.productTrainingTooltipText, - styles.pv2, - styles.textAlignCenter, - styles.textBold, styles.textWrap, - styles.gap2, - styles.justifyContentStart, + styles.mw100, + styles.flex1, + styles.justifyContentBetween, styles.ph2, + styles.pv2, + styles.gap2, + styles.textBold, theme.tooltipHighlightText, - tooltipName, + theme.icon, translate, + config.onConfirm, + config.onDismiss, + hideProductTrainingTooltip, ]); - const shouldShowProductTrainingTooltip = useMemo(() => { - return shouldShow && shouldRenderTooltip(tooltipName) && !shouldHideToolTip; - }, [shouldRenderTooltip, tooltipName, shouldShow, shouldHideToolTip]); - - const hideProductTrainingTooltip = useCallback(() => { - if (!shouldShowProductTrainingTooltip) { - return; - } - const tooltip = TOOLTIPS[tooltipName]; - tooltip.onHideTooltip(); - unregisterTooltip(tooltipName); - }, [tooltipName, shouldShowProductTrainingTooltip, unregisterTooltip]); - return { renderProductTrainingTooltip, hideProductTrainingTooltip, diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 0aaa8ddad9d0..cf45cd9ae9d5 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -6,6 +6,7 @@ import {startOnboardingFlow} from '@libs/actions/Welcome/OnboardingFlow'; import Navigation from '@libs/Navigation/Navigation'; import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector} from '@libs/onboardingSelectors'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; +import {isProductTrainingElementDismissed} from '@libs/TooltipUtils'; import CONFIG from '@src/CONFIG'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -45,8 +46,7 @@ function useOnboardingFlowRouter() { if (CONFIG.IS_HYBRID_APP && isLoadingOnyxValue(isSingleNewDotEntryMetadata)) { return; } - - if (hasBeenAddedToNudgeMigration && !dismissedProductTraining?.migratedUserWelcomeModal) { + if (hasBeenAddedToNudgeMigration && !isProductTrainingElementDismissed('migratedUserWelcomeModal', dismissedProductTraining)) { const defaultCannedQuery = buildCannedSearchQuery(); const query = defaultCannedQuery; Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query})); diff --git a/src/libs/API/parameters/DismissProductTraining.ts b/src/libs/API/parameters/DismissProductTraining.ts index 6a82ad995294..f3c066a5f948 100644 --- a/src/libs/API/parameters/DismissProductTraining.ts +++ b/src/libs/API/parameters/DismissProductTraining.ts @@ -1,5 +1,6 @@ type DismissProductTrainingParams = { name: string; + dismissedMethod: 'x' | 'click'; }; export default DismissProductTrainingParams; diff --git a/src/libs/TooltipUtils.ts b/src/libs/TooltipUtils.ts new file mode 100644 index 000000000000..16607c434553 --- /dev/null +++ b/src/libs/TooltipUtils.ts @@ -0,0 +1,16 @@ +import Onyx from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {DismissedProductTraining} from '@src/types/onyx'; + +let nvpDismissedProductTraining: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, + callback: (value) => (nvpDismissedProductTraining = value), +}); + +function isProductTrainingElementDismissed(elementName: keyof DismissedProductTraining, dismissedProductTraining: OnyxEntry = nvpDismissedProductTraining) { + return typeof dismissedProductTraining?.[elementName] === 'string' ? !!dismissedProductTraining?.[elementName] : !!dismissedProductTraining?.[elementName]?.dismissedTime; +} + +export {isProductTrainingElementDismissed}; diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index a98b600e5baf..fe66940eea0a 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -207,18 +207,22 @@ function setSelfTourViewed(shouldUpdateOnyxDataOnlyLocally = false) { API.write(WRITE_COMMANDS.SELF_TOUR_VIEWED, null, {optimisticData}); } -function dismissProductTraining(elementName: string) { +function dismissProductTraining(elementName: string, isDismissedUsingX = false) { const date = new Date(); + const dismissedMethod = isDismissedUsingX ? 'x' : 'click'; const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, value: { - [elementName]: DateUtils.getDBTime(date.valueOf()), + [elementName]: { + dismissedTime: DateUtils.getDBTime(date.valueOf()), + dismissedMethod + }, }, }, ]; - API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: elementName}, {optimisticData}); + API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: elementName, dismissedMethod}, {optimisticData}); } export { diff --git a/src/types/onyx/DismissedProductTraining.ts b/src/types/onyx/DismissedProductTraining.ts index 283c660be2e1..be1f886a4320 100644 --- a/src/types/onyx/DismissedProductTraining.ts +++ b/src/types/onyx/DismissedProductTraining.ts @@ -13,6 +13,17 @@ const { SCAN_TEST_TOOLTIP_MANAGER, SCAN_TEST_CONFIRMATION, } = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES; + +/** + * This type is used to store the timestamp of when the user dismisses a product training ui elements. + */ +type DismissedProductTrainingElement = { + /** The timestamp of when the user dismissed the product training element. */ + dismissedTime: string; + + /** The method of how the user dismissed the product training element, click or x. */ + dismissedMethod: 'click' | 'x'; +}; /** * This type is used to store the timestamp of when the user dismisses a product training ui elements. */ @@ -20,67 +31,67 @@ type DismissedProductTraining = { /** * When user dismisses the nudgeMigration Welcome Modal, we store the timestamp here. */ - [CONST.MIGRATED_USER_WELCOME_MODAL]: string; + [CONST.MIGRATED_USER_WELCOME_MODAL]: DismissedProductTrainingElement; /** * When user dismisses the conciergeLHNGBR product training tooltip, we store the timestamp here. */ - [CONCEIRGE_LHN_GBR]: string; + [CONCEIRGE_LHN_GBR]: DismissedProductTrainingElement; /** * When user dismisses the renameSavedSearch product training tooltip, we store the timestamp here. */ - [RENAME_SAVED_SEARCH]: string; + [RENAME_SAVED_SEARCH]: DismissedProductTrainingElement; /** * When user dismisses the workspaceChatCreate product training tooltip, we store the timestamp here. */ - [WORKSAPCE_CHAT_CREATE]: string; + [WORKSAPCE_CHAT_CREATE]: DismissedProductTrainingElement; /** * When user dismisses the quickActionButton product training tooltip, we store the timestamp here. */ - [QUICK_ACTION_BUTTON]: string; + [QUICK_ACTION_BUTTON]: DismissedProductTrainingElement; /** * When user dismisses the searchFilterButtonTooltip product training tooltip, we store the timestamp here. */ - [SEARCH_FILTER_BUTTON_TOOLTIP]: string; + [SEARCH_FILTER_BUTTON_TOOLTIP]: DismissedProductTrainingElement; /** * When user dismisses the bottomNavInboxTooltip product training tooltip, we store the timestamp here. */ - [BOTTOM_NAV_INBOX_TOOLTIP]: string; + [BOTTOM_NAV_INBOX_TOOLTIP]: DismissedProductTrainingElement; /** * When user dismisses the lhnWorkspaceChatTooltip product training tooltip, we store the timestamp here. */ - [LHN_WORKSPACE_CHAT_TOOLTIP]: string; + [LHN_WORKSPACE_CHAT_TOOLTIP]: DismissedProductTrainingElement; /** * When user dismisses the globalCreateTooltip product training tooltip, we store the timestamp here. */ - [GLOBAL_CREATE_TOOLTIP]: string; + [GLOBAL_CREATE_TOOLTIP]: DismissedProductTrainingElement; /** * When user dismisses the globalCreateTooltip product training tooltip, we store the timestamp here. */ - [SCAN_TEST_TOOLTIP]: string; + [SCAN_TEST_TOOLTIP]: DismissedProductTrainingElement; /** * When user dismisses the test manager tooltip product training tooltip, we store the timestamp here. */ - [SCAN_TEST_TOOLTIP_MANAGER]: string; + [SCAN_TEST_TOOLTIP_MANAGER]: DismissedProductTrainingElement; /** * When user dismisses the test manager on confirmantion page product training tooltip, we store the timestamp here. */ - [SCAN_TEST_CONFIRMATION]: string; + [SCAN_TEST_CONFIRMATION]: DismissedProductTrainingElement; /** * When user dismisses the ChangeReportPolicy feature training modal, we store the timestamp here. */ - [CONST.CHANGE_POLICY_TRAINING_MODAL]: string; + [CONST.CHANGE_POLICY_TRAINING_MODAL]: DismissedProductTrainingElement; }; export default DismissedProductTraining; diff --git a/tests/ui/components/ProductTrainingContextProvider.tsx b/tests/ui/components/ProductTrainingContextProvider.tsx index 6bc131f80be2..0d25e70cf4bb 100644 --- a/tests/ui/components/ProductTrainingContextProvider.tsx +++ b/tests/ui/components/ProductTrainingContextProvider.tsx @@ -129,7 +129,9 @@ describe('ProductTrainingContextProvider', () => { Onyx.merge(ONYXKEYS.NVP_TRYNEWDOT, {nudgeMigration: {timestamp: new Date()}}); const date = new Date(); Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { - migratedUserWelcomeModal: DateUtils.getDBTime(date.valueOf()), + migratedUserWelcomeModal: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, }); await waitForBatchedUpdatesWithAct(); @@ -148,8 +150,12 @@ describe('ProductTrainingContextProvider', () => { Onyx.merge(ONYXKEYS.NVP_ONBOARDING, {hasCompletedGuidedSetupFlow: true}); const testTooltip = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.GLOBAL_CREATE_TOOLTIP; Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { - migratedUserWelcomeModal: DateUtils.getDBTime(date.valueOf()), - [testTooltip]: DateUtils.getDBTime(date.valueOf()), + migratedUserWelcomeModal: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, + [testTooltip]: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, }); await waitForBatchedUpdatesWithAct(); @@ -164,7 +170,9 @@ describe('ProductTrainingContextProvider', () => { Onyx.merge(ONYXKEYS.NVP_TRYNEWDOT, {nudgeMigration: {timestamp: new Date()}}); const date = new Date(); Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { - migratedUserWelcomeModal: DateUtils.getDBTime(date.valueOf()), + migratedUserWelcomeModal: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, }); await waitForBatchedUpdatesWithAct(); const testTooltip = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.GLOBAL_CREATE_TOOLTIP; @@ -238,7 +246,9 @@ describe('ProductTrainingContextProvider', () => { Onyx.merge(ONYXKEYS.NVP_ONBOARDING, {hasCompletedGuidedSetupFlow: true}); const date = new Date(); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { - migratedUserWelcomeModal: DateUtils.getDBTime(date.valueOf()), + migratedUserWelcomeModal: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, }); await waitForBatchedUpdatesWithAct(); @@ -267,8 +277,12 @@ describe('ProductTrainingContextProvider', () => { const lowPriorityTooltip = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SEARCH_FILTER_BUTTON_TOOLTIP; Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { - migratedUserWelcomeModal: DateUtils.getDBTime(date.valueOf()), - [highPriorityTooltip]: DateUtils.getDBTime(date.valueOf()), + migratedUserWelcomeModal: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, + [highPriorityTooltip]: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, }); await waitForBatchedUpdatesWithAct(); @@ -291,7 +305,9 @@ describe('ProductTrainingContextProvider', () => { Onyx.merge(ONYXKEYS.NVP_ONBOARDING, {hasCompletedGuidedSetupFlow: true}); const date = new Date(); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { - migratedUserWelcomeModal: DateUtils.getDBTime(date.valueOf()), + migratedUserWelcomeModal: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, }); await waitForBatchedUpdatesWithAct(); @@ -312,7 +328,9 @@ describe('ProductTrainingContextProvider', () => { // When dismissing higher priority tooltip Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { - [highPriorityTooltip]: DateUtils.getDBTime(date.valueOf()), + [highPriorityTooltip]: { + dismissedTime: DateUtils.getDBTime(date.valueOf()) + }, }); await waitForBatchedUpdatesWithAct(); From 322e143e261d9569fadce12ede5cdef33eed51c9 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 19 Mar 2025 03:54:23 +0530 Subject: [PATCH 02/17] prettier --- src/libs/actions/Welcome/index.ts | 2 +- .../ProductTrainingContextProvider.tsx | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index fe66940eea0a..7a4552550149 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -217,7 +217,7 @@ function dismissProductTraining(elementName: string, isDismissedUsingX = false) value: { [elementName]: { dismissedTime: DateUtils.getDBTime(date.valueOf()), - dismissedMethod + dismissedMethod, }, }, }, diff --git a/tests/ui/components/ProductTrainingContextProvider.tsx b/tests/ui/components/ProductTrainingContextProvider.tsx index 0d25e70cf4bb..c8a6abeee3cc 100644 --- a/tests/ui/components/ProductTrainingContextProvider.tsx +++ b/tests/ui/components/ProductTrainingContextProvider.tsx @@ -130,7 +130,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -151,10 +151,10 @@ describe('ProductTrainingContextProvider', () => { const testTooltip = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.GLOBAL_CREATE_TOOLTIP; Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, [testTooltip]: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -171,7 +171,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -247,7 +247,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -278,10 +278,10 @@ describe('ProductTrainingContextProvider', () => { Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, [highPriorityTooltip]: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -306,7 +306,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -329,7 +329,7 @@ describe('ProductTrainingContextProvider', () => { // When dismissing higher priority tooltip Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { [highPriorityTooltip]: { - dismissedTime: DateUtils.getDBTime(date.valueOf()) + dismissedTime: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); From e0ad3575695eee5fb499e352a4b0138181247cea Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 19 Mar 2025 03:58:45 +0530 Subject: [PATCH 03/17] fix tests --- src/components/ProductTrainingContext/TOOLTIPS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index c8191386eca2..de511e2f6c8c 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -41,7 +41,7 @@ const TOOLTIPS: Record = { onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(CONCEIRGE_LHN_GBR, isDismissedUsingX), name: CONCEIRGE_LHN_GBR, priority: 1300, - shouldShow: () => true, + shouldShow: ({shouldUseNarrowLayout}) => !!shouldUseNarrowLayout, }, [RENAME_SAVED_SEARCH]: { content: [ From 462a06923d45def74f7e9ae81e312ffaa6210549 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 19 Mar 2025 04:08:51 +0530 Subject: [PATCH 04/17] fix lint --- src/components/ProductTrainingContext/index.tsx | 2 +- src/hooks/useOnboardingFlow.ts | 2 +- src/libs/TooltipUtils.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 6cec81377993..cde84d94bbc8 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -13,7 +13,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {parseFSAttributes} from '@libs/Fullstory'; import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors'; -import {isProductTrainingElementDismissed} from '@libs/TooltipUtils'; +import isProductTrainingElementDismissed from '@libs/TooltipUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index cf45cd9ae9d5..811a1dae4d2e 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -6,7 +6,7 @@ import {startOnboardingFlow} from '@libs/actions/Welcome/OnboardingFlow'; import Navigation from '@libs/Navigation/Navigation'; import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector} from '@libs/onboardingSelectors'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; -import {isProductTrainingElementDismissed} from '@libs/TooltipUtils'; +import isProductTrainingElementDismissed from '@libs/TooltipUtils'; import CONFIG from '@src/CONFIG'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/libs/TooltipUtils.ts b/src/libs/TooltipUtils.ts index 16607c434553..b262d21adb8f 100644 --- a/src/libs/TooltipUtils.ts +++ b/src/libs/TooltipUtils.ts @@ -13,4 +13,4 @@ function isProductTrainingElementDismissed(elementName: keyof DismissedProductTr return typeof dismissedProductTraining?.[elementName] === 'string' ? !!dismissedProductTraining?.[elementName] : !!dismissedProductTraining?.[elementName]?.dismissedTime; } -export {isProductTrainingElementDismissed}; +export default isProductTrainingElementDismissed; From c214dbbd94c486d647f55270674a489db1e425d5 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 19 Mar 2025 04:16:37 +0530 Subject: [PATCH 05/17] fix type error --- src/libs/actions/Report.ts | 2 +- tests/unit/OptionsListUtilsTest.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 660926815b25..fa2284542f53 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -4861,7 +4861,7 @@ function dismissChangePolicyModal() { }, }, ]; - API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: CONST.CHANGE_POLICY_TRAINING_MODAL}, {optimisticData}); + API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: CONST.CHANGE_POLICY_TRAINING_MODAL, dismissedMethod: 'click'}, {optimisticData}); } /** diff --git a/tests/unit/OptionsListUtilsTest.ts b/tests/unit/OptionsListUtilsTest.ts index 6da927490eeb..3713855a164c 100644 --- a/tests/unit/OptionsListUtilsTest.ts +++ b/tests/unit/OptionsListUtilsTest.ts @@ -684,7 +684,9 @@ describe('OptionsListUtils', () => { expect(options.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: CONST.EMAIL.MANAGER_MCTEST})])); return waitForBatchedUpdates() - .then(() => Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {[CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP]: new Date() as unknown as string})) + .then(() => Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {[CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP]: { + dismissedTime: DateUtils.getDBTime(new Date().valueOf()), + }})) .then(() => { // Manager McTest shouldn't be included to recipients when the user has already submitted an expense const optionsWhenUserAlreadySubmittedExpense = getValidOptions( From fedbd1d4f2772b1ebbf00dd6c80389de5c236fcd Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 19 Mar 2025 23:08:59 +0530 Subject: [PATCH 06/17] fix as per comments --- src/libs/TooltipUtils.ts | 2 +- src/libs/actions/Welcome/index.ts | 2 +- src/types/onyx/DismissedProductTraining.ts | 4 ++-- .../ProductTrainingContextProvider.tsx | 18 +++++++++--------- tests/unit/OptionsListUtilsTest.ts | 10 +++++++--- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/libs/TooltipUtils.ts b/src/libs/TooltipUtils.ts index b262d21adb8f..f77dbe95fb6c 100644 --- a/src/libs/TooltipUtils.ts +++ b/src/libs/TooltipUtils.ts @@ -10,7 +10,7 @@ Onyx.connect({ }); function isProductTrainingElementDismissed(elementName: keyof DismissedProductTraining, dismissedProductTraining: OnyxEntry = nvpDismissedProductTraining) { - return typeof dismissedProductTraining?.[elementName] === 'string' ? !!dismissedProductTraining?.[elementName] : !!dismissedProductTraining?.[elementName]?.dismissedTime; + return typeof dismissedProductTraining?.[elementName] === 'string' ? !!dismissedProductTraining?.[elementName] : !!dismissedProductTraining?.[elementName]?.timestamp; } export default isProductTrainingElementDismissed; diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 7a4552550149..3f31d58ce490 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -216,7 +216,7 @@ function dismissProductTraining(elementName: string, isDismissedUsingX = false) key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, value: { [elementName]: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), dismissedMethod, }, }, diff --git a/src/types/onyx/DismissedProductTraining.ts b/src/types/onyx/DismissedProductTraining.ts index be1f886a4320..d11672e70296 100644 --- a/src/types/onyx/DismissedProductTraining.ts +++ b/src/types/onyx/DismissedProductTraining.ts @@ -19,10 +19,10 @@ const { */ type DismissedProductTrainingElement = { /** The timestamp of when the user dismissed the product training element. */ - dismissedTime: string; + timestamp: string; /** The method of how the user dismissed the product training element, click or x. */ - dismissedMethod: 'click' | 'x'; + dismissedMethod: 'click' | 'x' | 'elsewhere'; }; /** * This type is used to store the timestamp of when the user dismisses a product training ui elements. diff --git a/tests/ui/components/ProductTrainingContextProvider.tsx b/tests/ui/components/ProductTrainingContextProvider.tsx index c8a6abeee3cc..d9e2a09023ca 100644 --- a/tests/ui/components/ProductTrainingContextProvider.tsx +++ b/tests/ui/components/ProductTrainingContextProvider.tsx @@ -130,7 +130,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -151,10 +151,10 @@ describe('ProductTrainingContextProvider', () => { const testTooltip = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.GLOBAL_CREATE_TOOLTIP; Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, [testTooltip]: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -171,7 +171,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -247,7 +247,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -278,10 +278,10 @@ describe('ProductTrainingContextProvider', () => { Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, [highPriorityTooltip]: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -306,7 +306,7 @@ describe('ProductTrainingContextProvider', () => { const date = new Date(); Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { migratedUserWelcomeModal: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); @@ -329,7 +329,7 @@ describe('ProductTrainingContextProvider', () => { // When dismissing higher priority tooltip Onyx.merge(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { [highPriorityTooltip]: { - dismissedTime: DateUtils.getDBTime(date.valueOf()), + timestamp: DateUtils.getDBTime(date.valueOf()), }, }); await waitForBatchedUpdatesWithAct(); diff --git a/tests/unit/OptionsListUtilsTest.ts b/tests/unit/OptionsListUtilsTest.ts index 3713855a164c..009c8acfe3ad 100644 --- a/tests/unit/OptionsListUtilsTest.ts +++ b/tests/unit/OptionsListUtilsTest.ts @@ -684,9 +684,13 @@ describe('OptionsListUtils', () => { expect(options.personalDetails).not.toEqual(expect.arrayContaining([expect.objectContaining({login: CONST.EMAIL.MANAGER_MCTEST})])); return waitForBatchedUpdates() - .then(() => Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {[CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP]: { - dismissedTime: DateUtils.getDBTime(new Date().valueOf()), - }})) + .then(() => + Onyx.set(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, { + [CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP]: { + timestamp: DateUtils.getDBTime(new Date().valueOf()), + }, + }), + ) .then(() => { // Manager McTest shouldn't be included to recipients when the user has already submitted an expense const optionsWhenUserAlreadySubmittedExpense = getValidOptions( From 14ecc886ec1c1721e2e328b36a65c35f2f1324dd Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Thu, 20 Mar 2025 00:50:05 +0530 Subject: [PATCH 07/17] Remove unused x-letter icon --- assets/images/x-letter.svg | 14 -------------- src/components/Icon/Expensicons.ts | 2 -- src/components/ProductTrainingContext/index.tsx | 6 ++++-- 3 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 assets/images/x-letter.svg diff --git a/assets/images/x-letter.svg b/assets/images/x-letter.svg deleted file mode 100644 index 559b11671e41..000000000000 --- a/assets/images/x-letter.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index c5ca05706e67..146a58f5940f 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -214,7 +214,6 @@ import Workflows from '@assets/images/workflows.svg'; import Workspace from '@assets/images/workspace-default-avatar.svg'; import Wrench from '@assets/images/wrench.svg'; import Clear from '@assets/images/x-circle.svg'; -import CrossX from '@assets/images/x-letter.svg'; import Zoom from '@assets/images/zoom.svg'; export { @@ -435,5 +434,4 @@ export { Train, boltSlash, MagnifyingGlassSpyMouthClosed, - CrossX, }; diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index cde84d94bbc8..5af72dde0603 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -20,6 +20,7 @@ import type ChildrenProps from '@src/types/utils/ChildrenProps'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import type {ProductTrainingTooltipName} from './TOOLTIPS'; import TOOLTIPS from './TOOLTIPS'; +import variables from '@styles/variables'; type ProductTrainingContextType = { shouldRenderTooltip: (tooltipName: ProductTrainingTooltipName) => boolean; @@ -266,9 +267,10 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou role={CONST.ROLE.BUTTON} > From 66fcb94e6ed91add6940a0d9c686faa128e765a0 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Thu, 20 Mar 2025 00:53:55 +0530 Subject: [PATCH 08/17] prettier --- src/components/ProductTrainingContext/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 5af72dde0603..f9d412c689a6 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -14,13 +14,13 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {parseFSAttributes} from '@libs/Fullstory'; import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors'; import isProductTrainingElementDismissed from '@libs/TooltipUtils'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import type {ProductTrainingTooltipName} from './TOOLTIPS'; import TOOLTIPS from './TOOLTIPS'; -import variables from '@styles/variables'; type ProductTrainingContextType = { shouldRenderTooltip: (tooltipName: ProductTrainingTooltipName) => boolean; From e5c39ef72ac6ba861d70c3dd55d03877b7f6de0c Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 00:22:18 +0530 Subject: [PATCH 09/17] fix padding --- src/components/ProductTrainingContext/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 107b744a097c..40b674f62b11 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -236,7 +236,8 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou styles.flexWrap, styles.textAlignCenter, styles.gap3, - styles.p2, + styles.pv2, + styles.ph1, ]} > { hideProductTrainingTooltip(true); }} - style={[styles.flexRow, styles.alignItemsCenter, styles.flex1]} accessibilityLabel={translate('productTrainingTooltip.scanTestTooltip.noThanks')} role={CONST.ROLE.BUTTON} > @@ -299,14 +299,14 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou styles.flexWrap, styles.textAlignCenter, styles.gap3, - styles.p2, + styles.pv2, + styles.ph1, styles.productTrainingTooltipText, styles.textWrap, styles.mw100, styles.flex1, styles.justifyContentBetween, styles.ph2, - styles.pv2, styles.gap2, styles.textBold, theme.tooltipHighlightText, From da17ce0cb50419c111e1151571ec4ac7e4101dcd Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 00:42:34 +0530 Subject: [PATCH 10/17] remove x from tooltip with buttons --- .../ProductTrainingContext/index.tsx | 30 ++++++++++--------- .../step/IOURequestStepScan/index.native.tsx | 2 +- .../request/step/IOURequestStepScan/index.tsx | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 40b674f62b11..cb337e4df318 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -258,20 +258,22 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou ); })} - { - hideProductTrainingTooltip(true); - }} - accessibilityLabel={translate('productTrainingTooltip.scanTestTooltip.noThanks')} - role={CONST.ROLE.BUTTON} - > - - + {!tooltip?.shouldRenderActionButtons && ( + { + hideProductTrainingTooltip(true); + }} + accessibilityLabel={translate('productTrainingTooltip.scanTestTooltip.noThanks')} + role={CONST.ROLE.BUTTON} + > + + + )} {!!tooltip?.shouldRenderActionButtons && ( diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 6a00b329a2b6..ab6b1fe76500 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -528,7 +528,7 @@ function IOURequestStepScan({ { onConfirm: setTestReceiptAndNavigate, onDismiss: () => { - dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP); + dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, true); }, }, ); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 2cef346c2125..544629580e23 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -587,7 +587,7 @@ function IOURequestStepScan({ { onConfirm: setTestReceiptAndNavigate, onDismiss: () => { - dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP); + dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, true); }, }, ); From 9cc72887d275e4a053f279683b3cd697aa0c5aab Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 01:17:29 +0530 Subject: [PATCH 11/17] Update tooltip dismissal logic to accept parameter for dismissal method --- src/components/ProductTrainingContext/TOOLTIPS.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index 1da7293a2ed7..83bd4e6fe714 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -102,7 +102,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.scanTestTooltip.part4', isBold: true}, {text: 'productTrainingTooltip.scanTestTooltip.part5', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER, isDismissedUsingX), name: SCAN_TEST_TOOLTIP_MANAGER, priority: 1000, shouldShow: () => true, @@ -113,7 +113,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.scanTestTooltip.part7', isBold: true}, {text: 'productTrainingTooltip.scanTestTooltip.part8', isBold: false}, ], - onHideTooltip: () => dismissProductTraining(SCAN_TEST_CONFIRMATION), + onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(SCAN_TEST_CONFIRMATION, isDismissedUsingX), name: SCAN_TEST_CONFIRMATION, priority: 1100, shouldShow: () => true, From 0dda3fd9d4f77cfb84022f4032142203b7776667 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 01:38:45 +0530 Subject: [PATCH 12/17] change variable name --- .../ProductTrainingContext/TOOLTIPS.ts | 16 ++++++++-------- src/components/ProductTrainingContext/index.tsx | 4 ++-- src/libs/Permissions.ts | 2 +- src/libs/actions/Welcome/index.ts | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index 83bd4e6fe714..0256c7f67988 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -22,7 +22,7 @@ type ShouldShowConditionProps = { type TooltipData = { content: Array<{text: TranslationPaths; isBold: boolean}>; - onHideTooltip: (isDismissedUsingX?: boolean) => void; + onHideTooltip: (isDismissedUsingCloseButton?: boolean) => void; name: ProductTrainingTooltipName; priority: number; shouldShow: (props: ShouldShowConditionProps) => boolean; @@ -35,7 +35,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.conciergeLHNGBR.part1', isBold: false}, {text: 'productTrainingTooltip.conciergeLHNGBR.part2', isBold: true}, ], - onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(CONCEIRGE_LHN_GBR, isDismissedUsingX), + onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(CONCEIRGE_LHN_GBR, isDismissedUsingCloseButton), name: CONCEIRGE_LHN_GBR, priority: 1300, // TODO: CONCEIRGE_LHN_GBR tooltip will be replaced by a tooltip in the #admins room @@ -47,7 +47,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.saveSearchTooltip.part1', isBold: true}, {text: 'productTrainingTooltip.saveSearchTooltip.part2', isBold: false}, ], - onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(RENAME_SAVED_SEARCH, isDismissedUsingX), + onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(RENAME_SAVED_SEARCH, isDismissedUsingCloseButton), name: RENAME_SAVED_SEARCH, priority: 1250, shouldShow: ({shouldUseNarrowLayout}) => !shouldUseNarrowLayout, @@ -58,7 +58,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.globalCreateTooltip.part2', isBold: false}, {text: 'productTrainingTooltip.globalCreateTooltip.part3', isBold: false}, ], - onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(GLOBAL_CREATE_TOOLTIP, isDismissedUsingX), + onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(GLOBAL_CREATE_TOOLTIP, isDismissedUsingCloseButton), name: GLOBAL_CREATE_TOOLTIP, priority: 1200, shouldShow: () => true, @@ -69,7 +69,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.bottomNavInboxTooltip.part2', isBold: false}, {text: 'productTrainingTooltip.bottomNavInboxTooltip.part3', isBold: false}, ], - onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP, isDismissedUsingX), + onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP, isDismissedUsingCloseButton), name: BOTTOM_NAV_INBOX_TOOLTIP, priority: 900, shouldShow: () => true, @@ -80,7 +80,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.workspaceChatTooltip.part2', isBold: false}, {text: 'productTrainingTooltip.workspaceChatTooltip.part3', isBold: false}, ], - onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(LHN_WORKSPACE_CHAT_TOOLTIP, isDismissedUsingX), + onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(LHN_WORKSPACE_CHAT_TOOLTIP, isDismissedUsingCloseButton), name: LHN_WORKSPACE_CHAT_TOOLTIP, priority: 800, shouldShow: () => true, @@ -102,7 +102,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.scanTestTooltip.part4', isBold: true}, {text: 'productTrainingTooltip.scanTestTooltip.part5', isBold: false}, ], - onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER, isDismissedUsingX), + onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER, isDismissedUsingCloseButton), name: SCAN_TEST_TOOLTIP_MANAGER, priority: 1000, shouldShow: () => true, @@ -113,7 +113,7 @@ const TOOLTIPS: Record = { {text: 'productTrainingTooltip.scanTestTooltip.part7', isBold: true}, {text: 'productTrainingTooltip.scanTestTooltip.part8', isBold: false}, ], - onHideTooltip: (isDismissedUsingX = false) => dismissProductTraining(SCAN_TEST_CONFIRMATION, isDismissedUsingX), + onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(SCAN_TEST_CONFIRMATION, isDismissedUsingCloseButton), name: SCAN_TEST_CONFIRMATION, priority: 1100, shouldShow: () => true, diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index cb337e4df318..35b4574e1e5b 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -210,12 +210,12 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou }, [shouldRenderTooltip, tooltipName, shouldShow, shouldHideToolTip]); const hideProductTrainingTooltip = useCallback( - (isDismissedUsingX = false) => { + (isDismissedUsingCloseButton = false) => { if (!shouldShowProductTrainingTooltip) { return; } const tooltip = TOOLTIPS[tooltipName]; - tooltip.onHideTooltip(isDismissedUsingX); + tooltip.onHideTooltip(isDismissedUsingCloseButton); unregisterTooltip(tooltipName); }, [tooltipName, shouldShowProductTrainingTooltip, unregisterTooltip], diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 635e996c322f..88831f7444fe 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -39,7 +39,7 @@ function canUsePDFExport(betas: OnyxEntry): boolean { } function canUseManagerMcTest(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.NEWDOT_MANAGER_MCTEST) || canUseAllBetas(betas); + return true; } function canUseInternationalBankAccount(betas: OnyxEntry): boolean { diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 3f31d58ce490..e959d84c9c13 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -207,9 +207,9 @@ function setSelfTourViewed(shouldUpdateOnyxDataOnlyLocally = false) { API.write(WRITE_COMMANDS.SELF_TOUR_VIEWED, null, {optimisticData}); } -function dismissProductTraining(elementName: string, isDismissedUsingX = false) { +function dismissProductTraining(elementName: string, isDismissedUsingCloseButton = false) { const date = new Date(); - const dismissedMethod = isDismissedUsingX ? 'x' : 'click'; + const dismissedMethod = isDismissedUsingCloseButton ? 'x' : 'click'; const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, From 505141e4a93f506b353c0f28b772324275a0c176 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 01:41:05 +0530 Subject: [PATCH 13/17] revert unrelated change --- src/libs/Permissions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 88831f7444fe..635e996c322f 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -39,7 +39,7 @@ function canUsePDFExport(betas: OnyxEntry): boolean { } function canUseManagerMcTest(betas: OnyxEntry): boolean { - return true; + return !!betas?.includes(CONST.BETAS.NEWDOT_MANAGER_MCTEST) || canUseAllBetas(betas); } function canUseInternationalBankAccount(betas: OnyxEntry): boolean { From 21d93d345345a739f3be428a5a1d742bbba130a4 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 02:04:45 +0530 Subject: [PATCH 14/17] fix as per review --- src/libs/actions/Report.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 05b7e12e169b..1260d0f0b46a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -4863,7 +4863,10 @@ function dismissChangePolicyModal() { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, value: { - [CONST.CHANGE_POLICY_TRAINING_MODAL]: DateUtils.getDBTime(date.valueOf()), + [CONST.CHANGE_POLICY_TRAINING_MODAL]: { + timestamp: DateUtils.getDBTime(date.valueOf()), + dismissedMethod: 'click' + }, }, }, ]; From 345db1d13af3922e2e7c0ec9914ef35c111449de Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 02:09:40 +0530 Subject: [PATCH 15/17] prettier --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1260d0f0b46a..7331c75c770a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -4865,7 +4865,7 @@ function dismissChangePolicyModal() { value: { [CONST.CHANGE_POLICY_TRAINING_MODAL]: { timestamp: DateUtils.getDBTime(date.valueOf()), - dismissedMethod: 'click' + dismissedMethod: 'click', }, }, }, From 1215bf0695645659058768cd9e3aafdff2a0d58c Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 02:59:52 +0530 Subject: [PATCH 16/17] changes as per review --- src/components/ProductTrainingContext/index.tsx | 13 ++++++++++--- src/libs/TooltipUtils.ts | 10 +--------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 35b4574e1e5b..4103f7047578 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -209,7 +209,7 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou return shouldShow && shouldRenderTooltip(tooltipName) && !shouldHideToolTip; }, [shouldRenderTooltip, tooltipName, shouldShow, shouldHideToolTip]); - const hideProductTrainingTooltip = useCallback( + const hideTooltip = useCallback( (isDismissedUsingCloseButton = false) => { if (!shouldShowProductTrainingTooltip) { return; @@ -261,7 +261,7 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou {!tooltip?.shouldRenderActionButtons && ( { - hideProductTrainingTooltip(true); + hideTooltip(true); }} accessibilityLabel={translate('productTrainingTooltip.scanTestTooltip.noThanks')} role={CONST.ROLE.BUTTON} @@ -316,9 +316,16 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou translate, config.onConfirm, config.onDismiss, - hideProductTrainingTooltip, + hideTooltip, ]); + const hideProductTrainingTooltip = useCallback( + () => { + hideTooltip(false); + }, + [hideTooltip], + ); + return { renderProductTrainingTooltip, hideProductTrainingTooltip, diff --git a/src/libs/TooltipUtils.ts b/src/libs/TooltipUtils.ts index f77dbe95fb6c..aa2040ce035f 100644 --- a/src/libs/TooltipUtils.ts +++ b/src/libs/TooltipUtils.ts @@ -1,15 +1,7 @@ -import Onyx from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import ONYXKEYS from '@src/ONYXKEYS'; import type {DismissedProductTraining} from '@src/types/onyx'; -let nvpDismissedProductTraining: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, - callback: (value) => (nvpDismissedProductTraining = value), -}); - -function isProductTrainingElementDismissed(elementName: keyof DismissedProductTraining, dismissedProductTraining: OnyxEntry = nvpDismissedProductTraining) { +function isProductTrainingElementDismissed(elementName: keyof DismissedProductTraining, dismissedProductTraining: OnyxEntry) { return typeof dismissedProductTraining?.[elementName] === 'string' ? !!dismissedProductTraining?.[elementName] : !!dismissedProductTraining?.[elementName]?.timestamp; } From 20d98ce1c1365772ed7f43366ec47b9150cc71e1 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 21 Mar 2025 03:03:54 +0530 Subject: [PATCH 17/17] prettier --- src/components/ProductTrainingContext/index.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 4103f7047578..02f6ad4a9c84 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -319,12 +319,9 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou hideTooltip, ]); - const hideProductTrainingTooltip = useCallback( - () => { - hideTooltip(false); - }, - [hideTooltip], - ); + const hideProductTrainingTooltip = useCallback(() => { + hideTooltip(false); + }, [hideTooltip]); return { renderProductTrainingTooltip,