From d00e2009748b155b860da01d89bb451f2afa4d91 Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Mon, 10 Feb 2025 16:52:50 +0100 Subject: [PATCH 01/10] Add test recipt toolstips to IOU Scan start page --- .../ProductTrainingContext/TOOLTIPS.ts | 14 +++---- .../ProductTrainingContext/index.tsx | 4 +- src/components/TabSelector/TabSelector.tsx | 9 +++++ .../TabSelector/TabSelectorItem.tsx | 39 ++++++++++++++++--- src/libs/OptionsListUtils.ts | 7 ++-- 5 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index 2e553b21681a..78e555ff6e4a 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -118,17 +118,17 @@ const TOOLTIPS: Record = { content: [ {text: 'productTrainingTooltip.scanTestTooltip.part1', isBold: false}, {text: 'productTrainingTooltip.scanTestTooltip.part2', isBold: true}, - {text: 'productTrainingTooltip.scanTestTooltip.part3', isBold: false}, - {text: 'productTrainingTooltip.scanTestTooltip.part4', isBold: true}, - {text: 'productTrainingTooltip.scanTestTooltip.part5', isBold: false}, - {text: 'productTrainingTooltip.scanTestTooltip.part6', isBold: false}, - {text: 'productTrainingTooltip.scanTestTooltip.part7', isBold: true}, - {text: 'productTrainingTooltip.scanTestTooltip.part8', isBold: false}, + // {text: 'productTrainingTooltip.scanTestTooltip.part3', isBold: false}, + // {text: 'productTrainingTooltip.scanTestTooltip.part4', isBold: true}, + // {text: 'productTrainingTooltip.scanTestTooltip.part5', isBold: false}, + // {text: 'productTrainingTooltip.scanTestTooltip.part6', isBold: false}, + // {text: 'productTrainingTooltip.scanTestTooltip.part7', isBold: true}, + // {text: 'productTrainingTooltip.scanTestTooltip.part8', isBold: false}, ], onHideTooltip: () => dismissProductTraining(SCAN_TEST_TOOLTIP), name: SCAN_TEST_TOOLTIP, priority: 900, - shouldShow: () => false, + shouldShow: () => true, }, }; diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index a7806574126d..ccf050002aa9 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -99,7 +99,7 @@ function ProductTrainingContextProvider({children}: ChildrenProps) { } // We need to make an exception for the QAB tooltip because it is shown in a modal, otherwise it would be hidden if a modal is visible - if (tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.QUICK_ACTION_BUTTON && isModalVisible) { + if (tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.QUICK_ACTION_BUTTON && tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP && isModalVisible) { return false; } @@ -129,7 +129,6 @@ function ProductTrainingContextProvider({children}: ChildrenProps) { return false; } const visibleTooltip = determineVisibleTooltip(); - // If this is the highest priority visible tooltip, show it if (tooltipName === visibleTooltip) { return true; @@ -217,7 +216,6 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou const shouldShowProductTrainingTooltip = useMemo(() => { return shouldShow && shouldRenderTooltip(tooltipName); }, [shouldRenderTooltip, tooltipName, shouldShow]); - const hideProductTrainingTooltip = useCallback(() => { if (!shouldShowProductTrainingTooltip) { return; diff --git a/src/components/TabSelector/TabSelector.tsx b/src/components/TabSelector/TabSelector.tsx index cc35dc482742..e41d16cb4711 100644 --- a/src/components/TabSelector/TabSelector.tsx +++ b/src/components/TabSelector/TabSelector.tsx @@ -1,13 +1,16 @@ import type {MaterialTopTabBarProps} from '@react-navigation/material-top-tabs/lib/typescript/src/types'; import React, {useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import FocusTrapContainerElement from '@components/FocusTrap/FocusTrapContainerElement'; import * as Expensicons from '@components/Icon/Expensicons'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getIsUserSubmittedExpenseOrScannedReceipt} from '@libs/OptionsListUtils'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import type IconAsset from '@src/types/utils/IconAsset'; import getBackgroundColor from './getBackground'; import getOpacity from './getOpacity'; @@ -54,6 +57,7 @@ function TabSelector({state, navigation, onTabPress = () => {}, position, onFocu const styles = useThemeStyles(); const defaultAffectedAnimatedTabs = useMemo(() => Array.from({length: state.routes.length}, (v, i) => i), [state.routes.length]); const [affectedAnimatedTabs, setAffectedAnimatedTabs] = useState(defaultAffectedAnimatedTabs); + const [betas] = useOnyx(ONYXKEYS.BETAS); useEffect(() => { // It is required to wait transition end to reset affectedAnimatedTabs because tabs style is still animating during transition. @@ -62,6 +66,10 @@ function TabSelector({state, navigation, onTabPress = () => {}, position, onFocu }, CONST.ANIMATED_TRANSITION); }, [defaultAffectedAnimatedTabs, state.index]); + const shouldShowTestReceiptTooltip = (routeName: string, isActive: boolean) => { + return routeName === CONST.TAB_REQUEST.SCAN && isActive && !getIsUserSubmittedExpenseOrScannedReceipt(betas); + }; + return ( @@ -104,6 +112,7 @@ function TabSelector({state, navigation, onTabPress = () => {}, position, onFocu backgroundColor={backgroundColor} isActive={isActive} shouldShowLabelWhenInactive={shouldShowLabelWhenInactive} + shouldShowTestReceiptTooltip={shouldShowTestReceiptTooltip(route.name, isActive)} /> ); })} diff --git a/src/components/TabSelector/TabSelectorItem.tsx b/src/components/TabSelector/TabSelectorItem.tsx index ea5ffc00d81d..6072617c8e1c 100644 --- a/src/components/TabSelector/TabSelectorItem.tsx +++ b/src/components/TabSelector/TabSelectorItem.tsx @@ -2,7 +2,9 @@ import React, {useState} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated} from 'react-native'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import {useProductTrainingContext} from '@components/ProductTrainingContext'; import Tooltip from '@components/Tooltip'; +import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -35,6 +37,9 @@ type TabSelectorItemProps = { /** Whether to show the label when the tab is inactive */ shouldShowLabelWhenInactive?: boolean; + + /** Whether to show the test receipt tooltip */ + shouldShowTestReceiptTooltip?: boolean; }; function TabSelectorItem({ @@ -46,15 +51,17 @@ function TabSelectorItem({ inactiveOpacity = 1, isActive = false, shouldShowLabelWhenInactive = true, + shouldShowTestReceiptTooltip = false, }: TabSelectorItemProps) { const styles = useThemeStyles(); const [isHovered, setIsHovered] = useState(false); + const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext( + CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, + shouldShowTestReceiptTooltip, + ); - return ( - + const content = () => { + return ( )} + ); + }; + + return shouldShowTestReceiptTooltip ? ( + + {content()} + + ) : ( + + {content()} ); } diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index e4093c9a414e..454e0d9134f7 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1449,8 +1449,8 @@ function getValidReports(reports: OptionList['reports'], config: GetValidReports /** * Whether user submitted already an expense or scanned receipt */ -function getIsUserSubmittedExpenseOrScannedReceipt(): boolean { - return !!nvpDismissedProductTraining?.[CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP]; +function getIsUserSubmittedExpenseOrScannedReceipt(betas: OnyxEntry): boolean { + return !!nvpDismissedProductTraining?.[CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP] && Permissions.canUseManagerMcTest(betas); } /** @@ -1476,7 +1476,7 @@ function getValidOptions( [CONST.EMAIL.NOTIFICATIONS]: true, ...excludeLogins, // Exclude Manager McTest if user submitted expense or scanned receipt and when selection is made from Create or Submit flow - [CONST.EMAIL.MANAGER_MCTEST]: !(Permissions.canUseManagerMcTest(config.betas) && !getIsUserSubmittedExpenseOrScannedReceipt() && canShowManagerMcTest), + [CONST.EMAIL.MANAGER_MCTEST]: !(!getIsUserSubmittedExpenseOrScannedReceipt(config.betas) && canShowManagerMcTest), }; // If we're including selected options from the search results, we only want to exclude them if the search input is empty // This is because on certain pages, we show the selected options at the top when the search input is empty @@ -2190,6 +2190,7 @@ export { orderWorkspaceOptions, filterSelfDMChat, filterReports, + getIsUserSubmittedExpenseOrScannedReceipt, }; export type {Section, SectionBase, MemberForList, Options, OptionList, SearchOption, PayeePersonalDetails, Option, OptionTree, ReportAndPersonalDetailOptions, GetUserToInviteConfig}; From 48e1b1b51e0307e1686ff4cee39f2651fde0ec0a Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Tue, 11 Feb 2025 15:13:36 +0100 Subject: [PATCH 02/10] add tooltip to manager mctest recipient --- .../SelectionList/InviteMemberListItem.tsx | 63 ++++++++++++------- src/components/TabSelector/TabSelector.tsx | 11 ++-- .../TabSelector/TabSelectorItem.tsx | 5 +- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx index 7442fe24f7d2..15512037c581 100644 --- a/src/components/SelectionList/InviteMemberListItem.tsx +++ b/src/components/SelectionList/InviteMemberListItem.tsx @@ -1,18 +1,24 @@ import {Str} from 'expensify-common'; import React, {useCallback} from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import {FallbackAvatar} from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import {useProductTrainingContext} from '@components/ProductTrainingContext'; import SelectCircle from '@components/SelectCircle'; import SubscriptAvatar from '@components/SubscriptAvatar'; import Text from '@components/Text'; import TextWithTooltip from '@components/TextWithTooltip'; +import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getIsUserSubmittedExpenseOrScannedReceipt} from '@libs/OptionsListUtils'; +import Permissions from '@libs/Permissions'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import type {Icon} from '@src/types/onyx/OnyxCommon'; import BaseListItem from './BaseListItem'; import type {InviteMemberListItemProps, ListItem} from './types'; @@ -42,6 +48,11 @@ function InviteMemberListItem({ const theme = useTheme(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); + const [betas] = useOnyx(ONYXKEYS.BETAS); + const {renderProductTrainingTooltip, shouldShowProductTrainingTooltip} = useProductTrainingContext( + CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, + !getIsUserSubmittedExpenseOrScannedReceipt() && Permissions.canUseManagerMcTest(betas) && item.login === CONST.EMAIL.MANAGER_MCTEST, + ); const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; @@ -104,28 +115,38 @@ function InviteMemberListItem({ ]} /> ))} - - - + + + + + + {!!item.alternateText && ( + + )} - {!!item.alternateText && ( - - )} - + {!!item.rightElement && item.rightElement} {!!shouldShowCheckBox && ( {}, position, onFocu }, CONST.ANIMATED_TRANSITION); }, [defaultAffectedAnimatedTabs, state.index]); - const shouldShowTestReceiptTooltip = (routeName: string, isActive: boolean) => { - return routeName === CONST.TAB_REQUEST.SCAN && isActive && !getIsUserSubmittedExpenseOrScannedReceipt(betas); - }; - return ( @@ -79,7 +76,8 @@ function TabSelector({state, navigation, onTabPress = () => {}, position, onFocu const inactiveOpacity = getOpacity({routesLength: state.routes.length, tabIndex: index, active: false, affectedTabs: affectedAnimatedTabs, position, isActive}); const backgroundColor = getBackgroundColor({routesLength: state.routes.length, tabIndex: index, affectedTabs: affectedAnimatedTabs, theme, position, isActive}); const {icon, title} = getIconAndTitle(route.name, translate); - + const shouldShowTestReceiptTooltip = + route.name === CONST.TAB_REQUEST.SCAN && isActive && !getIsUserSubmittedExpenseOrScannedReceipt() && Permissions.canUseManagerMcTest(betas); const onPress = () => { if (isActive) { return; @@ -112,7 +110,7 @@ function TabSelector({state, navigation, onTabPress = () => {}, position, onFocu backgroundColor={backgroundColor} isActive={isActive} shouldShowLabelWhenInactive={shouldShowLabelWhenInactive} - shouldShowTestReceiptTooltip={shouldShowTestReceiptTooltip(route.name, isActive)} + shouldShowTestReceiptTooltip={shouldShowTestReceiptTooltip} /> ); })} @@ -122,7 +120,6 @@ function TabSelector({state, navigation, onTabPress = () => {}, position, onFocu } TabSelector.displayName = 'TabSelector'; - export default TabSelector; export type {TabSelectorProps}; diff --git a/src/components/TabSelector/TabSelectorItem.tsx b/src/components/TabSelector/TabSelectorItem.tsx index 6072617c8e1c..5dfc0f4796f3 100644 --- a/src/components/TabSelector/TabSelectorItem.tsx +++ b/src/components/TabSelector/TabSelectorItem.tsx @@ -55,10 +55,7 @@ function TabSelectorItem({ }: TabSelectorItemProps) { const styles = useThemeStyles(); const [isHovered, setIsHovered] = useState(false); - const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext( - CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, - shouldShowTestReceiptTooltip, - ); + const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip} = useProductTrainingContext(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, shouldShowTestReceiptTooltip); const content = () => { return ( From 70aa35a6feffecfa789e6fb60ed71d7fd30ef780 Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Tue, 11 Feb 2025 17:58:17 +0100 Subject: [PATCH 03/10] added new tooltip for recipents view for trying manager mc test --- src/CONST.ts | 1 + src/components/ProductTrainingContext/TOOLTIPS.ts | 12 ++++++++++++ src/components/ProductTrainingContext/index.tsx | 9 ++++++++- .../SelectionList/InviteMemberListItem.tsx | 6 +++++- src/libs/OptionsListUtils.ts | 6 +++--- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index e747ea4afa33..f33b551968d4 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6633,6 +6633,7 @@ const CONST = { LHN_WORKSPACE_CHAT_TOOLTIP: 'workspaceChatLHNTooltip', GLOBAL_CREATE_TOOLTIP: 'globalCreateTooltip', SCAN_TEST_TOOLTIP: 'scanTestTooltip', + SCAN_TEST_TOOLTIP_MANAGER: 'scanTestTooltipManager', }, SMART_BANNER_HEIGHT: 152, TRAVEL: { diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index b266aabeb950..f2cf007ccb58 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -13,6 +13,7 @@ const { LHN_WORKSPACE_CHAT_TOOLTIP, GLOBAL_CREATE_TOOLTIP, SCAN_TEST_TOOLTIP, + SCAN_TEST_TOOLTIP_MANAGER, } = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES; type ProductTrainingTooltipName = ValueOf; @@ -132,6 +133,17 @@ const TOOLTIPS: Record = { shouldShow: () => true, shouldRenderActionButtons: true, }, + [SCAN_TEST_TOOLTIP_MANAGER]: { + content: [ + {text: 'productTrainingTooltip.scanTestTooltip.part3', isBold: false}, + {text: 'productTrainingTooltip.scanTestTooltip.part4', isBold: true}, + {text: 'productTrainingTooltip.scanTestTooltip.part5', isBold: false}, + ], + onHideTooltip: () => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER), + name: SCAN_TEST_TOOLTIP_MANAGER, + priority: 900, + shouldShow: () => true, + }, }; export default TOOLTIPS; diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 397f778a6049..638ff0c3b476 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -100,7 +100,13 @@ function ProductTrainingContextProvider({children}: ChildrenProps) { } // We need to make an exception for the QAB tooltip because it is shown in a modal, otherwise it would be hidden if a modal is visible - if (tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.QUICK_ACTION_BUTTON && tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP && isModalVisible) { + if ( + tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.QUICK_ACTION_BUTTON && + tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP && + tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP_MANAGER && + isModalVisible + ) { + console.log('here'); return false; } @@ -234,6 +240,7 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou ]); const shouldShowProductTrainingTooltip = useMemo(() => { + console.log('shouldShowProductTrainingTooltip useProductTrainingContext', tooltipName, shouldRenderTooltip(tooltipName)); return shouldShow && shouldRenderTooltip(tooltipName); }, [shouldRenderTooltip, tooltipName, shouldShow]); const hideProductTrainingTooltip = useCallback(() => { diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx index 15512037c581..5adcf2c32aa4 100644 --- a/src/components/SelectionList/InviteMemberListItem.tsx +++ b/src/components/SelectionList/InviteMemberListItem.tsx @@ -50,9 +50,13 @@ function InviteMemberListItem({ const {translate} = useLocalize(); const [betas] = useOnyx(ONYXKEYS.BETAS); const {renderProductTrainingTooltip, shouldShowProductTrainingTooltip} = useProductTrainingContext( - CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, + CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP_MANAGER, !getIsUserSubmittedExpenseOrScannedReceipt() && Permissions.canUseManagerMcTest(betas) && item.login === CONST.EMAIL.MANAGER_MCTEST, ); + // console.log('!getIsUserSubmittedExpenseOrScannedReceipt()', !getIsUserSubmittedExpenseOrScannedReceipt()); + // console.log('item.login', item.login); + // console.log('Permissions.canUseManagerMcTest(betas)', Permissions.canUseManagerMcTest(betas)); + // console.log('InviteMemberListItem shouldShowProductTrainingTooltip', shouldShowProductTrainingTooltip); const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 689fad0b3e38..1a624b56a4a6 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1455,8 +1455,8 @@ function getValidReports(reports: OptionList['reports'], config: GetValidReports /** * Whether user submitted already an expense or scanned receipt */ -function getIsUserSubmittedExpenseOrScannedReceipt(betas: OnyxEntry): boolean { - return !!nvpDismissedProductTraining?.[CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP] && Permissions.canUseManagerMcTest(betas); +function getIsUserSubmittedExpenseOrScannedReceipt(): boolean { + return !!nvpDismissedProductTraining?.[CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP]; } /** @@ -1482,7 +1482,7 @@ function getValidOptions( [CONST.EMAIL.NOTIFICATIONS]: true, ...excludeLogins, // Exclude Manager McTest if user submitted expense or scanned receipt and when selection is made from Create or Submit flow - [CONST.EMAIL.MANAGER_MCTEST]: !(!getIsUserSubmittedExpenseOrScannedReceipt(config.betas) && canShowManagerMcTest), + [CONST.EMAIL.MANAGER_MCTEST]: !(!getIsUserSubmittedExpenseOrScannedReceipt() && canShowManagerMcTest && Permissions.canUseManagerMcTest(config.betas)), }; // If we're including selected options from the search results, we only want to exclude them if the search input is empty // This is because on certain pages, we show the selected options at the top when the search input is empty From f06c76c42bfbfe97a1c746c182fb5566f0753af1 Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Tue, 11 Feb 2025 18:53:15 +0100 Subject: [PATCH 04/10] removed log --- src/components/ProductTrainingContext/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 638ff0c3b476..4c6e9461c3ba 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -106,7 +106,6 @@ function ProductTrainingContextProvider({children}: ChildrenProps) { tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP_MANAGER && isModalVisible ) { - console.log('here'); return false; } From 85cfe40e68a1b4691eb0bf37433de906f2b7ab46 Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Tue, 11 Feb 2025 18:53:27 +0100 Subject: [PATCH 05/10] fix styling --- .../SelectionList/InviteMemberListItem.tsx | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx index 5adcf2c32aa4..f1d11f755f5a 100644 --- a/src/components/SelectionList/InviteMemberListItem.tsx +++ b/src/components/SelectionList/InviteMemberListItem.tsx @@ -99,35 +99,35 @@ function InviteMemberListItem({ shouldDisplayRBR={!shouldShowCheckBox} > {(hovered?: boolean) => ( - <> - {!!item.icons && - (item.shouldShowSubscript ? ( - - ) : ( - - ))} - + + + {!!item.icons && + (item.shouldShowSubscript ? ( + + ) : ( + + ))} ({ /> )} - - {!!item.rightElement && item.rightElement} - {!!shouldShowCheckBox && ( - - - - )} - + {!!item.rightElement && item.rightElement} + {!!shouldShowCheckBox && ( + + + + )} + + )} ); From d406483c58480e98240544b911585f1cdb1526e6 Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Wed, 12 Feb 2025 15:26:30 +0100 Subject: [PATCH 06/10] add last tooltip for manager mctest --- src/CONST.ts | 1 + .../MoneyRequestConfirmationList.tsx | 40 +++++++++++++++++-- .../ProductTrainingContext/TOOLTIPS.ts | 20 ++++++---- .../ProductTrainingContext/index.tsx | 2 +- .../SelectionList/InviteMemberListItem.tsx | 5 +-- .../TabSelector/TabSelectorItem.tsx | 1 + src/languages/en.ts | 2 +- src/types/onyx/DismissedProductTraining.ts | 12 ++++++ 8 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index f33b551968d4..3a519b97d251 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6634,6 +6634,7 @@ const CONST = { GLOBAL_CREATE_TOOLTIP: 'globalCreateTooltip', SCAN_TEST_TOOLTIP: 'scanTestTooltip', SCAN_TEST_TOOLTIP_MANAGER: 'scanTestTooltipManager', + SCAN_TEST_CONFIRMATION: 'scanTestConfirmation', }, SMART_BANNER_HEIGHT: 152, TRAVEL: { diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 5679f59f5f21..d26cad764542 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -32,6 +32,7 @@ import Log from '@libs/Log'; import {validateAmount} from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getIOUConfirmationOptionsFromPayeePersonalDetail, hasEnabledOptions} from '@libs/OptionsListUtils'; +import Permissions from '@libs/Permissions'; import {getDistanceRateCustomUnitRate, getTagLists, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import {isDraftReport, isOptimisticPersonalDetail} from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; @@ -66,11 +67,13 @@ import FormHelpMessage from './FormHelpMessage'; import MoneyRequestAmountInput from './MoneyRequestAmountInput'; import MoneyRequestConfirmationListFooter from './MoneyRequestConfirmationListFooter'; import {PressableWithFeedback} from './Pressable'; +import {useProductTrainingContext} from './ProductTrainingContext'; import SelectionList from './SelectionList'; import type {SectionListDataType} from './SelectionList/types'; import UserListItem from './SelectionList/UserListItem'; import SettlementButton from './SettlementButton'; import Text from './Text'; +import EducationalTooltip from './Tooltip/EducationalTooltip'; type MoneyRequestConfirmationListProps = { /** Callback to inform parent modal of success */ @@ -216,6 +219,11 @@ function MoneyRequestConfirmationList({ const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`); const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); + const [betas] = useOnyx(ONYXKEYS.BETAS); + const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip} = useProductTrainingContext( + CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_CONFIRMATION, + Permissions.canUseManagerMcTest(betas) && selectedParticipantsProp.some((participant) => participant.login === CONST.EMAIL.MANAGER_MCTEST), + ); const policy = policyReal ?? policyDraft; const policyCategories = policyCategoriesReal ?? policyCategoriesDraft; @@ -977,11 +985,37 @@ function MoneyRequestConfirmationList({ message={errorMessage} /> )} - - {button} + + {button} + ); - }, [isReadOnly, iouType, confirm, isConfirmed, bankAccountRoute, iouCurrencyCode, policyID, splitOrRequestOptions, styles.ph1, styles.mb2, errorMessage]); + }, [ + isReadOnly, + iouType, + confirm, + bankAccountRoute, + iouCurrencyCode, + policyID, + isConfirmed, + splitOrRequestOptions, + errorMessage, + styles.ph1, + styles.mb2, + styles.productTrainingTooltipWrapper, + shouldShowProductTrainingTooltip, + renderProductTrainingTooltip, + ]); const listFooterContent = ( ; @@ -120,16 +121,10 @@ const TOOLTIPS: Record = { content: [ {text: 'productTrainingTooltip.scanTestTooltip.part1', isBold: false}, {text: 'productTrainingTooltip.scanTestTooltip.part2', isBold: true}, - // {text: 'productTrainingTooltip.scanTestTooltip.part3', isBold: false}, - // {text: 'productTrainingTooltip.scanTestTooltip.part4', isBold: true}, - // {text: 'productTrainingTooltip.scanTestTooltip.part5', isBold: false}, - // {text: 'productTrainingTooltip.scanTestTooltip.part6', isBold: false}, - // {text: 'productTrainingTooltip.scanTestTooltip.part7', isBold: true}, - // {text: 'productTrainingTooltip.scanTestTooltip.part8', isBold: false}, ], onHideTooltip: () => dismissProductTraining(SCAN_TEST_TOOLTIP), name: SCAN_TEST_TOOLTIP, - priority: 900, + priority: 800, shouldShow: () => true, shouldRenderActionButtons: true, }, @@ -141,6 +136,17 @@ const TOOLTIPS: Record = { ], onHideTooltip: () => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER), name: SCAN_TEST_TOOLTIP_MANAGER, + priority: 1000, + shouldShow: () => true, + }, + [SCAN_TEST_CONFIRMATION]: { + content: [ + {text: 'productTrainingTooltip.scanTestTooltip.part6', isBold: false}, + {text: 'productTrainingTooltip.scanTestTooltip.part7', isBold: true}, + {text: 'productTrainingTooltip.scanTestTooltip.part8', isBold: false}, + ], + onHideTooltip: () => dismissProductTraining(SCAN_TEST_CONFIRMATION), + name: SCAN_TEST_CONFIRMATION, priority: 900, shouldShow: () => true, }, diff --git a/src/components/ProductTrainingContext/index.tsx b/src/components/ProductTrainingContext/index.tsx index 4c6e9461c3ba..423e93d12047 100644 --- a/src/components/ProductTrainingContext/index.tsx +++ b/src/components/ProductTrainingContext/index.tsx @@ -104,6 +104,7 @@ function ProductTrainingContextProvider({children}: ChildrenProps) { tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.QUICK_ACTION_BUTTON && tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP && tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP_MANAGER && + tooltipName !== CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_CONFIRMATION && isModalVisible ) { return false; @@ -239,7 +240,6 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou ]); const shouldShowProductTrainingTooltip = useMemo(() => { - console.log('shouldShowProductTrainingTooltip useProductTrainingContext', tooltipName, shouldRenderTooltip(tooltipName)); return shouldShow && shouldRenderTooltip(tooltipName); }, [shouldRenderTooltip, tooltipName, shouldShow]); const hideProductTrainingTooltip = useCallback(() => { diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx index f1d11f755f5a..016cbfeeec97 100644 --- a/src/components/SelectionList/InviteMemberListItem.tsx +++ b/src/components/SelectionList/InviteMemberListItem.tsx @@ -53,10 +53,6 @@ function InviteMemberListItem({ CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP_MANAGER, !getIsUserSubmittedExpenseOrScannedReceipt() && Permissions.canUseManagerMcTest(betas) && item.login === CONST.EMAIL.MANAGER_MCTEST, ); - // console.log('!getIsUserSubmittedExpenseOrScannedReceipt()', !getIsUserSubmittedExpenseOrScannedReceipt()); - // console.log('item.login', item.login); - // console.log('Permissions.canUseManagerMcTest(betas)', Permissions.canUseManagerMcTest(betas)); - // console.log('InviteMemberListItem shouldShowProductTrainingTooltip', shouldShowProductTrainingTooltip); const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; @@ -106,6 +102,7 @@ function InviteMemberListItem({ horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }} + shouldHideOnNavigate wrapperStyle={styles.productTrainingTooltipWrapper} > diff --git a/src/components/TabSelector/TabSelectorItem.tsx b/src/components/TabSelector/TabSelectorItem.tsx index 5dfc0f4796f3..b7201a35553e 100644 --- a/src/components/TabSelector/TabSelectorItem.tsx +++ b/src/components/TabSelector/TabSelectorItem.tsx @@ -89,6 +89,7 @@ function TabSelectorItem({ Date: Thu, 13 Feb 2025 10:22:26 +0100 Subject: [PATCH 07/10] resolve comment --- src/components/MoneyRequestConfirmationList.tsx | 4 ++-- src/components/SelectionList/InviteMemberListItem.tsx | 4 ++-- src/libs/OptionsListUtils.ts | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index d26cad764542..e665b14fdf7b 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -31,7 +31,7 @@ import {calculateAmount, insertTagIntoTransactionTagsString, isMovingTransaction import Log from '@libs/Log'; import {validateAmount} from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {getIOUConfirmationOptionsFromPayeePersonalDetail, hasEnabledOptions} from '@libs/OptionsListUtils'; +import {getIOUConfirmationOptionsFromPayeePersonalDetail, hasEnabledOptions, isSelectedManagerMcTest} from '@libs/OptionsListUtils'; import Permissions from '@libs/Permissions'; import {getDistanceRateCustomUnitRate, getTagLists, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import {isDraftReport, isOptimisticPersonalDetail} from '@libs/ReportUtils'; @@ -222,7 +222,7 @@ function MoneyRequestConfirmationList({ const [betas] = useOnyx(ONYXKEYS.BETAS); const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip} = useProductTrainingContext( CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_CONFIRMATION, - Permissions.canUseManagerMcTest(betas) && selectedParticipantsProp.some((participant) => participant.login === CONST.EMAIL.MANAGER_MCTEST), + Permissions.canUseManagerMcTest(betas) && selectedParticipantsProp.some((participant) => isSelectedManagerMcTest(participant.login)), ); const policy = policyReal ?? policyDraft; diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx index 016cbfeeec97..d399ff8ade65 100644 --- a/src/components/SelectionList/InviteMemberListItem.tsx +++ b/src/components/SelectionList/InviteMemberListItem.tsx @@ -15,7 +15,7 @@ import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getIsUserSubmittedExpenseOrScannedReceipt} from '@libs/OptionsListUtils'; +import {getIsUserSubmittedExpenseOrScannedReceipt, isSelectedManagerMcTest} from '@libs/OptionsListUtils'; import Permissions from '@libs/Permissions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -51,7 +51,7 @@ function InviteMemberListItem({ const [betas] = useOnyx(ONYXKEYS.BETAS); const {renderProductTrainingTooltip, shouldShowProductTrainingTooltip} = useProductTrainingContext( CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP_MANAGER, - !getIsUserSubmittedExpenseOrScannedReceipt() && Permissions.canUseManagerMcTest(betas) && item.login === CONST.EMAIL.MANAGER_MCTEST, + !getIsUserSubmittedExpenseOrScannedReceipt() && Permissions.canUseManagerMcTest(betas) && isSelectedManagerMcTest(item.login), ); const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 1a624b56a4a6..11ae89ea4aa0 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -2144,6 +2144,10 @@ function shouldUseBoldText(report: OptionData): boolean { return report.isUnread === true && notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE && !isHiddenForCurrentUser(notificationPreference); } +function isSelectedManagerMcTest(email: string | null | undefined): boolean { + return email === CONST.EMAIL.MANAGER_MCTEST; +} + export { getAvatarsForAccountIDs, isCurrentUser, @@ -2198,6 +2202,7 @@ export { filterSelfDMChat, filterReports, getIsUserSubmittedExpenseOrScannedReceipt, + isSelectedManagerMcTest, }; export type {Section, SectionBase, MemberForList, Options, OptionList, SearchOption, PayeePersonalDetails, Option, OptionTree, ReportAndPersonalDetailOptions, GetUserToInviteConfig}; From ff9e9d9da25d19cb325b1f60cfb58048f47f256e Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Thu, 13 Feb 2025 16:25:09 +0100 Subject: [PATCH 08/10] fix navigation issues --- src/components/MoneyRequestConfirmationList.tsx | 2 +- src/components/ProductTrainingContext/TOOLTIPS.ts | 4 ++-- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 79be59fa6c3a..8ca71206796f 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -805,7 +805,7 @@ function MoneyRequestConfirmationList({ return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(CONST.IOU.TYPE.CREATE, transactionID, transaction.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(CONST.IOU.TYPE.CREATE, transactionID, transaction.reportID), {forceReplace: true}); }; /** diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index 6fa9056e0f1e..90e91166fba7 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -124,7 +124,7 @@ const TOOLTIPS: Record = { ], onHideTooltip: () => dismissProductTraining(SCAN_TEST_TOOLTIP), name: SCAN_TEST_TOOLTIP, - priority: 800, + priority: 900, shouldShow: () => true, shouldRenderActionButtons: true, }, @@ -147,7 +147,7 @@ const TOOLTIPS: Record = { ], onHideTooltip: () => dismissProductTraining(SCAN_TEST_CONFIRMATION), name: SCAN_TEST_CONFIRMATION, - priority: 900, + priority: 1100, shouldShow: () => true, }, }; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 8d1ef11ef23f..f35c7d058d8a 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -166,7 +166,7 @@ function IOURequestStepParticipants({ Navigation.navigate(route); }); } else { - Navigation.navigate(route); + Navigation.navigate(route, {forceReplace: true}); } }, [isAndroidNative, isMobileSafari], From b1a180eabe815024d137d76a3e503d0cbc6a3735 Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Thu, 13 Feb 2025 17:53:37 +0100 Subject: [PATCH 09/10] fix test --- tests/ui/components/TabSelectorItem.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/ui/components/TabSelectorItem.tsx b/tests/ui/components/TabSelectorItem.tsx index 5fb0274a38c4..4e301baa0b11 100644 --- a/tests/ui/components/TabSelectorItem.tsx +++ b/tests/ui/components/TabSelectorItem.tsx @@ -5,11 +5,18 @@ import Tooltip from '@components/Tooltip'; // Mock the Tooltip component since it uses portals which aren't supported in RNTL jest.mock('@components/Tooltip'); +jest.mock('@libs/Fullstory', () => ({ + default: { + consentAndIdentify: jest.fn(), + }, + parseFSAttributes: jest.fn(), +})); describe('TabSelectorItem Component', () => { const title = 'Test Tab'; beforeEach(() => { + // wrapOnyxWithWaitForBatchedUpdates(Onyx); jest.clearAllMocks(); }); From e27157d4268f8a163c36def8c5759e7c060f8fec Mon Sep 17 00:00:00 2001 From: kubabutkiewicz Date: Fri, 14 Feb 2025 12:32:01 +0100 Subject: [PATCH 10/10] resolve comments --- src/libs/OptionsListUtils.ts | 3 +++ tests/ui/components/TabSelectorItem.tsx | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index f424e725289f..4067bfadaf0f 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -2145,6 +2145,9 @@ function shouldUseBoldText(report: OptionData): boolean { return report.isUnread === true && notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE && !isHiddenForCurrentUser(notificationPreference); } +/** + * Helper method to check if participant email is Manager McTest + */ function isSelectedManagerMcTest(email: string | null | undefined): boolean { return email === CONST.EMAIL.MANAGER_MCTEST; } diff --git a/tests/ui/components/TabSelectorItem.tsx b/tests/ui/components/TabSelectorItem.tsx index 4e301baa0b11..0e595a610319 100644 --- a/tests/ui/components/TabSelectorItem.tsx +++ b/tests/ui/components/TabSelectorItem.tsx @@ -16,7 +16,6 @@ describe('TabSelectorItem Component', () => { const title = 'Test Tab'; beforeEach(() => { - // wrapOnyxWithWaitForBatchedUpdates(Onyx); jest.clearAllMocks(); });