From 890eba70df851f1613896c3df84e137538dead09 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 10:46:29 +0700 Subject: [PATCH 001/108] Feature: add loading indicator when ReconnectApp is running --- src/pages/home/ReportScreen.tsx | 3 +++ .../sidebar/SidebarScreen/BaseSidebarScreen.tsx | 4 ++++ src/styles/index.ts | 13 +++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 4eaf3633af38..be4cf15787a5 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -13,6 +13,7 @@ import DragAndDropProvider from '@components/DragAndDrop/Provider'; import MoneyReportHeader from '@components/MoneyReportHeader'; import MoneyRequestHeader from '@components/MoneyRequestHeader'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import ProgressBar from '@components/ProgressBar'; import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; import ScreenWrapper from '@components/ScreenWrapper'; import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; @@ -128,6 +129,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); const [workspaceTooltip] = useOnyx(ONYXKEYS.NVP_WORKSPACE_TOOLTIP); const wasLoadingApp = usePrevious(isLoadingApp); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); const finishedLoadingApp = wasLoadingApp && !isLoadingApp; const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(parentReportAction); const prevIsDeletedParentAction = usePrevious(isDeletedParentAction); @@ -756,6 +758,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro needsOffscreenAlphaCompositing > {headerView} + {shouldUseNarrowLayout && } {ReportUtils.isTaskReport(report) && shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && ( diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index edc8dfb3cb3a..4626429fb53c 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -1,6 +1,7 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import ProgressBar from '@components/ProgressBar'; import ScreenWrapper from '@components/ScreenWrapper'; import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; import useLocalize from '@hooks/useLocalize'; @@ -29,6 +30,8 @@ function BaseSidebarScreen() { const {translate} = useLocalize(); const [activeWorkspace] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID ?? -1}`); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {initialValue: true}); + useEffect(() => { Performance.markStart(CONST.TIMING.SIDEBAR_LOADED); Timing.start(CONST.TIMING.SIDEBAR_LOADED); @@ -57,6 +60,7 @@ function BaseSidebarScreen() { breadcrumbLabel={translate('common.inbox')} activeWorkspaceID={activeWorkspaceID} /> + top: 80, left: 12, }, + progressBarWrapper: { + height: 2, + width: '100%', + backgroundColor: '#1A3D32', + borderRadius: 5, + overflow: 'hidden', + }, + progressBar: { + height: '100%', + backgroundColor: '#03D47C', + borderRadius: 5, + width: '100%', + }, } satisfies Styles); type ThemeStyles = ReturnType; From 7a77276bd2414483144892228d98584faac03b9d Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 10:55:23 +0700 Subject: [PATCH 002/108] fix ts check --- src/components/ProgressBar.tsx | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/components/ProgressBar.tsx diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx new file mode 100644 index 000000000000..d37d34887c03 --- /dev/null +++ b/src/components/ProgressBar.tsx @@ -0,0 +1,78 @@ +import React, {useEffect} from 'react'; +import Animated, {cancelAnimation, Easing, runOnJS, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated'; +import useThemeStyles from '@hooks/useThemeStyles'; + +function ProgressBar({shouldShow}: {shouldShow: boolean}) { + const left = useSharedValue(0); + const width = useSharedValue(0); + const opacity = useSharedValue(0); + const isVisible = useSharedValue(false); + const styles = useThemeStyles(); + + useEffect(() => { + if (shouldShow) { + // eslint-disable-next-line react-compiler/react-compiler + isVisible.value = true; + left.value = 0; + width.value = 0; + opacity.value = withTiming(1, {duration: 300}); + left.value = withDelay( + 300, // 0.3s delay + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, + ), + ); + + width.value = withDelay( + 300, // 0.3s delay + withRepeat( + withSequence( + withTiming(0, {duration: 0}), + withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + ), + -1, + false, + ), + ); + } else if (isVisible.value) { + opacity.value = withTiming(0, {duration: 300}, () => { + runOnJS(() => { + isVisible.value = false; + cancelAnimation(left); + cancelAnimation(width); + }); + }); + } + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [shouldShow]); + + const animatedIndicatorStyle = useAnimatedStyle(() => { + return { + left: `${left.value}%`, + width: `${width.value}%`, + }; + }); + + const animatedContainerStyle = useAnimatedStyle(() => { + return { + opacity: opacity.value, + }; + }); + + return isVisible.value ? ( + + + + ) : null; +} + +ProgressBar.displayName = 'ProgressBar'; + +export default ProgressBar; From 0265bc6133f6e93382e98105646ba484831fd173 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 16:53:46 +0700 Subject: [PATCH 003/108] fix style of progress bar --- src/styles/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 8a742934739c..825f17701740 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5202,13 +5202,13 @@ const styles = (theme: ThemeColors) => progressBarWrapper: { height: 2, width: '100%', - backgroundColor: '#1A3D32', + backgroundColor: theme.buttonDefaultBG, borderRadius: 5, overflow: 'hidden', }, progressBar: { height: '100%', - backgroundColor: '#03D47C', + backgroundColor: theme.success, borderRadius: 5, width: '100%', }, From af9b5b8a2f9406d1bf39b01727b3efeb65036bfe Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 9 Sep 2024 17:26:43 +0700 Subject: [PATCH 004/108] fix style progress bar --- src/styles/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 825f17701740..b086d4cfd9b3 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5202,7 +5202,7 @@ const styles = (theme: ThemeColors) => progressBarWrapper: { height: 2, width: '100%', - backgroundColor: theme.buttonDefaultBG, + backgroundColor: theme.border, borderRadius: 5, overflow: 'hidden', }, From c177829d889eb4a1998d524d17c4b453ff82d5bf Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 10 Sep 2024 22:05:34 +0700 Subject: [PATCH 005/108] add new const and fix style progress bar --- src/CONST.ts | 3 +++ src/components/ProgressBar.tsx | 17 +++++++++-------- src/styles/index.ts | 1 - 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d0695b1e285f..a6578118e7c8 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -180,6 +180,9 @@ const CONST = { ANIMATED_HIGHLIGHT_END_DURATION: 2000, ANIMATED_TRANSITION: 300, ANIMATED_TRANSITION_FROM_VALUE: 100, + ANIMATED_PROGRESS_BAR_DELAY: 300, + ANIMATED_PROGRESS_BAR_OPACITY_DURATION: 300, + ANIMATED_PROGRESS_BAR_DURATION: 750, ANIMATION_IN_TIMING: 100, ANIMATION_DIRECTION: { IN: 'in', diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index d37d34887c03..63d3d1142d30 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -1,6 +1,7 @@ import React, {useEffect} from 'react'; import Animated, {cancelAnimation, Easing, runOnJS, useAnimatedStyle, useSharedValue, withDelay, withRepeat, withSequence, withTiming} from 'react-native-reanimated'; import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; function ProgressBar({shouldShow}: {shouldShow: boolean}) { const left = useSharedValue(0); @@ -15,14 +16,14 @@ function ProgressBar({shouldShow}: {shouldShow: boolean}) { isVisible.value = true; left.value = 0; width.value = 0; - opacity.value = withTiming(1, {duration: 300}); + opacity.value = withTiming(1, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}); left.value = withDelay( - 300, // 0.3s delay + CONST.ANIMATED_PROGRESS_BAR_DELAY, withRepeat( withSequence( withTiming(0, {duration: 0}), - withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), ), -1, false, @@ -30,19 +31,19 @@ function ProgressBar({shouldShow}: {shouldShow: boolean}) { ); width.value = withDelay( - 300, // 0.3s delay + CONST.ANIMATED_PROGRESS_BAR_DELAY, withRepeat( withSequence( withTiming(0, {duration: 0}), - withTiming(100, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), - withTiming(0, {duration: 750, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(100, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), + withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_DURATION, easing: Easing.bezier(0.65, 0, 0.35, 1)}), ), -1, false, ), ); } else if (isVisible.value) { - opacity.value = withTiming(0, {duration: 300}, () => { + opacity.value = withTiming(0, {duration: CONST.ANIMATED_PROGRESS_BAR_OPACITY_DURATION}, () => { runOnJS(() => { isVisible.value = false; cancelAnimation(left); diff --git a/src/styles/index.ts b/src/styles/index.ts index b086d4cfd9b3..9221f480ab8e 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5209,7 +5209,6 @@ const styles = (theme: ThemeColors) => progressBar: { height: '100%', backgroundColor: theme.success, - borderRadius: 5, width: '100%', }, } satisfies Styles); From 32117b20050e7bce79e80d93ff9cb0cf0a87d153 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 30 Sep 2024 16:02:17 +0700 Subject: [PATCH 006/108] fix style progress bar --- src/components/ProgressBar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index 63d3d1142d30..885fe337c002 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -67,11 +67,11 @@ function ProgressBar({shouldShow}: {shouldShow: boolean}) { }; }); - return isVisible.value ? ( + return ( - + {isVisible.value ? : null} - ) : null; + ); } ProgressBar.displayName = 'ProgressBar'; From 3620918bf11759e18a7d23ae49faa4eacf1a211f Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 19:16:12 +0530 Subject: [PATCH 007/108] add selftourviewed command --- src/libs/API/types.ts | 2 ++ src/libs/actions/Welcome/index.ts | 17 ++++++++++++++++- src/types/onyx/Onboarding.ts | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 929e709559b7..0bfd77c4dcd0 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -429,6 +429,7 @@ const WRITE_COMMANDS = { SET_CARD_EXPORT_ACCOUNT: 'SetCardExportAccount', SET_MISSING_PERSONAL_DETAILS_AND_SHIP_EXPENSIFY_CARD: 'SetMissingPersonalDetailsAndShipExpensifyCard', SET_INVOICING_TRANSFER_BANK_ACCOUNT: 'SetInvoicingTransferBankAccount', + SELF_TOUR_VIEWED: 'SelfTourViewed', } as const; type WriteCommand = ValueOf; @@ -868,6 +869,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_XERO_SYNC_REIMBURSEMENT_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams; [WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT]: Parameters.SetInvoicingTransferBankAccountParams; + [WRITE_COMMANDS.SELF_TOUR_VIEWED]: null; }; const READ_COMMANDS = { diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index d504c5550331..19a570ab610f 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -2,7 +2,7 @@ import {NativeModules} from 'react-native'; import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; -import {SIDE_EFFECT_REQUEST_COMMANDS} from '@libs/API/types'; +import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import Log from '@libs/Log'; import type {OnboardingCompanySizeType, OnboardingPurposeType} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -183,6 +183,20 @@ function resetAllChecks() { OnboardingFlow.clearInitialPath(); } +function setSelfTourViewed() { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_ONBOARDING, + value: { + selfTourViewed: true, + }, + }, + ]; + + API.write(WRITE_COMMANDS.SELF_TOUR_VIEWED, null, {optimisticData}); +} + export { onServerDataReady, isOnboardingFlowCompleted, @@ -195,4 +209,5 @@ export { completeHybridAppOnboarding, setOnboardingErrorMessage, setOnboardingCompanySize, + setSelfTourViewed, }; diff --git a/src/types/onyx/Onboarding.ts b/src/types/onyx/Onboarding.ts index 4b6a52f25cb4..2cf8eccba1c2 100644 --- a/src/types/onyx/Onboarding.ts +++ b/src/types/onyx/Onboarding.ts @@ -11,6 +11,9 @@ type Onboarding = { /** A string that informs which qualifier the user selected during sign up */ signupQualifier: ValueOf; + + /** A Boolean that tells whether the user has seen navattic tour */ + selfTourViewed?: boolean; }; export default Onboarding; From 31ff69bb79ad057aafd7825c5598bb156c7f49b0 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:00:06 +0530 Subject: [PATCH 008/108] add util to get tour url --- src/CONST.ts | 6 ++++-- src/libs/TourUtils.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/libs/TourUtils.ts diff --git a/src/CONST.ts b/src/CONST.ts index 84003710938a..ff9f960c756e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -748,8 +748,10 @@ const CONST = { // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', NAVATTIC: { - ADMIN_TOUR: 'https://expensify.navattic.com/kh204a7', - EMPLOYEE_TOUR: 'https://expensify.navattic.com/35609gb', + ADMIN_TOUR_PRODUCTION: 'https://expensify.navattic.com/kh204a7', + ADMIN_TOUR_STAGING: 'https://expensify.navattic.com/stagingAdmin', + EMPLOYEE_TOUR_PRODUCTION: 'https://expensify.navattic.com/35609gb', + EMPLOYEE_TOUR_STAGING: 'https://expensify.navattic.com/stagingEmployee', }, OLDDOT_URLS: { diff --git a/src/libs/TourUtils.ts b/src/libs/TourUtils.ts new file mode 100644 index 000000000000..a88ee47cc563 --- /dev/null +++ b/src/libs/TourUtils.ts @@ -0,0 +1,14 @@ +import type {ValueOf} from 'type-fest'; +import CONST from '@src/CONST'; +import type {OnboardingPurposeType} from '@src/CONST'; + +function getNavatticURL(environment: ValueOf, introSelected?: OnboardingPurposeType) { + const adminTourURL = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.NAVATTIC.ADMIN_TOUR_PRODUCTION : CONST.NAVATTIC.ADMIN_TOUR_STAGING; + const employeeTourURL = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.NAVATTIC.EMPLOYEE_TOUR_PRODUCTION : CONST.NAVATTIC.EMPLOYEE_TOUR_STAGING; + return introSelected === CONST.SELECTABLE_ONBOARDING_CHOICES.MANAGE_TEAM ? adminTourURL : employeeTourURL; +} + +export { + // eslint-disable-next-line import/prefer-default-export + getNavatticURL, +}; From 528395398d39a00243c860a4d1553559de8a5c54 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:02:57 +0530 Subject: [PATCH 009/108] use util to get url --- src/pages/Search/EmptySearchView.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index c148f1b56d62..8bcb3ee7b46d 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -10,12 +10,14 @@ import MenuItem from '@components/MenuItem'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import * as ReportUtils from '@libs/ReportUtils'; +import {getNavatticURL} from '@libs/TourUtils'; import * as TripsResevationUtils from '@libs/TripReservationUtils'; import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; @@ -93,7 +95,8 @@ function EmptySearchView({type}: EmptySearchViewProps) { }, [styles, translate, ctaErrorMessage]); const [onboardingPurpose] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: (introSelected) => introSelected?.choice}); - const navatticLink = onboardingPurpose === CONST.SELECTABLE_ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.NAVATTIC.ADMIN_TOUR : CONST.NAVATTIC.EMPLOYEE_TOUR; + const {environment} = useEnvironment(); + const navatticURL = getNavatticURL(environment, onboardingPurpose); const content = useMemo(() => { switch (type) { @@ -120,7 +123,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { title: translate('search.searchResults.emptyExpenseResults.title'), subtitle: translate('search.searchResults.emptyExpenseResults.subtitle'), buttons: [ - {buttonText: translate('emptySearchView.takeATour'), buttonAction: () => Link.openExternalLink(navatticLink)}, + {buttonText: translate('emptySearchView.takeATour'), buttonAction: () => Link.openExternalLink(navatticURL)}, { buttonText: translate('iou.createExpense'), buttonAction: () => interceptAnonymousUser(() => IOU.startMoneyRequest(CONST.IOU.TYPE.CREATE, ReportUtils.generateReportID())), @@ -140,7 +143,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { headerContentStyles: styles.emptyStateFolderWebStyles, }; } - }, [type, StyleUtils, translate, theme, styles, subtitleComponent, ctaErrorMessage, navatticLink]); + }, [type, StyleUtils, translate, theme, styles, subtitleComponent, ctaErrorMessage, navatticURL]); return ( Date: Sun, 20 Oct 2024 20:27:57 +0530 Subject: [PATCH 010/108] fix staging url --- src/CONST.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ff9f960c756e..01466d8baf86 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -749,9 +749,9 @@ const CONST = { DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', NAVATTIC: { ADMIN_TOUR_PRODUCTION: 'https://expensify.navattic.com/kh204a7', - ADMIN_TOUR_STAGING: 'https://expensify.navattic.com/stagingAdmin', + ADMIN_TOUR_STAGING: 'https://expensify.navattic.com/3i300k18', EMPLOYEE_TOUR_PRODUCTION: 'https://expensify.navattic.com/35609gb', - EMPLOYEE_TOUR_STAGING: 'https://expensify.navattic.com/stagingEmployee', + EMPLOYEE_TOUR_STAGING: 'https://expensify.navattic.com/cf15002s', }, OLDDOT_URLS: { From c1081d02784a0020a5dde6b865f582afcbd98b84 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:28:05 +0530 Subject: [PATCH 011/108] add tour icon --- assets/images/tour.svg | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 assets/images/tour.svg diff --git a/assets/images/tour.svg b/assets/images/tour.svg new file mode 100644 index 000000000000..926cfcd8de69 --- /dev/null +++ b/assets/images/tour.svg @@ -0,0 +1,6 @@ + + + + + + From cd6558b6a095cd842500ad22f7466e9219acc7f8 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:28:12 +0530 Subject: [PATCH 012/108] add tour icon --- src/components/Icon/Expensicons.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index cd9c97105ff0..621bc5e43bea 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -182,6 +182,7 @@ import Task from '@assets/images/task.svg'; import Thread from '@assets/images/thread.svg'; import ThreeDots from '@assets/images/three-dots.svg'; import ThumbsUp from '@assets/images/thumbs-up.svg'; +import Tour from '@assets/images/tour.svg'; import Transfer from '@assets/images/transfer.svg'; import Trashcan from '@assets/images/trashcan.svg'; import Unlock from '@assets/images/unlock.svg'; @@ -404,4 +405,5 @@ export { Bookmark, Star, QBDSquare, + Tour, }; From 38820777379e9e84eb42231e4e88bdbdc1f2364d Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:29:12 +0530 Subject: [PATCH 013/108] add lang --- src/languages/en.ts | 4 ++++ src/languages/es.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5798f7fe48e9..e68e89296222 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5132,6 +5132,10 @@ const translations = { emptySearchView: { takeATour: 'Take a tour', }, + tour: { + takeATwoMinuteTour: 'Take a 2-minute tour', + exploreExpensify: 'Explore everything Expensify has to offer', + }, }; export default translations satisfies TranslationDeepObject; diff --git a/src/languages/es.ts b/src/languages/es.ts index 84c03d5d9bf3..9a24b1bf9386 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5649,6 +5649,10 @@ const translations = { emptySearchView: { takeATour: 'Haz un tour', }, + tour: { + takeATwoMinuteTour: 'Haz un tour de 2 minutos', + exploreExpensify: 'Explora todo lo que Expensify tiene para ofrecer', + }, }; export default translations satisfies TranslationDeepObject; From 469c0520a80fd3f6a97961c0b99b86f2591be2ab Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:29:25 +0530 Subject: [PATCH 014/108] add onboarding selftourviewed selector --- src/libs/onboardingSelectors.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libs/onboardingSelectors.ts b/src/libs/onboardingSelectors.ts index efa67d2aed48..f4b4759867bf 100644 --- a/src/libs/onboardingSelectors.ts +++ b/src/libs/onboardingSelectors.ts @@ -35,4 +35,19 @@ function hasCompletedHybridAppOnboardingFlowSelector(tryNewDotData: OnyxValue): boolean | undefined { + if (Array.isArray(onboarding)) { + return true; + } + + return onboarding?.selfTourViewed; +} + +export {hasCompletedGuidedSetupFlowSelector, hasCompletedHybridAppOnboardingFlowSelector, hasSeenTourSelector}; From 63af4e7b7f01d664e132860d71a303aeb1705f54 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 20 Oct 2024 20:30:03 +0530 Subject: [PATCH 015/108] add tour to FAB --- .../FloatingActionButtonAndPopover.tsx | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index a49b474b185e..764095a49193 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -11,6 +11,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import PopoverMenu from '@components/PopoverMenu'; import Text from '@components/Text'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; @@ -23,14 +24,18 @@ import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {CentralPaneName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; +import {hasSeenTourSelector} from '@libs/onboardingSelectors'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; +import {getNavatticURL} from '@libs/TourUtils'; import * as App from '@userActions/App'; import * as IOU from '@userActions/IOU'; +import * as Link from '@userActions/Link'; import * as Policy from '@userActions/Policy/Policy'; import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; +import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -189,6 +194,12 @@ function FloatingActionButtonAndPopover( const {canUseSpotnanaTravel, canUseCombinedTrackSubmit} = usePermissions(); const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection, session?.email), [allPolicies, session?.email]); + const {environment} = useEnvironment(); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const navatticURL = getNavatticURL(environment, introSelected?.choice); + const [hasSeenTour = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { + selector: hasSeenTourSelector, + }); const quickActionAvatars = useMemo(() => { if (quickActionReport) { @@ -454,7 +465,24 @@ function FloatingActionButtonAndPopover( }, ] : []), - ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(allPolicies) + ...(!hasSeenTour + ? [ + { + icon: Expensicons.Tour, + displayInDefaultIconColor: true, + contentFit: 'contain' as ImageContentFit, + iconWidth: 46, + iconHeight: 40, + text: translate('tour.takeATwoMinuteTour'), + description: translate('tour.exploreExpensify'), + onSelected: () => { + Welcome.setSelfTourViewed(); + Link.openExternalLink(navatticURL); + }, + }, + ] + : []), + ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(allPolicies) && hasSeenTour ? [ { displayInDefaultIconColor: true, From 7e9bf7c70414d2fdefba7a8f51d584c99fad69ce Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 21 Oct 2024 11:35:46 +0530 Subject: [PATCH 016/108] add self guided tours to tasks --- src/CONST.ts | 17 ++++++++++++++++- src/libs/actions/Report.ts | 18 +++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 01466d8baf86..4bb90fca7aab 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -83,6 +83,13 @@ const signupQualifiers = { SMB: 'smb', } as const; +const selfGuidedTourTask: OnboardingTaskType = { + type: 'viewTour', + autoCompleted: false, + title: 'Take a 2-minute tour', + description: ({navatticURL}) => `[Take a self-guided product tour](${navatticURL}) and learn about everything Expensify has to offer.`, +}; + const onboardingEmployerOrSubmitMessage: OnboardingMessageType = { message: 'Getting paid back is as easy as sending a message. Let’s go over the basics.', video: { @@ -93,6 +100,7 @@ const onboardingEmployerOrSubmitMessage: OnboardingMessageType = { height: 960, }, tasks: [ + selfGuidedTourTask, { type: 'submitExpense', autoCompleted: false, @@ -149,11 +157,15 @@ const onboardingCompanySize = { type OnboardingInviteType = ValueOf; +type Description = + | string + | ((params: Partial<{adminsRoomLink: string; workspaceCategoriesLink: string; workspaceMoreFeaturesLink: string; workspaceMembersLink: string; navatticURL: string}>) => string); + type OnboardingTaskType = { type: string; autoCompleted: boolean; title: string; - description: string | ((params: Partial<{adminsRoomLink: string; workspaceCategoriesLink: string; workspaceMoreFeaturesLink: string; workspaceMembersLink: string}>) => string); + description: Description; }; type OnboardingMessageType = { @@ -4652,6 +4664,7 @@ const CONST = { '\n' + '*Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.*', }, + selfGuidedTourTask, { type: 'meetGuide', autoCompleted: false, @@ -4748,6 +4761,7 @@ const CONST = { height: 960, }, tasks: [ + selfGuidedTourTask, { type: 'trackExpense', autoCompleted: false, @@ -4776,6 +4790,7 @@ const CONST = { height: 960, }, tasks: [ + selfGuidedTourTask, { type: 'startChat', autoCompleted: false, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7071c96f8612..bd237908a96e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -58,6 +58,8 @@ import DateUtils from '@libs/DateUtils'; import {prepareDraftComment} from '@libs/DraftCommentUtils'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as Environment from '@libs/Environment/Environment'; +import getEnvironment from '@libs/Environment/getEnvironment'; +import type EnvironmentType from '@libs/Environment/getEnvironment/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import fileDownload from '@libs/fileDownload'; import HttpUtils from '@libs/HttpUtils'; @@ -83,6 +85,7 @@ import type {OptimisticAddCommentReportAction} from '@libs/ReportUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; +import {getNavatticURL} from '@libs/TourUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import type {OnboardingAccountingType, OnboardingCompanySizeType, OnboardingPurposeType} from '@src/CONST'; @@ -102,6 +105,7 @@ import type { ReportActionReactions, ReportUserIsTyping, } from '@src/types/onyx'; +import type IntroSelected from '@src/types/onyx/IntroSelected'; import type {Decision} from '@src/types/onyx/OriginalMessage'; import type {ConnectionName} from '@src/types/onyx/Policy'; import type Report from '@src/types/onyx/Report'; @@ -269,9 +273,21 @@ Onyx.connect({ callback: (value) => (allReportDraftComments = value), }); +let introSelected: IntroSelected | undefined = {}; + +Onyx.connect({ + key: ONYXKEYS.NVP_INTRO_SELECTED, + callback: (val) => (introSelected = val), +}); + let environmentURL: string; Environment.getEnvironmentURL().then((url: string) => (environmentURL = url)); +let environment: EnvironmentType; +getEnvironment().then((env) => { + environment = env; +}); + registerPaginationConfig({ initialCommand: WRITE_COMMANDS.OPEN_REPORT, previousCommand: READ_COMMANDS.GET_OLDER_ACTIONS, @@ -3359,7 +3375,6 @@ function completeOnboarding( reportComment: videoComment.commentText, }; } - const tasksData = data.tasks.map((task, index) => { const taskDescription = typeof task.description === 'function' @@ -3368,6 +3383,7 @@ function completeOnboarding( workspaceCategoriesLink: `${environmentURL}/${ROUTES.WORKSPACE_CATEGORIES.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMembersLink: `${environmentURL}/${ROUTES.WORKSPACE_MEMBERS.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID ?? '-1')}`, + navatticURL: getNavatticURL(environment, introSelected?.choice), }) : task.description; const currentTask = ReportUtils.buildOptimisticTaskReport( From 28940c2602ead14d427cb3a726287edf2828f8a9 Mon Sep 17 00:00:00 2001 From: daledah Date: Tue, 22 Oct 2024 19:05:34 +0700 Subject: [PATCH 017/108] fix: app crash when opening split view --- src/components/MoneyRequestConfirmationList.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index a46b9d62dfde..df12ec986aa9 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -159,7 +159,7 @@ function MoneyRequestConfirmationList({ payeePersonalDetails: payeePersonalDetailsProp, isReadOnly = false, bankAccountRoute = '', - policyID = '', + policyID, reportID = '', receiptPath = '', iouComment, @@ -174,14 +174,14 @@ function MoneyRequestConfirmationList({ shouldPlaySound = true, isConfirmed, }: MoneyRequestConfirmationListProps) { - const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`); - const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`); - const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); - const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`); - const [defaultMileageRate] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, { + const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID ?? '-1'}`); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID ?? '-1'}`); + const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID ?? '-1'}`); + const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID ?? '-1'}`); + const [defaultMileageRate] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID ?? '-1'}`, { selector: (selectedPolicy) => DistanceRequestUtils.getDefaultMileageRate(selectedPolicy), }); - const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`); + const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID ?? '-1'}`); const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); const policy = policyReal ?? policyDraft; From 2f2165bb6d41ebe02c43228bd2e01c691fe42c55 Mon Sep 17 00:00:00 2001 From: daledah Date: Fri, 25 Oct 2024 10:38:52 +0700 Subject: [PATCH 018/108] fix: migrate withFullTransactionOrNotFound --- .../step/withFullTransactionOrNotFound.tsx | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx index 66736dc80b52..8b4519d2758d 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx @@ -3,7 +3,7 @@ import {useIsFocused} from '@react-navigation/native'; import type {ComponentType, ForwardedRef, RefAttributes} from 'react'; import React, {forwardRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import * as IOUUtils from '@libs/IOUUtils'; @@ -38,14 +38,24 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_SEND_FROM | typeof SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO; -type Route = RouteProp; +type Route = RouteProp; -type WithFullTransactionOrNotFoundProps = WithFullTransactionOrNotFoundOnyxProps & {route: Route}; +type WithFullTransactionOrNotFoundProps = WithFullTransactionOrNotFoundOnyxProps & { + route: Route; +}; -export default function , TRef>(WrappedComponent: ComponentType>) { +export default function , TRef>( + WrappedComponent: ComponentType>, +): React.ComponentType & RefAttributes> { // eslint-disable-next-line rulesdir/no-negated-variables - function WithFullTransactionOrNotFound(props: TProps, ref: ForwardedRef) { - const transactionID = props.transaction?.transactionID; + function WithFullTransactionOrNotFound(props: Omit, ref: ForwardedRef) { + const {route} = props; + const transactionID = route.params.transactionID ?? -1; + const userAction = 'action' in route.params && route.params.action ? route.params.action : CONST.IOU.ACTION.CREATE; + + const shouldUseTransactionDraft = IOUUtils.shouldUseTransactionDraft(userAction); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`); + const [transactionDraft] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`); const isFocused = useIsFocused(); @@ -55,31 +65,19 @@ export default function ; } - return ( ); } WithFullTransactionOrNotFound.displayName = `withFullTransactionOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - // eslint-disable-next-line deprecation/deprecation - return withOnyx, WithFullTransactionOrNotFoundOnyxProps>({ - transaction: { - key: ({route}) => { - const transactionID = route.params.transactionID ?? -1; - const userAction = 'action' in route.params && route.params.action ? route.params.action : CONST.IOU.ACTION.CREATE; - if (IOUUtils.shouldUseTransactionDraft(userAction)) { - return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}` as `${typeof ONYXKEYS.COLLECTION.TRANSACTION}${string}`; - } - return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; - }, - }, - })(forwardRef(WithFullTransactionOrNotFound)); + return forwardRef(WithFullTransactionOrNotFound); } export type {WithFullTransactionOrNotFoundProps}; From 55abb2507c0b8f5f0ef2e39ff5e7fc964471f764 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 08:21:34 +0530 Subject: [PATCH 019/108] don't hide create workspace --- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 764095a49193..a80dc72cb281 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -482,7 +482,7 @@ function FloatingActionButtonAndPopover( }, ] : []), - ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(allPolicies) && hasSeenTour + ...(!isLoading && !Policy.hasActiveChatEnabledPolicies(allPolicies) ? [ { displayInDefaultIconColor: true, From 9979c5231a1e7016ef0590804af96898b0f1836a Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 09:11:45 +0530 Subject: [PATCH 020/108] replace tour with binoculars icon --- assets/images/binoculars.svg | 25 +++++++++++++++++++++++++ assets/images/tour.svg | 6 ------ src/components/Icon/Expensicons.ts | 4 ++-- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 assets/images/binoculars.svg delete mode 100644 assets/images/tour.svg diff --git a/assets/images/binoculars.svg b/assets/images/binoculars.svg new file mode 100644 index 000000000000..64977dee38b5 --- /dev/null +++ b/assets/images/binoculars.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/tour.svg b/assets/images/tour.svg deleted file mode 100644 index 926cfcd8de69..000000000000 --- a/assets/images/tour.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index f430db6d6ded..fa531ce34adf 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -23,6 +23,7 @@ import Bed from '@assets/images/bed.svg'; import Bell from '@assets/images/bell.svg'; import BellSlash from '@assets/images/bellSlash.svg'; import Bill from '@assets/images/bill.svg'; +import Binoculars from '@assets/images/binoculars.svg'; import Bolt from '@assets/images/bolt.svg'; import Bookmark from '@assets/images/bookmark.svg'; import Box from '@assets/images/box.svg'; @@ -183,7 +184,6 @@ import Task from '@assets/images/task.svg'; import Thread from '@assets/images/thread.svg'; import ThreeDots from '@assets/images/three-dots.svg'; import ThumbsUp from '@assets/images/thumbs-up.svg'; -import Tour from '@assets/images/tour.svg'; import Transfer from '@assets/images/transfer.svg'; import Trashcan from '@assets/images/trashcan.svg'; import Unlock from '@assets/images/unlock.svg'; @@ -224,6 +224,7 @@ export { Bill, Bell, BellSlash, + Binoculars, Bolt, Box, Briefcase, @@ -406,6 +407,5 @@ export { Bookmark, Star, QBDSquare, - Tour, GalleryNotFound, }; From 18daf190d1d07992e262a396ce2197219e8cb967 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 09:14:13 +0530 Subject: [PATCH 021/108] add circle icon style --- src/styles/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/styles/index.ts b/src/styles/index.ts index 5b04a7a8eace..dad337c04038 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1798,6 +1798,11 @@ const styles = (theme: ThemeColors) => alignItems: 'center', }, + popoverIconCircle: { + backgroundColor: theme.buttonDefaultBG, + borderRadius: variables.buttonBorderRadius, + }, + rightLabelMenuItem: { fontSize: variables.fontSizeLabel, color: theme.textSupporting, From c830443fe31a8fdd3b78f63ac82ccc0b997e9297 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 09:14:33 +0530 Subject: [PATCH 022/108] change fab icon style --- .../SidebarScreen/FloatingActionButtonAndPopover.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index a80dc72cb281..6581d08cbfbe 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -17,6 +17,7 @@ import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import getIconForAction from '@libs/getIconForAction'; @@ -182,6 +183,7 @@ function FloatingActionButtonAndPopover( ref: ForwardedRef, ) { const styles = useThemeStyles(); + const theme = useTheme(); const {translate} = useLocalize(); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID ?? -1}`); const [isCreateMenuActive, setIsCreateMenuActive] = useState(false); @@ -468,11 +470,9 @@ function FloatingActionButtonAndPopover( ...(!hasSeenTour ? [ { - icon: Expensicons.Tour, - displayInDefaultIconColor: true, - contentFit: 'contain' as ImageContentFit, - iconWidth: 46, - iconHeight: 40, + icon: Expensicons.Binoculars, + iconStyles: styles.popoverIconCircle, + iconFill: theme.icon, text: translate('tour.takeATwoMinuteTour'), description: translate('tour.exploreExpensify'), onSelected: () => { From 053b628390bf256054e4d70adbb06188f602ca4d Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 09:16:58 +0530 Subject: [PATCH 023/108] default to false --- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 6581d08cbfbe..8e8bea95e585 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -199,7 +199,7 @@ function FloatingActionButtonAndPopover( const {environment} = useEnvironment(); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const navatticURL = getNavatticURL(environment, introSelected?.choice); - const [hasSeenTour = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { + const [hasSeenTour = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { selector: hasSeenTourSelector, }); From 512fa36bb6f8ef410bf5554daba949798a69777e Mon Sep 17 00:00:00 2001 From: c3024 Date: Sat, 26 Oct 2024 14:54:21 +0530 Subject: [PATCH 024/108] use existing param engagementChoice --- src/libs/actions/Report.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 35e4c9b75ae1..d6eb2473e2aa 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -105,7 +105,6 @@ import type { ReportActionReactions, ReportUserIsTyping, } from '@src/types/onyx'; -import type IntroSelected from '@src/types/onyx/IntroSelected'; import type {Decision} from '@src/types/onyx/OriginalMessage'; import type {ConnectionName} from '@src/types/onyx/Policy'; import type Report from '@src/types/onyx/Report'; @@ -274,13 +273,6 @@ Onyx.connect({ callback: (value) => (allReportDraftComments = value), }); -let introSelected: IntroSelected | undefined = {}; - -Onyx.connect({ - key: ONYXKEYS.NVP_INTRO_SELECTED, - callback: (val) => (introSelected = val), -}); - let environmentURL: string; Environment.getEnvironmentURL().then((url: string) => (environmentURL = url)); @@ -3394,7 +3386,7 @@ function completeOnboarding( workspaceCategoriesLink: `${environmentURL}/${ROUTES.WORKSPACE_CATEGORIES.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMembersLink: `${environmentURL}/${ROUTES.WORKSPACE_MEMBERS.getRoute(onboardingPolicyID ?? '-1')}`, workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID ?? '-1')}`, - navatticURL: getNavatticURL(environment, introSelected?.choice), + navatticURL: getNavatticURL(environment, engagementChoice), }) : task.description; const currentTask = ReportUtils.buildOptimisticTaskReport( From b370bc6b66977d4c179e918b822ff66e6dea56f4 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 17:59:52 +0530 Subject: [PATCH 025/108] replace withOnyx -> useOnyx --- .../FloatingActionButtonAndPopover.tsx | 86 ++++--------------- 1 file changed, 15 insertions(+), 71 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 8e8bea95e585..531ad8e39a8f 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -1,10 +1,10 @@ import {useIsFocused as useIsFocusedOriginal, useNavigationState} from '@react-navigation/native'; import type {ImageContentFit} from 'expo-image'; -import type {ForwardedRef, RefAttributes} from 'react'; +import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; import FloatingActionButton from '@components/FloatingActionButton'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -45,6 +45,7 @@ import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {QuickActionName} from '@src/types/onyx/QuickAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; // On small screen we hide the search page from central pane to show the search bottom tab page with bottom tab bar. // We need to take this in consideration when checking if the screen is focused. @@ -57,33 +58,7 @@ const useIsFocused = () => { type PolicySelector = Pick; -type FloatingActionButtonAndPopoverOnyxProps = { - /** The list of policies the user has access to. */ - allPolicies: OnyxCollection; - - /** Whether app is in loading state */ - isLoading: OnyxEntry; - - /** Information on the last taken action to display as Quick Action */ - quickAction: OnyxEntry; - - /** The report data of the quick action */ - quickActionReport: OnyxEntry; - - /** The policy data of the quick action */ - quickActionPolicy: OnyxEntry; - - /** The current session */ - session: OnyxEntry; - - /** Personal details of all the users */ - personalDetails: OnyxEntry; - - /** Has user seen track expense training interstitial */ - hasSeenTrackTraining: OnyxEntry; -}; - -type FloatingActionButtonAndPopoverProps = FloatingActionButtonAndPopoverOnyxProps & { +type FloatingActionButtonAndPopoverProps = { /* Callback function when the menu is shown */ onShowCreateMenu?: () => void; @@ -167,25 +142,20 @@ const getQuickActionTitle = (action: QuickActionName): TranslationPaths => { * Responsible for rendering the {@link PopoverMenu}, and the accompanying * FAB that can open or close the menu. */ -function FloatingActionButtonAndPopover( - { - onHideCreateMenu, - onShowCreateMenu, - isLoading = false, - allPolicies, - quickAction, - quickActionReport, - quickActionPolicy, - session, - personalDetails, - hasSeenTrackTraining, - }: FloatingActionButtonAndPopoverProps, - ref: ForwardedRef, -) { +function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: FloatingActionButtonAndPopoverProps, ref: ForwardedRef) { const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); + const [isLoading] = useOnyx(ONYXKEYS.IS_LOADING_APP); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [session] = useOnyx(ONYXKEYS.SESSION); + const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); + const [quickActionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID ?? -1}`); + const [quickActionPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${quickActionReport?.policyID}`); + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (c) => mapOnyxCollectionItems(c, policySelector)}); + const [hasSeenTrackTraining] = useOnyx(ONYXKEYS.NVP_HAS_SEEN_TRACK_TRAINING); + const [isCreateMenuActive, setIsCreateMenuActive] = useState(false); const fabRef = useRef(null); const {windowHeight} = useWindowDimensions(); @@ -538,32 +508,6 @@ function FloatingActionButtonAndPopover( FloatingActionButtonAndPopover.displayName = 'FloatingActionButtonAndPopover'; -export default withOnyx, FloatingActionButtonAndPopoverOnyxProps>({ - allPolicies: { - key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, - }, - isLoading: { - key: ONYXKEYS.IS_LOADING_APP, - }, - quickAction: { - key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, - }, - quickActionReport: { - key: ({quickAction}) => `${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`, - }, - quickActionPolicy: { - key: ({quickActionReport}) => `${ONYXKEYS.COLLECTION.POLICY}${quickActionReport?.policyID}`, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - hasSeenTrackTraining: { - key: ONYXKEYS.NVP_HAS_SEEN_TRACK_TRAINING, - }, -})(forwardRef(FloatingActionButtonAndPopover)); +export default forwardRef(FloatingActionButtonAndPopover); export type {PolicySelector}; From 5fe757cb27dbeff44e4e4c9253ce601229f738e6 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 19:20:27 +0530 Subject: [PATCH 026/108] Update src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx Co-authored-by: Krishna Chaitanya <102477862+c3024@users.noreply.github.com> --- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 531ad8e39a8f..fa3034933c56 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -146,7 +146,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); - const [isLoading] = useOnyx(ONYXKEYS.IS_LOADING_APP); + const [isLoading = false] = useOnyx(ONYXKEYS.IS_LOADING_APP); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [session] = useOnyx(ONYXKEYS.SESSION); const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); From 09dfbc97efaa6e0ad477f6adf23930d8556e4690 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 19:59:52 +0530 Subject: [PATCH 027/108] add fixed width and height to icon --- src/styles/index.ts | 2 ++ src/styles/variables.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/styles/index.ts b/src/styles/index.ts index dad337c04038..e9f9a204551b 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1801,6 +1801,8 @@ const styles = (theme: ThemeColors) => popoverIconCircle: { backgroundColor: theme.buttonDefaultBG, borderRadius: variables.buttonBorderRadius, + height: variables.w40, + width: variables.fabIconWidth, }, rightLabelMenuItem: { diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 4e9bf50e3c8e..b6ac4747e63d 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -215,6 +215,7 @@ export default { restrictedActionIllustrationHeight: 136, photoUploadPopoverWidth: 335, onboardingModalWidth: 500, + fabIconWidth: 46, // The height of the empty list is 14px (2px for borders and 12px for vertical padding) // This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility From 2c561aee7b9e92f086a02b088c9f66934d93ca93 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 20:02:21 +0530 Subject: [PATCH 028/108] add fixed width and height to icon --- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 5 +++-- src/styles/index.ts | 4 ++-- src/styles/variables.ts | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index fa3034933c56..cefde33af0c2 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -30,6 +30,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import {getNavatticURL} from '@libs/TourUtils'; +import variables from '@styles/variables'; import * as App from '@userActions/App'; import * as IOU from '@userActions/IOU'; import * as Link from '@userActions/Link'; @@ -458,8 +459,8 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl displayInDefaultIconColor: true, contentFit: 'contain' as ImageContentFit, icon: Expensicons.NewWorkspace, - iconWidth: 46, - iconHeight: 40, + iconWidth: variables.h40, + iconHeight: variables.w46, text: translate('workspace.new.newWorkspace'), description: translate('workspace.new.getTheExpensifyCardAndMore'), onSelected: () => interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt()), diff --git a/src/styles/index.ts b/src/styles/index.ts index e9f9a204551b..7b4c6af382b4 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1801,8 +1801,8 @@ const styles = (theme: ThemeColors) => popoverIconCircle: { backgroundColor: theme.buttonDefaultBG, borderRadius: variables.buttonBorderRadius, - height: variables.w40, - width: variables.fabIconWidth, + height: variables.h40, + width: variables.w46, }, rightLabelMenuItem: { diff --git a/src/styles/variables.ts b/src/styles/variables.ts index b6ac4747e63d..aaaebcca138c 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -215,7 +215,6 @@ export default { restrictedActionIllustrationHeight: 136, photoUploadPopoverWidth: 335, onboardingModalWidth: 500, - fabIconWidth: 46, // The height of the empty list is 14px (2px for borders and 12px for vertical padding) // This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility @@ -265,6 +264,7 @@ export default { h20: 20, h28: 28, h36: 36, + h40: 40, h112: 112, h172: 172, w20: 20, @@ -272,6 +272,7 @@ export default { w36: 36, w40: 40, w44: 44, + w46: 46, w52: 52, w80: 80, w92: 92, From 4fd98859bfd7e0d9ff9816800874c8b3f9aa054d Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sat, 26 Oct 2024 20:03:11 +0530 Subject: [PATCH 029/108] fix typo --- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index cefde33af0c2..5899429cf23f 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -459,8 +459,8 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl displayInDefaultIconColor: true, contentFit: 'contain' as ImageContentFit, icon: Expensicons.NewWorkspace, - iconWidth: variables.h40, - iconHeight: variables.w46, + iconWidth: variables.w46, + iconHeight: variables.h40, text: translate('workspace.new.newWorkspace'), description: translate('workspace.new.getTheExpensifyCardAndMore'), onSelected: () => interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt()), From 525880dbb8ec438418527941d31f5adcd68cca39 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 28 Oct 2024 16:09:31 +0100 Subject: [PATCH 030/108] Add feed details help links --- src/languages/en.ts | 3 +++ src/languages/es.ts | 3 +++ .../companyCards/addNew/DetailsStep.tsx | 23 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index f4501f758557..59085de429d8 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3101,14 +3101,17 @@ const translations = { processorLabel: 'Processor ID', bankLabel: 'Financial institution (bank) ID', companyLabel: 'Company ID', + helpLabel: 'Where do I find these IDs?', }, gl1025: { title: `What's the Amex delivery file name?`, fileNameLabel: 'Delivery file name', + helpLabel: 'Where do I find the delivery file name?', }, cdf: { title: `What's the Mastercard distribution ID?`, distributionLabel: 'Distribution ID', + helpLabel: 'Where do I find the distribution ID?', }, }, amexCorporate: 'Select this if the front of your cards say “Corporate”', diff --git a/src/languages/es.ts b/src/languages/es.ts index 6614afbc483d..853684a32a19 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3140,14 +3140,17 @@ const translations = { processorLabel: 'ID del procesador', bankLabel: 'Identificación de la institución financiera (banco)', companyLabel: 'Empresa ID', + helpLabel: '¿Dónde encuentro estos IDs?', }, gl1025: { title: `¿Cuál es el nombre del archivo de entrega de Amex?`, fileNameLabel: 'Nombre del archivo de entrega', + helpLabel: '¿Dónde encuentro el nombre del archivo de entrega?', }, cdf: { title: `¿Cuál es el identificador de distribución de Mastercard?`, distributionLabel: 'ID de distribución', + helpLabel: '¿Dónde encuentro el ID de distribución?', }, }, amexCorporate: 'Seleccione esto si el frente de sus tarjetas dice “Corporativa”', diff --git a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx index 2c719acd47d8..86a5410766b5 100644 --- a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx @@ -1,19 +1,25 @@ import React, {useCallback} from 'react'; +import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; +import TextLink from '@components/TextLink'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ValidationUtils from '@libs/ValidationUtils'; import Navigation from '@navigation/Navigation'; +import variables from '@styles/variables'; import * as CompanyCards from '@userActions/CompanyCards'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -27,6 +33,7 @@ type DetailsStepProps = { function DetailsStep({policyID}: DetailsStepProps) { const {translate} = useLocalize(); + const theme = useTheme(); const styles = useThemeStyles(); const {inputCallbackRef} = useAutoFocusInput(); const {canUseDirectFeeds} = usePermissions(); @@ -177,6 +184,22 @@ function DetailsStep({policyID}: DetailsStepProps) { enabledWhenOffline > {renderInputs()} + {feedProvider && !isStripeFeedProvider && ( + + + + {translate(`workspace.companyCards.addNewCard.feedDetails.${feedProvider}.helpLabel`)} + + + )} From e0b1413111d3dbfba0062162974da43292777073 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 21:44:37 +0530 Subject: [PATCH 031/108] add new method to get latest error --- src/libs/ErrorUtils.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index ba2a33a367d4..b880239b8abf 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -134,6 +134,15 @@ function getLatestErrorFieldForAnyField Object.assign(acc, error), {}); } +function getLatestError(errors?: Errors): Errors { + if (!errors || Object.keys(errors).length === 0) { + return {}; + } + + const key = Object.keys(errors).sort().reverse().at(0) ?? ''; + return {[key]: getErrorMessageWithTranslationData(errors[key])}; +} + /** * Method used to attach already translated message * @param errors - An object containing current errors in the form @@ -198,6 +207,7 @@ export { getLatestErrorFieldForAnyField, getLatestErrorMessage, getLatestErrorMessageField, + getLatestError, getMicroSecondOnyxErrorWithTranslationKey, getMicroSecondOnyxErrorWithMessage, getMicroSecondOnyxErrorObject, From 7266a96c79df45c9075d98080e3366f9f9073caf Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 21:44:59 +0530 Subject: [PATCH 032/108] update type to handle errors keyed by email for delegate --- src/types/onyx/Account.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/types/onyx/Account.ts b/src/types/onyx/Account.ts index b856ed9010dd..db991bb2f1e5 100644 --- a/src/types/onyx/Account.ts +++ b/src/types/onyx/Account.ts @@ -20,9 +20,6 @@ type Delegate = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Whether the user validation code was sent */ validateCodeSent?: boolean; - /** Field-specific server side errors keyed by microtime */ - errorFields?: OnyxCommon.ErrorFields; - /** Whether the user is loading */ isLoading?: boolean; @@ -30,6 +27,18 @@ type Delegate = OnyxCommon.OnyxValueWithOfflineFeedback<{ optimisticAccountID?: number; }>; +/** Delegate errors */ +type DelegateErrors = { + /** Errors while adding a delegate */ + addDelegate?: Record; + + /** Errors while updating a delegate's role */ + updateDelegateRole?: Record; + + /** Errors while removing a delegate */ + removeDelegate?: Record; +}; + /** Model of delegated access data */ type DelegatedAccess = { /** The users that can access your account as a delegate */ @@ -41,8 +50,8 @@ type DelegatedAccess = { /** The email of original user when they are acting as a delegate for another account */ delegate?: string; - /** Authentication failure errors when disconnecting as a copilot */ - errorFields?: OnyxCommon.ErrorFields; + /** Field-specific server side errors keyed by microtime */ + errorFields?: DelegateErrors; }; /** Model of user account */ From aa00cbecdba7315b921a5bad89fed02d850f6071 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 22:07:06 +0530 Subject: [PATCH 033/108] update error field to be keyed by email --- src/libs/actions/Delegate.ts | 63 ++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index 28f2019bb231..cc9b1b6633fc 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -190,7 +190,6 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { : { ...delegate, isLoading: true, - errorFields: {addDelegate: null}, pendingFields: {email: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, role: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, @@ -204,7 +203,6 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { email, role, isLoading: true, - errorFields: {addDelegate: null}, pendingFields: {email: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, role: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, @@ -218,6 +216,11 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { value: { delegatedAccess: { delegates: optimisticDelegateData(), + errorFields: { + addDelegate: { + [email]: null, + }, + }, }, isLoading: true, }, @@ -233,7 +236,6 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { : { ...delegate, isLoading: false, - errorFields: {addDelegate: null}, pendingAction: null, pendingFields: {email: null, role: null}, optimisticAccountID: undefined, @@ -247,7 +249,6 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { { email, role, - errorFields: {addDelegate: null}, isLoading: false, pendingAction: null, pendingFields: {email: null, role: null}, @@ -263,6 +264,11 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { value: { delegatedAccess: { delegates: successDelegateData(), + errorFields: { + addDelegate: { + [email]: null, + }, + }, }, isLoading: false, }, @@ -278,7 +284,6 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { : { ...delegate, isLoading: false, - errorFields: {addDelegate: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('contacts.genericFailureMessages.validateSecondaryLogin')}, }, ) ?? [] ); @@ -289,9 +294,6 @@ function addDelegate(email: string, role: DelegateRole, validateCode: string) { { email, role, - errorFields: { - addDelegate: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('contacts.genericFailureMessages.validateSecondaryLogin'), - }, isLoading: false, pendingFields: {email: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, role: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, @@ -328,11 +330,15 @@ function removeDelegate(email: string) { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { + errorFields: { + removeDelegate: { + [email]: null, + }, + }, delegates: delegatedAccess.delegates?.map((delegate) => delegate.email === email ? { ...delegate, - errorFields: {removeDelegate: null}, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, pendingFields: {email: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, role: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}, } @@ -361,13 +367,15 @@ function removeDelegate(email: string) { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { + errorFields: { + removeDelegate: { + [email]: null, + }, + }, delegates: delegatedAccess.delegates?.map((delegate) => delegate.email === email ? { ...delegate, - errorFields: { - removeDelegate: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('delegate.genericError'), - }, pendingAction: null, pendingFields: undefined, } @@ -383,14 +391,18 @@ function removeDelegate(email: string) { API.write(WRITE_COMMANDS.REMOVE_DELEGATE, parameters, {optimisticData, successData, failureData}); } -function clearAddDelegateErrors(email: string, fieldName: string) { +function clearDelegateErrorsByField(email: string, fieldName: string) { if (!delegatedAccess?.delegates) { return; } Onyx.merge(ONYXKEYS.ACCOUNT, { delegatedAccess: { - delegates: delegatedAccess.delegates.map((delegate) => (delegate.email !== email ? delegate : {...delegate, errorFields: {...delegate.errorFields, [fieldName]: null}})), + errorFields: { + [fieldName]: { + [email]: null, + }, + }, }, }); } @@ -422,12 +434,16 @@ function updateDelegateRole(email: string, role: DelegateRole, validateCode: str key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { + errorFields: { + updateDelegateRole: { + [email]: null, + }, + }, delegates: delegatedAccess.delegates.map((delegate) => delegate.email === email ? { ...delegate, role, - errorFields: {updateDelegateRole: null}, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, pendingFields: {role: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, isLoading: true, @@ -445,12 +461,16 @@ function updateDelegateRole(email: string, role: DelegateRole, validateCode: str key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { + errorFields: { + updateDelegateRole: { + [email]: null, + }, + }, delegates: delegatedAccess.delegates.map((delegate) => delegate.email === email ? { ...delegate, role, - errorFields: {updateDelegateRole: null}, pendingAction: null, pendingFields: {role: null}, isLoading: false, @@ -472,9 +492,6 @@ function updateDelegateRole(email: string, role: DelegateRole, validateCode: str delegate.email === email ? { ...delegate, - errorFields: { - updateDelegateRole: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('delegate.genericError'), - }, isLoading: false, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, pendingFields: {role: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, @@ -502,12 +519,16 @@ function updateDelegateRoleOptimistically(email: string, role: DelegateRole) { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { + errorFields: { + updateDelegateRole: { + [email]: null, + }, + }, delegates: delegatedAccess.delegates.map((delegate) => delegate.email === email ? { ...delegate, role, - errorFields: {updateDelegateRole: null}, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, pendingFields: {role: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, } @@ -568,7 +589,7 @@ export { clearDelegatorErrors, addDelegate, requestValidationCode, - clearAddDelegateErrors, + clearDelegateErrorsByField, removePendingDelegate, restoreDelegateSession, isConnectedAsDelegate, From 32cae875e4a397cf8536d8ef58eaf9e4654ee5da Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 22:07:19 +0530 Subject: [PATCH 034/108] handle connect and disconnect --- src/libs/actions/Delegate.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index cc9b1b6633fc..e294a57e6c5f 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -47,7 +47,11 @@ function connect(email: string) { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { - delegators: delegatedAccess.delegators.map((delegator) => (delegator.email === email ? {...delegator, errorFields: {connect: null}} : delegator)), + errorFields: { + connect: { + [email]: null, + }, + }, }, }, }, @@ -59,7 +63,11 @@ function connect(email: string) { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { - delegators: delegatedAccess.delegators.map((delegator) => (delegator.email === email ? {...delegator, errorFields: undefined} : delegator)), + errorFields: { + connect: { + [email]: null, + }, + }, }, }, }, @@ -71,9 +79,11 @@ function connect(email: string) { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { - delegators: delegatedAccess.delegators.map((delegator) => - delegator.email === email ? {...delegator, errorFields: {connect: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('delegate.genericError')}} : delegator, - ), + errorFields: { + connect: { + [email]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('delegate.genericError'), + }, + }, }, }, }, @@ -112,7 +122,7 @@ function disconnect() { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { - errorFields: {connect: null}, + errorFields: {disconnect: null}, }, }, }, @@ -136,7 +146,7 @@ function disconnect() { key: ONYXKEYS.ACCOUNT, value: { delegatedAccess: { - errorFields: {connect: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('delegate.genericError')}, + errorFields: {disconnect: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('delegate.genericError')}, }, }, }, From 6f8df7a15bf8b765017d7880017c8fb673417c42 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 22:07:27 +0530 Subject: [PATCH 035/108] handle connect and disconnect --- src/components/AccountSwitcher.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/AccountSwitcher.tsx b/src/components/AccountSwitcher.tsx index 8ccab44a2cb9..7df2195200af 100644 --- a/src/components/AccountSwitcher.tsx +++ b/src/components/AccountSwitcher.tsx @@ -87,7 +87,7 @@ function AccountSwitcher() { } const delegatePersonalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); - const error = ErrorUtils.getLatestErrorField(account?.delegatedAccess, 'connect'); + const error = ErrorUtils.getLatestError(account?.delegatedAccess?.errorFields?.disconnect); return [ createBaseMenuItem(delegatePersonalDetails, error, { @@ -105,8 +105,9 @@ function AccountSwitcher() { const delegatorMenuItems: PopoverMenuItem[] = delegators .filter(({email}) => email !== currentUserPersonalDetails.login) - .map(({email, role, errorFields}) => { - const error = ErrorUtils.getLatestErrorField({errorFields}, 'connect'); + .map(({email, role}) => { + const errorFields = account?.delegatedAccess?.errorFields ?? {}; + const error = ErrorUtils.getLatestError(errorFields?.connect?.[email]); const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email); return createBaseMenuItem(personalDetails, error, { badgeText: translate('delegate.role', {role}), From e308d227c75a84ac19f6d137b709c65fd4084e9e Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 22:08:15 +0530 Subject: [PATCH 036/108] update error paths --- src/pages/settings/Security/SecuritySettingsPage.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/settings/Security/SecuritySettingsPage.tsx b/src/pages/settings/Security/SecuritySettingsPage.tsx index 42352594db7a..3ed01e1bcea7 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.tsx +++ b/src/pages/settings/Security/SecuritySettingsPage.tsx @@ -26,7 +26,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {clearAddDelegateErrors, removeDelegate} from '@libs/actions/Delegate'; +import {clearDelegateErrorsByField, removeDelegate} from '@libs/actions/Delegate'; import * as ErrorUtils from '@libs/ErrorUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; @@ -56,6 +56,7 @@ function SecuritySettingsPage() { const [shouldShowDelegatePopoverMenu, setShouldShowDelegatePopoverMenu] = useState(false); const [shouldShowRemoveDelegateModal, setShouldShowRemoveDelegateModal] = useState(false); const [selectedDelegate, setSelectedDelegate] = useState(); + const errorFields = account?.delegatedAccess?.errorFields ?? {}; const [anchorPosition, setAnchorPosition] = useState({ anchorPositionHorizontal: 0, @@ -136,9 +137,10 @@ function SecuritySettingsPage() { () => delegates .filter((d) => !d.optimisticAccountID) - .map(({email, role, pendingAction, errorFields, pendingFields}) => { + .map(({email, role, pendingAction, pendingFields}) => { const personalDetail = getPersonalDetailByEmail(email); - const error = ErrorUtils.getLatestErrorField({errorFields}, 'addDelegate'); + const addDelegateErrors = errorFields?.addDelegate?.[email]; + const error = ErrorUtils.getLatestError(addDelegateErrors); const onPress = (e: GestureResponderEvent | KeyboardEvent) => { if (isEmptyObject(pendingAction)) { @@ -171,14 +173,14 @@ function SecuritySettingsPage() { shouldShowRightIcon: true, pendingAction, shouldForceOpacity: !!pendingAction, - onPendingActionDismiss: () => clearAddDelegateErrors(email, 'addDelegate'), + onPendingActionDismiss: () => clearDelegateErrorsByField(email, 'addDelegate'), error, onPress, }; }), // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps - [delegates, translate, styles, personalDetails], + [delegates, translate, styles, personalDetails, errorFields], ); const delegatorMenuItems: MenuItemProps[] = useMemo( From 2854ff949201659fb98aaa27c246fc3cfc05221a Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 22:08:32 +0530 Subject: [PATCH 037/108] update error paths --- .../Security/AddDelegate/DelegateMagicCodeModal.tsx | 9 +++++---- .../UpdateDelegateRole/UpdateDelegateMagicCodePage.tsx | 5 +++-- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/pages/settings/Security/AddDelegate/DelegateMagicCodeModal.tsx b/src/pages/settings/Security/AddDelegate/DelegateMagicCodeModal.tsx index dd54aa5b9404..48a90621bb5f 100644 --- a/src/pages/settings/Security/AddDelegate/DelegateMagicCodeModal.tsx +++ b/src/pages/settings/Security/AddDelegate/DelegateMagicCodeModal.tsx @@ -23,16 +23,17 @@ function DelegateMagicCodeModal({login, role, onClose}: DelegateMagicCodeModalPr const [isValidateCodeActionModalVisible, setIsValidateCodeActionModalVisible] = useState(true); const currentDelegate = account?.delegatedAccess?.delegates?.find((d) => d.email === login); - const validateLoginError = ErrorUtils.getLatestErrorField(currentDelegate, 'addDelegate'); + const addDelegateErrors = account?.delegatedAccess?.errorFields?.addDelegate?.[login]; + const validateLoginError = ErrorUtils.getLatestError(addDelegateErrors); useEffect(() => { - if (!currentDelegate || !!currentDelegate.pendingFields?.email || !!currentDelegate.errorFields?.addDelegate) { + if (!currentDelegate || !!currentDelegate.pendingFields?.email || !!addDelegateErrors) { return; } // Dismiss modal on successful magic code verification Navigation.navigate(ROUTES.SETTINGS_SECURITY); - }, [login, currentDelegate, role]); + }, [login, currentDelegate, role, addDelegateErrors]); const onBackButtonPress = () => { onClose?.(); @@ -43,7 +44,7 @@ function DelegateMagicCodeModal({login, role, onClose}: DelegateMagicCodeModalPr if (!validateLoginError) { return; } - Delegate.clearAddDelegateErrors(currentDelegate?.email ?? '', 'addDelegate'); + Delegate.clearDelegateErrorsByField(currentDelegate?.email ?? '', 'addDelegate'); }; const sendValidateCode = () => { diff --git a/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/UpdateDelegateMagicCodePage.tsx b/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/UpdateDelegateMagicCodePage.tsx index 2c1bc55e0e92..3bc82e8d7e65 100644 --- a/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/UpdateDelegateMagicCodePage.tsx +++ b/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/UpdateDelegateMagicCodePage.tsx @@ -28,15 +28,16 @@ function UpdateDelegateMagicCodePage({route}: UpdateDelegateMagicCodePageProps) const validateCodeFormRef = useRef(null); const currentDelegate = account?.delegatedAccess?.delegates?.find((d) => d.email === login); + const updateDelegateErrors = account?.delegatedAccess?.errorFields?.addDelegate?.[login]; useEffect(() => { - if (!currentDelegate || !!currentDelegate.pendingFields?.role || !!currentDelegate.errorFields?.updateDelegateRole) { + if (!currentDelegate || !!currentDelegate.pendingFields?.role || !!updateDelegateErrors) { return; } // Dismiss modal on successful magic code verification Navigation.dismissModal(); - }, [login, currentDelegate, role]); + }, [login, currentDelegate, role, updateDelegateErrors]); const onBackButtonPress = () => { Navigation.goBack(ROUTES.SETTINGS_UPDATE_DELEGATE_ROLE.getRoute(login, role)); diff --git a/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx index 4c07803ef0e3..446810418eed 100644 --- a/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -127,7 +127,7 @@ function BaseValidateCodeForm({autoComplete = 'one-time-code', innerRef = () => setValidateCode(text); setFormError({}); if (validateLoginError) { - Delegate.clearAddDelegateErrors(currentDelegate?.email ?? '', 'updateDelegateRole'); + Delegate.clearDelegateErrorsByField(currentDelegate?.email ?? '', 'updateDelegateRole'); } }, [currentDelegate?.email, validateLoginError], From e255aa0fab98b915cd887e877ee837bc3e9ef7e6 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 22:08:49 +0530 Subject: [PATCH 038/108] add connect and disconnect errors --- src/types/onyx/Account.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/types/onyx/Account.ts b/src/types/onyx/Account.ts index db991bb2f1e5..3902d67882c4 100644 --- a/src/types/onyx/Account.ts +++ b/src/types/onyx/Account.ts @@ -29,14 +29,20 @@ type Delegate = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Delegate errors */ type DelegateErrors = { - /** Errors while adding a delegate */ + /** Errors while adding a delegate keyed by email */ addDelegate?: Record; - /** Errors while updating a delegate's role */ + /** Errors while updating a delegate's role keyed by email */ updateDelegateRole?: Record; - /** Errors while removing a delegate */ + /** Errors while removing a delegate keyed by email */ removeDelegate?: Record; + + /** Errors while connecting as a delegate keyed by email */ + connect?: Record; + + /** Errors while disconnecting as a delegate. No email needed here. */ + disconnect?: OnyxCommon.Errors; }; /** Model of delegated access data */ From 0e7a6892c055bc6817eb33c512329cd8d0490ad7 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 28 Oct 2024 22:24:03 +0530 Subject: [PATCH 039/108] fix update delegate error --- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx index 446810418eed..7c35d1478eb2 100644 --- a/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/settings/Security/AddDelegate/UpdateDelegateRole/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -66,7 +66,8 @@ function BaseValidateCodeForm({autoComplete = 'one-time-code', innerRef = () => const focusTimeoutRef = useRef(null); const currentDelegate = account?.delegatedAccess?.delegates?.find((d) => d.email === delegate); - const validateLoginError = ErrorUtils.getLatestErrorField(currentDelegate, 'updateDelegateRole'); + const errorFields = account?.delegatedAccess?.errorFields ?? {}; + const validateLoginError = ErrorUtils.getLatestError(errorFields.updateDelegateRole?.[currentDelegate?.email ?? '']); const shouldDisableResendValidateCode = !!isOffline || currentDelegate?.isLoading; From eb4ab896c942c75175f3be7e15f87df5003799ce Mon Sep 17 00:00:00 2001 From: Anusha Date: Tue, 29 Oct 2024 19:22:31 +0500 Subject: [PATCH 040/108] fix navigation --- .../HTMLRenderers/MentionReportRenderer/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx index 3ab907dc767d..7de9cb230e19 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx @@ -10,10 +10,13 @@ import Text from '@components/Text'; import useCurrentReportID from '@hooks/useCurrentReportID'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@navigation/Navigation'; +import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; +import {RootStackParamList, State} from '@libs/Navigation/types'; +import Navigation, {navigationRef} from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import MentionReportContext from './MentionReportContext'; @@ -69,7 +72,12 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, ...defaultRender } const {reportID, mentionDisplayText} = mentionDetails; - const navigationRoute = reportID ? ROUTES.REPORT_WITH_ID.getRoute(reportID) : undefined; + let navigationRoute: any = reportID ? ROUTES.REPORT_WITH_ID.getRoute(reportID) : undefined; + const topmostCentralPaneRoute = getTopmostCentralPaneRoute(navigationRef.getRootState() as State); + const backTo = Navigation.getActiveRoute(); + if (topmostCentralPaneRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { + navigationRoute = reportID ? ROUTES.SEARCH_REPORT.getRoute({reportID, backTo}) : undefined; + } const isCurrentRoomMention = reportID === currentReportIDValue; const flattenStyle = StyleSheet.flatten(style as TextStyle); From 9cc3ab46ec30f8a58dd81714703d669fb94e6b51 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 29 Oct 2024 15:53:19 +0100 Subject: [PATCH 041/108] Fix company cards display --- src/pages/settings/Wallet/PaymentMethodList.tsx | 16 +++++++++------- .../members/WorkspaceMemberDetailsPage.tsx | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 23d0b5ab6550..652beb921d31 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -9,8 +9,6 @@ import type {ValueOf} from 'type-fest'; import type {RenderSuggestionMenuItemProps} from '@components/AutoCompleteSuggestions/types'; import Button from '@components/Button'; import FormAlertWrapper from '@components/FormAlertWrapper'; -import getBankIcon from '@components/Icon/BankIcons'; -import type {BankName} from '@components/Icon/BankIconsUtils'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -30,7 +28,7 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {AccountData} from '@src/types/onyx'; +import type {AccountData, CompanyCardFeed} from '@src/types/onyx'; import type {BankIcon} from '@src/types/onyx/Bank'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type PaymentMethod from '@src/types/onyx/PaymentMethod'; @@ -223,12 +221,12 @@ function PaymentMethodList({ const assignedCardsGrouped: PaymentMethodItem[] = []; assignedCardsSorted.forEach((card) => { - const icon = getBankIcon({bankName: card.bank as BankName, isCard: true, styles}); + const icon = CardUtils.getCardFeedIcon(card.bank as CompanyCardFeed); if (!CardUtils.isExpensifyCard(card.cardID)) { assignedCardsGrouped.push({ key: card.cardID.toString(), - title: card.bank, + title: card.cardName, description: getDescriptionForPolicyDomainCard(card.domainName), shouldShowRightIcon: false, interactive: false, @@ -238,7 +236,9 @@ function PaymentMethodList({ card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - ...icon, + icon, + iconStyles: [styles.assignedCardsIconContainer], + iconSize: variables.iconSizeExtraLarge, }); return; } @@ -275,7 +275,9 @@ function PaymentMethodList({ card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - ...icon, + icon, + iconStyles: [styles.assignedCardsIconContainer], + iconSize: variables.iconSizeExtraLarge, }); }); return assignedCardsGrouped; diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 8f5c9b2994cd..2a6b9a16827c 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -316,8 +316,8 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM displayInDefaultIconColor iconStyles={styles.cardIcon} contentFit="contain" - iconWidth={variables.cardIconWidth} - iconHeight={variables.cardIconHeight} + iconWidth={variables.iconSizeExtraLarge} + iconHeight={variables.iconSizeExtraLarge} onPress={() => navigateToDetails(memberCard)} shouldShowRightIcon /> From 37dbfa8899ed1346bd4fb59f9499e1a5063e2a72 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 29 Oct 2024 16:26:31 +0100 Subject: [PATCH 042/108] Android fix --- src/pages/settings/Wallet/PaymentMethodList.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 652beb921d31..676083f31004 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -277,7 +277,8 @@ function PaymentMethodList({ : undefined, icon, iconStyles: [styles.assignedCardsIconContainer], - iconSize: variables.iconSizeExtraLarge, + iconWidth: variables.bankCardWidth, + iconHeight: variables.bankCardHeight, }); }); return assignedCardsGrouped; From 370bf5aa0907530eeb70016d17e0bc4f7510f751 Mon Sep 17 00:00:00 2001 From: Anusha Date: Tue, 29 Oct 2024 23:48:02 +0500 Subject: [PATCH 043/108] fix type --- .../HTMLRenderers/MentionReportRenderer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx index 7de9cb230e19..70aa39f2722d 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx @@ -72,7 +72,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, ...defaultRender } const {reportID, mentionDisplayText} = mentionDetails; - let navigationRoute: any = reportID ? ROUTES.REPORT_WITH_ID.getRoute(reportID) : undefined; + let navigationRoute: string | undefined = reportID ? ROUTES.REPORT_WITH_ID.getRoute(reportID) : undefined; const topmostCentralPaneRoute = getTopmostCentralPaneRoute(navigationRef.getRootState() as State); const backTo = Navigation.getActiveRoute(); if (topmostCentralPaneRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { From 8c2aa8a3f8d04b0a22bb54c24828aeb74a23156c Mon Sep 17 00:00:00 2001 From: Anusha Date: Tue, 29 Oct 2024 23:50:11 +0500 Subject: [PATCH 044/108] fix imports --- .../HTMLRenderers/MentionReportRenderer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx index 70aa39f2722d..c1b75cc62e87 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx @@ -11,7 +11,7 @@ import useCurrentReportID from '@hooks/useCurrentReportID'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; -import {RootStackParamList, State} from '@libs/Navigation/types'; +import type {RootStackParamList, State} from '@libs/Navigation/types'; import Navigation, {navigationRef} from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From 494afe1969bb42555cf35588cc6ea4cdb95c83f6 Mon Sep 17 00:00:00 2001 From: Anusha Date: Wed, 30 Oct 2024 00:02:35 +0500 Subject: [PATCH 045/108] type error fixed --- .../HTMLRenderers/MentionReportRenderer/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx index c1b75cc62e87..94a46d861dde 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/index.tsx @@ -16,6 +16,7 @@ import Navigation, {navigationRef} from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -72,7 +73,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, ...defaultRender } const {reportID, mentionDisplayText} = mentionDetails; - let navigationRoute: string | undefined = reportID ? ROUTES.REPORT_WITH_ID.getRoute(reportID) : undefined; + let navigationRoute: Route | undefined = reportID ? ROUTES.REPORT_WITH_ID.getRoute(reportID) : undefined; const topmostCentralPaneRoute = getTopmostCentralPaneRoute(navigationRef.getRootState() as State); const backTo = Navigation.getActiveRoute(); if (topmostCentralPaneRoute?.name === SCREENS.SEARCH.CENTRAL_PANE) { From f0f0685949b8ce6ba79cb8b28dbd2b0b8e6282a6 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 30 Oct 2024 13:13:34 +0700 Subject: [PATCH 046/108] fix: show newly added feed as last selected feed --- src/libs/actions/CompanyCards.ts | 28 +++++++++++++++++-- .../companyCards/addNew/DetailsStep.tsx | 3 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index a106fbeff510..2955a62f28c7 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -51,13 +51,37 @@ function clearAddNewCardFlow() { }); } -function addNewCompanyCardsFeed(policyID: string, feedType: string, feedDetails: string) { +function addNewCompanyCardsFeed(policyID: string, feedType: CompanyCardFeed, feedDetails: string, lastSelectedFeed?: CompanyCardFeed) { const authToken = NetworkStore.getAuthToken(); if (!authToken) { return; } + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, + value: feedType, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, + value: lastSelectedFeed ?? null, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`, + value: feedType, + }, + ]; + const parameters: RequestFeedSetupParams = { policyID, authToken, @@ -65,7 +89,7 @@ function addNewCompanyCardsFeed(policyID: string, feedType: string, feedDetails: feedDetails, }; - API.write(WRITE_COMMANDS.REQUEST_FEED_SETUP, parameters); + API.write(WRITE_COMMANDS.REQUEST_FEED_SETUP, parameters, {optimisticData, failureData, successData}); } function setWorkspaceCompanyCardFeedName(policyID: string, workspaceAccountID: number, bankName: string, userDefinedName: string) { diff --git a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx index 2c719acd47d8..ae975135505e 100644 --- a/src/pages/workspace/companyCards/addNew/DetailsStep.tsx +++ b/src/pages/workspace/companyCards/addNew/DetailsStep.tsx @@ -31,6 +31,7 @@ function DetailsStep({policyID}: DetailsStepProps) { const {inputCallbackRef} = useAutoFocusInput(); const {canUseDirectFeeds} = usePermissions(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD); + const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); const feedProvider = addNewCard?.data?.feedType; const isStripeFeedProvider = feedProvider === CONST.COMPANY_CARD.FEED_BANK_NAME.STRIPE; const bank = addNewCard?.data?.selectedBank; @@ -48,7 +49,7 @@ function DetailsStep({policyID}: DetailsStepProps) { .map(([key, value]) => `${key}: ${value}`) .join(', '); - CompanyCards.addNewCompanyCardsFeed(policyID, addNewCard.data.feedType, feedDetails); + CompanyCards.addNewCompanyCardsFeed(policyID, addNewCard.data.feedType, feedDetails, lastSelectedFeed); Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policyID)); }; From c399ae18b9caa82fa58f376e9ce62b2e2ad43b28 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 30 Oct 2024 16:49:53 +0700 Subject: [PATCH 047/108] fix: minor change --- .../companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index efd95ecb8980..25d9f62d6d32 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -37,7 +37,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const shouldChangeLayout = isMediumScreenWidth || shouldUseNarrowLayout; - const feedName = cardFeeds?.settings?.companyCardNicknames?.[selectedFeed] ?? CardUtils.getCardFeedName(selectedFeed); + const feedName = CardUtils.getCardFeedName(selectedFeed); const isCustomFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD === selectedFeed || CONST.COMPANY_CARD.FEED_BANK_NAME.VISA === selectedFeed || CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX === selectedFeed; From 84f9960c8f0c0be6a2fb357773fa3b8855f55d22 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 30 Oct 2024 17:01:31 +0700 Subject: [PATCH 048/108] fix: change conditions to prevent card selector from crashing --- .../companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx | 4 +++- .../workspace/companyCards/WorkspaceCompanyCardsPage.tsx | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx index 25d9f62d6d32..4fc6ac35881b 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsListHeaderButtons.tsx @@ -20,6 +20,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {CompanyCardFeed} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WorkspaceCompanyCardsListHeaderButtonsProps = { /** Current policy id */ @@ -40,6 +41,7 @@ function WorkspaceCompanyCardsListHeaderButtons({policyID, selectedFeed}: Worksp const feedName = CardUtils.getCardFeedName(selectedFeed); const isCustomFeed = CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD === selectedFeed || CONST.COMPANY_CARD.FEED_BANK_NAME.VISA === selectedFeed || CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX === selectedFeed; + const currentFeedData = cardFeeds?.settings?.companyCards?.[selectedFeed] ?? {pending: true, errors: {}}; return (