From ec50a62af1ccfcda7df227f2efbedab5861ab590 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 3 Apr 2024 17:25:10 +0200 Subject: [PATCH 001/565] fix BaseGenericPressable defocusing after press --- .../Pressable/GenericPressable/BaseGenericPressable.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx index 9dda35f41e25..d7c7b922258c 100644 --- a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx +++ b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx @@ -85,12 +85,11 @@ function GenericPressable( if (shouldUseHapticsOnLongPress) { HapticFeedback.longPress(); } - if (ref && 'current' in ref) { + if (ref && 'current' in ref && nextFocusRef) { ref.current?.blur(); + Accessibility.moveAccessibilityFocus(nextFocusRef); } onLongPress(event); - - Accessibility.moveAccessibilityFocus(nextFocusRef); }, [shouldUseHapticsOnLongPress, onLongPress, nextFocusRef, ref, isDisabled], ); @@ -106,11 +105,11 @@ function GenericPressable( if (shouldUseHapticsOnPress) { HapticFeedback.press(); } - if (ref && 'current' in ref) { + if (ref && 'current' in ref && nextFocusRef) { ref.current?.blur(); + Accessibility.moveAccessibilityFocus(nextFocusRef); } const onPressResult = onPress(event); - Accessibility.moveAccessibilityFocus(nextFocusRef); return onPressResult; }, [shouldUseHapticsOnPress, onPress, nextFocusRef, ref, isDisabled], From 623f6cab3548c6e08b1ed56bc9110e3568193824 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 3 Apr 2024 17:25:53 +0200 Subject: [PATCH 002/565] add focus-trap-react package --- package-lock.json | 28 ++++++++++++++++++++++++++++ package.json | 3 ++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index e50cff0dbf27..a9a67f12a508 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,6 +55,7 @@ "expo-av": "~13.10.4", "expo-image": "1.11.0", "expo-image-manipulator": "11.8.0", + "focus-trap-react": "^10.2.3", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", "jest-expo": "50.0.1", @@ -28487,6 +28488,28 @@ "readable-stream": "^2.3.6" } }, + "node_modules/focus-trap": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/focus-trap-react": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/focus-trap-react/-/focus-trap-react-10.2.3.tgz", + "integrity": "sha512-YXBpFu/hIeSu6NnmV2xlXzOYxuWkoOtar9jzgp3lOmjWLWY59C/b8DtDHEAV4SPU07Nd/t+nS/SBNGkhUBFmEw==", + "dependencies": { + "focus-trap": "^7.5.4", + "tabbable": "^6.2.0" + }, + "peerDependencies": { + "prop-types": "^15.8.1", + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, "node_modules/follow-redirects": { "version": "1.15.5", "funding": [ @@ -43419,6 +43442,11 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/table": { "version": "6.8.1", "dev": true, diff --git a/package.json b/package.json index d600f2dfc1bf..f11f25dcaafe 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "expo-av": "~13.10.4", "expo-image": "1.11.0", "expo-image-manipulator": "11.8.0", + "focus-trap-react": "^10.2.3", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", "jest-expo": "50.0.1", @@ -248,8 +249,8 @@ "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-remove-console": "^6.9.4", "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^10.1.0", "concurrently": "^8.2.2", + "copy-webpack-plugin": "^10.1.0", "css-loader": "^6.7.2", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", From 770035987c28c14e5530a8b4034a7e01991b9c92 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 3 Apr 2024 17:26:21 +0200 Subject: [PATCH 003/565] add focus trap for screens and popovers --- .../FocusTrap/BOTTOM_TAB_SCREENS.ts | 5 ++ .../FocusTrapForModalProps.ts | 6 ++ .../FocusTrap/FocusTrapForModal/index.tsx | 9 +++ .../FocusTrap/FocusTrapForModal/index.web.tsx | 23 ++++++ .../FocusTrapForScreen/FocusTrapProps.ts | 5 ++ .../FocusTrap/FocusTrapForScreen/index.tsx | 9 +++ .../FocusTrapForScreen/index.web.tsx | 68 ++++++++++++++++ .../FocusTrap/SCREENS_WITH_AUTOFOCUS.ts | 15 ++++ .../FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts | 36 +++++++++ src/components/FocusTrap/sharedTrapStack.ts | 5 ++ src/components/PopoverMenu.tsx | 61 +++++++------- src/components/ScreenWrapper.tsx | 81 ++++++++++--------- .../Navigators/FullScreenNavigator.tsx | 31 +++---- 13 files changed, 272 insertions(+), 82 deletions(-) create mode 100644 src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts create mode 100644 src/components/FocusTrap/FocusTrapForModal/FocusTrapForModalProps.ts create mode 100644 src/components/FocusTrap/FocusTrapForModal/index.tsx create mode 100644 src/components/FocusTrap/FocusTrapForModal/index.web.tsx create mode 100644 src/components/FocusTrap/FocusTrapForScreen/FocusTrapProps.ts create mode 100644 src/components/FocusTrap/FocusTrapForScreen/index.tsx create mode 100644 src/components/FocusTrap/FocusTrapForScreen/index.web.tsx create mode 100644 src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts create mode 100644 src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts create mode 100644 src/components/FocusTrap/sharedTrapStack.ts diff --git a/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts b/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts new file mode 100644 index 000000000000..b62c3b563594 --- /dev/null +++ b/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts @@ -0,0 +1,5 @@ +import SCREENS from '@src/SCREENS'; + +const BOTTOM_TAB_SCREENS: string[] = [SCREENS.HOME, SCREENS.SETTINGS.ROOT]; + +export default BOTTOM_TAB_SCREENS; diff --git a/src/components/FocusTrap/FocusTrapForModal/FocusTrapForModalProps.ts b/src/components/FocusTrap/FocusTrapForModal/FocusTrapForModalProps.ts new file mode 100644 index 000000000000..6bc2350a6c55 --- /dev/null +++ b/src/components/FocusTrap/FocusTrapForModal/FocusTrapForModalProps.ts @@ -0,0 +1,6 @@ +type FocusTrapForModalProps = { + children: React.ReactNode; + active: boolean; +}; + +export default FocusTrapForModalProps; diff --git a/src/components/FocusTrap/FocusTrapForModal/index.tsx b/src/components/FocusTrap/FocusTrapForModal/index.tsx new file mode 100644 index 000000000000..01632998b079 --- /dev/null +++ b/src/components/FocusTrap/FocusTrapForModal/index.tsx @@ -0,0 +1,9 @@ +import type FocusTrapForModalProps from './FocusTrapForModalProps'; + +function FocusTrapForModal({children}: FocusTrapForModalProps) { + return children; +} + +FocusTrapForModal.displayName = 'FocusTrapForModal'; + +export default FocusTrapForModal; diff --git a/src/components/FocusTrap/FocusTrapForModal/index.web.tsx b/src/components/FocusTrap/FocusTrapForModal/index.web.tsx new file mode 100644 index 000000000000..00fa87d3a38d --- /dev/null +++ b/src/components/FocusTrap/FocusTrapForModal/index.web.tsx @@ -0,0 +1,23 @@ +import FocusTrapOriginal from 'focus-trap-react'; +import React from 'react'; +import sharedTrapStack from '@components/FocusTrap/sharedTrapStack'; +import type FocusTrapForModalProps from './FocusTrapForModalProps'; + +function FocusTrapForModal({children, active}: FocusTrapForModalProps) { + return ( + + {children} + + ); +} + +FocusTrapForModal.displayName = 'FocusTrapForModal'; + +export default FocusTrapForModal; diff --git a/src/components/FocusTrap/FocusTrapForScreen/FocusTrapProps.ts b/src/components/FocusTrap/FocusTrapForScreen/FocusTrapProps.ts new file mode 100644 index 000000000000..d2f6e5323445 --- /dev/null +++ b/src/components/FocusTrap/FocusTrapForScreen/FocusTrapProps.ts @@ -0,0 +1,5 @@ +type FocusTrapForScreenProps = { + children: React.ReactNode; +}; + +export default FocusTrapForScreenProps; diff --git a/src/components/FocusTrap/FocusTrapForScreen/index.tsx b/src/components/FocusTrap/FocusTrapForScreen/index.tsx new file mode 100644 index 000000000000..58cb53448706 --- /dev/null +++ b/src/components/FocusTrap/FocusTrapForScreen/index.tsx @@ -0,0 +1,9 @@ +import type FocusTrapProps from './FocusTrapProps'; + +function FocusTrapView({children}: FocusTrapProps) { + return children; +} + +FocusTrapView.displayName = 'FocusTrapView'; + +export default FocusTrapView; diff --git a/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx new file mode 100644 index 000000000000..2e9a59096819 --- /dev/null +++ b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx @@ -0,0 +1,68 @@ +import {useFocusEffect, useIsFocused, useRoute} from '@react-navigation/native'; +import FocusTrapOriginal from 'focus-trap-react'; +import React, {useMemo, useRef} from 'react'; +import BOTTOM_TAB_SCREENS from '@components/FocusTrap/BOTTOM_TAB_SCREENS'; +import SCREENS_WITH_AUTOFOCUS from '@components/FocusTrap/SCREENS_WITH_AUTOFOCUS'; +import sharedTrapStack from '@components/FocusTrap/sharedTrapStack'; +import WIDE_LAYOUT_INACTIVE_SCREENS from '@components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import type FocusTrapProps from './FocusTrapProps'; + +let activeRouteName = ''; + +function FocusTrap({children}: FocusTrapProps) { + const isFocused = useIsFocused(); + const route = useRoute(); + const {isSmallScreenWidth} = useWindowDimensions(); + + const isActive = useMemo(() => { + // Focus trap can't be active on bottom tab screens because it would block access to the tab bar. + if (BOTTOM_TAB_SCREENS.includes(route.name)) { + return false; + } + + // Focus trap can't be active on these screens if the layout is wide because they may be displayed side by side. + if (WIDE_LAYOUT_INACTIVE_SCREENS.includes(route.name) && !isSmallScreenWidth) { + return false; + } + return true; + }, [isSmallScreenWidth, route]); + + useFocusEffect(() => { + activeRouteName = route.name; + }); + + const focusTrapRef = useRef(null); + + return ( + { + if (SCREENS_WITH_AUTOFOCUS.includes(activeRouteName)) { + return false; + } + return undefined; + }, + setReturnFocus: (element) => { + if (SCREENS_WITH_AUTOFOCUS.includes(activeRouteName)) { + return false; + } + return element; + }, + }} + > + {children} + + ); +} + +FocusTrap.displayName = 'FocusTrapView'; + +export default FocusTrap; diff --git a/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts b/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts new file mode 100644 index 000000000000..439c45a8bd00 --- /dev/null +++ b/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts @@ -0,0 +1,15 @@ +import SCREENS from '@src/SCREENS'; + +const SCREENS_WITH_AUTOFOCUS: string[] = [ + SCREENS.WORKSPACE_SWITCHER.ROOT, + SCREENS.SEARCH_ROOT, + SCREENS.REPORT, + SCREENS.REPORT_DESCRIPTION_ROOT, + SCREENS.PRIVATE_NOTES.EDIT, + SCREENS.SETTINGS.PROFILE.STATUS, + SCREENS.SETTINGS.PROFILE.PRONOUNS, + SCREENS.NEW_TASK.DETAILS, + SCREENS.MONEY_REQUEST.CREATE, +]; + +export default SCREENS_WITH_AUTOFOCUS; diff --git a/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts b/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts new file mode 100644 index 000000000000..6815dcc455a9 --- /dev/null +++ b/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts @@ -0,0 +1,36 @@ +import NAVIGATORS from '@src/NAVIGATORS'; +import SCREENS from '@src/SCREENS'; + +const WIDE_LAYOUT_INACTIVE_SCREENS: string[] = [ + NAVIGATORS.BOTTOM_TAB_NAVIGATOR, + SCREENS.HOME, + SCREENS.SETTINGS.ROOT, + SCREENS.REPORT, + SCREENS.SETTINGS.PROFILE.ROOT, + SCREENS.SETTINGS.PREFERENCES.ROOT, + SCREENS.SETTINGS.SECURITY, + SCREENS.SETTINGS.WALLET.ROOT, + SCREENS.SETTINGS.ABOUT, + SCREENS.SETTINGS.WORKSPACES, + + SCREENS.WORKSPACE.INITIAL, + + SCREENS.WORKSPACE.PROFILE, + SCREENS.WORKSPACE.CARD, + SCREENS.WORKSPACE.WORKFLOWS, + SCREENS.WORKSPACE.WORKFLOWS_APPROVER, + SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY, + SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET, + SCREENS.WORKSPACE.REIMBURSE, + SCREENS.WORKSPACE.BILLS, + SCREENS.WORKSPACE.INVOICES, + SCREENS.WORKSPACE.TRAVEL, + SCREENS.WORKSPACE.MEMBERS, + SCREENS.WORKSPACE.CATEGORIES, + SCREENS.WORKSPACE.MORE_FEATURES, + SCREENS.WORKSPACE.TAGS, + SCREENS.WORKSPACE.TAXES, + SCREENS.WORKSPACE.DISTANCE_RATES, +]; + +export default WIDE_LAYOUT_INACTIVE_SCREENS; diff --git a/src/components/FocusTrap/sharedTrapStack.ts b/src/components/FocusTrap/sharedTrapStack.ts new file mode 100644 index 000000000000..9ee213c111fc --- /dev/null +++ b/src/components/FocusTrap/sharedTrapStack.ts @@ -0,0 +1,5 @@ +import type {FocusTrap as FocusTrapHandler} from 'focus-trap'; + +const trapStack: FocusTrapHandler[] = []; + +export default trapStack; diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 1fd1c8ef5a3b..51bed834579a 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -9,6 +9,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; import type {AnchorPosition} from '@src/styles'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; +import FocusTrapForModal from './FocusTrap/FocusTrapForModal'; import * as Expensicons from './Icon/Expensicons'; import type {MenuItemProps} from './MenuItem'; import MenuItem from './MenuItem'; @@ -189,35 +190,37 @@ function PopoverMenu({ withoutOverlay={withoutOverlay} shouldSetModalVisibility={shouldSetModalVisibility} > - - {!!headerText && {headerText}} - {enteredSubMenuIndexes.length > 0 && renderBackButtonItem()} - {currentMenuItems.map((item, menuIndex) => ( - selectItem(menuIndex)} - focused={focusedIndex === menuIndex} - displayInDefaultIconColor={item.displayInDefaultIconColor} - shouldShowRightIcon={item.shouldShowRightIcon} - shouldPutLeftPaddingWhenNoIcon={item.shouldPutLeftPaddingWhenNoIcon} - label={item.label} - isLabelHoverable={item.isLabelHoverable} - floatRightAvatars={item.floatRightAvatars} - floatRightAvatarSize={item.floatRightAvatarSize} - shouldShowSubscriptRightAvatar={item.shouldShowSubscriptRightAvatar} - disabled={item.disabled} - /> - ))} - + + + {!!headerText && {headerText}} + {enteredSubMenuIndexes.length > 0 && renderBackButtonItem()} + {currentMenuItems.map((item, menuIndex) => ( + selectItem(menuIndex)} + focused={focusedIndex === menuIndex} + displayInDefaultIconColor={item.displayInDefaultIconColor} + shouldShowRightIcon={item.shouldShowRightIcon} + shouldPutLeftPaddingWhenNoIcon={item.shouldPutLeftPaddingWhenNoIcon} + label={item.label} + isLabelHoverable={item.isLabelHoverable} + floatRightAvatars={item.floatRightAvatars} + floatRightAvatarSize={item.floatRightAvatarSize} + shouldShowSubscriptRightAvatar={item.shouldShowSubscriptRightAvatar} + disabled={item.disabled} + /> + ))} + + ); } diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index b78e274371ca..8ca5877e1d51 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -18,6 +18,7 @@ import type {CentralPaneNavigatorParamList, RootStackParamList} from '@libs/Navi import toggleTestToolsModal from '@userActions/TestTool'; import CONST from '@src/CONST'; import CustomDevMenu from './CustomDevMenu'; +import FocusTrapForScreens from './FocusTrap/FocusTrapForScreen'; import HeaderGap from './HeaderGap'; import KeyboardAvoidingView from './KeyboardAvoidingView'; import OfflineIndicator from './OfflineIndicator'; @@ -227,51 +228,53 @@ function ScreenWrapper( } return ( - + - - - - - {isDevelopment && } - { - // If props.children is a function, call it to provide the insets to the children. - typeof children === 'function' - ? children({ - insets, - safeAreaPaddingBottomStyle, - didScreenTransitionEnd, - }) - : children - } - {isSmallScreenWidth && shouldShowOfflineIndicator && } - {!isSmallScreenWidth && shouldShowOfflineIndicatorInWideScreen && ( - - )} - - + + + + {isDevelopment && } + { + // If props.children is a function, call it to provide the insets to the children. + typeof children === 'function' + ? children({ + insets, + safeAreaPaddingBottomStyle, + didScreenTransitionEnd, + }) + : children + } + {isSmallScreenWidth && shouldShowOfflineIndicator && } + {!isSmallScreenWidth && shouldShowOfflineIndicatorInWideScreen && ( + + )} + + + - + ); }} diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 07b069462dd1..7631115f82b5 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -19,20 +20,22 @@ function FullScreenNavigator() { const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth, styles, StyleUtils); return ( - - - - - - + + + + + + + + ); } From ee852a4fefb9e8c06253929b05544420c2d54237 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:17:08 +0200 Subject: [PATCH 004/565] fix back button --- src/CONST.ts | 2 ++ src/components/HeaderWithBackButton/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index e7358b382f14..45fe2018e9b0 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3161,6 +3161,8 @@ const CONST = { TIMER: 'timer', /** Use for toolbars containing action buttons or components. */ TOOLBAR: 'toolbar', + /** Use for navigation elements */ + NAVIGATION: 'navigation', }, TRANSLATION_KEYS: { ATTACHMENT: 'common.attachment', diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index f3293596aa46..6e6722744376 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -6,6 +6,7 @@ import Header from '@components/Header'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PinButton from '@components/PinButton'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; import Tooltip from '@components/Tooltip'; @@ -100,7 +101,7 @@ function HeaderWithBackButton({ } }} style={[styles.touchableButtonImage]} - role="button" + role={CONST.ROLE.NAVIGATION} accessibilityLabel={translate('common.back')} nativeID={CONST.BACK_BUTTON_NATIVE_ID} > From 05f1ad6d0d50a45b188c4d8fccd8f72788205fc6 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 11 Apr 2024 19:19:37 +0800 Subject: [PATCH 005/565] navigate back when delete modal hides --- src/components/MoneyReportHeader.tsx | 18 +++++++++++++----- src/components/MoneyRequestHeader.tsx | 17 +++++++++++++---- src/libs/actions/IOU.ts | 7 +++---- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 14227d6a2051..5d68440266f3 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -13,7 +13,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, { Route } from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -83,6 +83,8 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); + const navigateBackToAfterDelete = useRef(); + const cancelPayment = useCallback(() => { if (!chatReport) { return; @@ -136,10 +138,10 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money if (requestParentReportAction) { const iouTransactionID = requestParentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? requestParentReportAction.originalMessage?.IOUTransactionID ?? '' : ''; if (ReportActionsUtils.isTrackExpenseAction(requestParentReportAction)) { - IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, true); - return; + navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, true); + } else { + navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, true); } - IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, true); } setIsDeleteRequestModalVisible(false); @@ -292,6 +294,12 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money isVisible={isDeleteRequestModalVisible} onConfirm={deleteTransaction} onCancel={() => setIsDeleteRequestModalVisible(false)} + onModalHide={() => { + if (!navigateBackToAfterDelete.current) { + return; + } + Navigation.goBack(navigateBackToAfterDelete.current) + }} prompt={translate('iou.deleteConfirmation')} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index f451f5f15581..2aea42d46503 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useState} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -14,6 +14,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type {Policy, Report, ReportAction, ReportActions, Session, Transaction} from '@src/types/onyx'; import type {OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import ConfirmModal from './ConfirmModal'; @@ -63,6 +64,8 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, const isOnHold = TransactionUtils.isOnHold(transaction); const {isSmallScreenWidth, windowWidth} = useWindowDimensions(); + const navigateBackToAfterDelete = useRef(); + // Only the requestor can take delete the request, admins can only edit it. const isActionOwner = typeof parentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && parentReportAction.actorAccountID === session?.accountID; const isPolicyAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; @@ -72,10 +75,10 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, if (parentReportAction) { const iouTransactionID = parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction.originalMessage?.IOUTransactionID ?? '' : ''; if (ReportActionsUtils.isTrackExpenseAction(parentReportAction)) { - IOU.deleteTrackExpense(parentReport?.reportID ?? '', iouTransactionID, parentReportAction, true); - return; + navigateBackToAfterDelete.current = IOU.deleteTrackExpense(parentReport?.reportID ?? '', iouTransactionID, parentReportAction, true); + } else { + navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, parentReportAction, true); } - IOU.deleteMoneyRequest(iouTransactionID, parentReportAction, true); } setIsDeleteModalVisible(false); @@ -200,6 +203,12 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, isVisible={isDeleteModalVisible} onConfirm={deleteTransaction} onCancel={() => setIsDeleteModalVisible(false)} + onModalHide={() => { + if (!navigateBackToAfterDelete.current) { + return; + } + Navigation.goBack(navigateBackToAfterDelete.current) + }} prompt={translate('iou.deleteConfirmation')} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c2d462bbc4a8..ab50e709a972 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4133,13 +4133,12 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor // STEP 7: Navigate the user depending on which page they are on and which resources were deleted if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. - Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID)); - return; + return ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID); } if (iouReport?.chatReportID && shouldDeleteIOUReport) { // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. - Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID)); + return ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID); } } @@ -4304,7 +4303,7 @@ function deleteTrackExpense(chatReportID: string, transactionID: string, reportA // STEP 7: Navigate the user depending on which page they are on and which resources were deleted if (isSingleTransactionView && shouldDeleteTransactionThread) { // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. - Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(chatReport?.reportID ?? '')); + return ROUTES.REPORT_WITH_ID.getRoute(chatReport?.reportID ?? ''); } } From 3b45b392f04b677ca39a59e4b90f3c206c3d5293 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 17 Apr 2024 15:16:23 +0700 Subject: [PATCH 006/565] animate sign-in pages --- assets/animations/Abracadabra.lottie | Bin 0 -> 15694 bytes assets/animations/MagicCode.lottie | Bin 0 -> 21405 bytes src/components/LottieAnimations/index.tsx | 10 ++++++++++ .../ValidateCode/JustSignedInModal.tsx | 13 ++++++++----- .../ValidateCode/ValidateCodeModal.tsx | 13 ++++++++----- src/styles/index.ts | 9 +++++++++ 6 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 assets/animations/Abracadabra.lottie create mode 100644 assets/animations/MagicCode.lottie diff --git a/assets/animations/Abracadabra.lottie b/assets/animations/Abracadabra.lottie new file mode 100644 index 0000000000000000000000000000000000000000..8805aed1944e6a00a0ebcb33496f36a122e9df37 GIT binary patch literal 15694 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 WRgwjV_=g4L@0s)Wp2hc1?*9OB|0t3G literal 0 HcmV?d00001 diff --git a/assets/animations/MagicCode.lottie b/assets/animations/MagicCode.lottie new file mode 100644 index 0000000000000000000000000000000000000000..ea94f1138f97e960e9254b08c8176590cce7a9dd GIT binary patch literal 21405 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 UU{th!O+o(a*?;4q#=o-v0Wb!wc>n+a literal 0 HcmV?d00001 diff --git a/src/components/LottieAnimations/index.tsx b/src/components/LottieAnimations/index.tsx index 18cb9188d60c..c4c76ec8cc95 100644 --- a/src/components/LottieAnimations/index.tsx +++ b/src/components/LottieAnimations/index.tsx @@ -3,6 +3,11 @@ import variables from '@styles/variables'; import type DotLottieAnimation from './types'; const DotLottieAnimations = { + Abracadabra: { + file: require('@assets/animations/Abracadabra.lottie'), + w: 375, + h: 400, + }, FastMoney: { file: require('@assets/animations/FastMoney.lottie'), w: 375, @@ -46,6 +51,11 @@ const DotLottieAnimations = { h: 400, backgroundColor: colors.ice500, }, + MagicCode: { + file: require('@assets/animations/MagicCode.lottie'), + w: 200, + h: 164, + }, Magician: { file: require('@assets/animations/Magician.lottie'), w: 853, diff --git a/src/components/ValidateCode/JustSignedInModal.tsx b/src/components/ValidateCode/JustSignedInModal.tsx index 19e67b0c56fe..923ea14a3f51 100644 --- a/src/components/ValidateCode/JustSignedInModal.tsx +++ b/src/components/ValidateCode/JustSignedInModal.tsx @@ -2,7 +2,8 @@ import React from 'react'; import {View} from 'react-native'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; -import * as Illustrations from '@components/Icon/Illustrations'; +import Lottie from '@components/Lottie'; +import LottieAnimations from '@components/LottieAnimations'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; @@ -22,10 +23,12 @@ function JustSignedInModal({is2FARequired}: JustSignedInModalProps) { - diff --git a/src/components/ValidateCode/ValidateCodeModal.tsx b/src/components/ValidateCode/ValidateCodeModal.tsx index 1e42773c2dc2..07fc8cb7167e 100644 --- a/src/components/ValidateCode/ValidateCodeModal.tsx +++ b/src/components/ValidateCode/ValidateCodeModal.tsx @@ -4,7 +4,8 @@ import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; -import * as Illustrations from '@components/Icon/Illustrations'; +import Lottie from '@components/Lottie'; +import LottieAnimations from '@components/LottieAnimations'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; @@ -37,10 +38,12 @@ function ValidateCodeModal({code, accountID, session = {}}: ValidateCodeModalPro - {translate('validateCodeModal.title')} diff --git a/src/styles/index.ts b/src/styles/index.ts index 537038d9f2e1..fee143b2a95c 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4294,6 +4294,15 @@ const styles = (theme: ThemeColors) => borderColor: theme.highlightBG, borderWidth: 2, }, + + magicCodeAnimationWidth: { + width: variables.modalTopIconWidth, + }, + + justSignedInModalAnimationHeight: (is2FARequired: boolean) => ({ + height: is2FARequired ? variables.modalTopIconHeight : variables.modalTopBigIconHeight, + }), + moneyRequestViewImage: { ...spacing.mh5, ...spacing.mv3, From 1e0e916ffc25a7573657d1680f3c859fe6ddb326 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 17 Apr 2024 21:49:04 +0200 Subject: [PATCH 007/565] feat: verify identity step setup --- .../VerifyIdentity/VerifyIdentity.tsx | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx diff --git a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx new file mode 100644 index 000000000000..f402f52255ab --- /dev/null +++ b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx @@ -0,0 +1,120 @@ +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; +import Onfido from '@components/Onfido'; +import type {OnfidoData} from '@components/Onfido/types'; +import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Growl from '@libs/Growl'; +import * as BankAccounts from '@userActions/BankAccounts'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {PersonalBankAccount, WalletOnfido} from '@src/types/onyx'; + +const DEFAULT_WALLET_ONFIDO_DATA = { + loading: false, + hasAcceptedPrivacyPolicy: false, +}; + +type VerifyIdentityOnyxProps = { + /** Reimbursement account from ONYX */ + personalBankAccount: OnyxEntry; + + /** Onfido applicant ID from ONYX */ + onfidoApplicantID: OnyxEntry; + + /** The token required to initialize the Onfido SDK */ + onfidoToken: OnyxEntry; + + /** The wallet onfido data */ + walletOnfidoData: OnyxEntry; +}; + +type VerifyIdentityProps = VerifyIdentityOnyxProps & { + /** Goes to the previous step */ + onBackButtonPress: () => void; +}; + +const ONFIDO_ERROR_DISPLAY_DURATION = 10000; + +function VerifyIdentity({personalBankAccount, onBackButtonPress, onfidoApplicantID, onfidoToken, walletOnfidoData = DEFAULT_WALLET_ONFIDO_DATA}: VerifyIdentityProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const handleOnfidoSuccess = useCallback( + (onfidoData: OnfidoData) => { + BankAccounts.verifyIdentity({ + onfidoData: JSON.stringify({ + ...onfidoData, + applicantID: walletOnfidoData?.applicantID, + }), + }); + BankAccounts.updateAddPersonalBankAccountDraft({isOnfidoSetupComplete: true}); + }, + [personalBankAccount, onfidoApplicantID], + ); + + const handleOnfidoError = () => { + // In case of any unexpected error we log it to the server, show a growl, and return the user back to the requestor step so they can try again. + Growl.error(translate('onfidoStep.genericError'), ONFIDO_ERROR_DISPLAY_DURATION); + BankAccounts.clearOnfidoToken(); + // BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); + }; + + const handleOnfidoUserExit = () => { + BankAccounts.clearOnfidoToken(); + // BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); + }; + + return ( + + + + + + + + + + + + ); +} + +VerifyIdentity.displayName = 'VerifyIdentity'; + +export default withOnyx({ + // @ts-expect-error: ONYXKEYS.PERSONAL_BANK_ACCOUNT is conflicting with ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_FORM + personalBankAccount: { + key: ONYXKEYS.PERSONAL_BANK_ACCOUNT, + }, + onfidoApplicantID: { + key: ONYXKEYS.ONFIDO_APPLICANT_ID, + }, + onfidoToken: { + key: ONYXKEYS.ONFIDO_TOKEN, + }, + walletOnfidoData: { + key: ONYXKEYS.WALLET_ONFIDO, + + // Let's get a new onfido token each time the user hits this flow (as it should only be once) + initWithStoredValues: false, + }, +})(VerifyIdentity); From f7275e04e6d5aa07438760e51f1532fd165423df Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Fri, 19 Apr 2024 11:11:35 +0200 Subject: [PATCH 008/565] introduce OnyxAwareParser --- src/libs/OnyxAwareParser.ts | 39 +++++++++++++++++++ .../report/ContextMenu/ContextMenuActions.tsx | 4 +- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/libs/OnyxAwareParser.ts diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts new file mode 100644 index 000000000000..4a8d0045abe8 --- /dev/null +++ b/src/libs/OnyxAwareParser.ts @@ -0,0 +1,39 @@ +import ONYXKEYS from "@src/ONYXKEYS"; +import ExpensiMark from "expensify-common/lib/ExpensiMark"; +import Onyx from "react-native-onyx"; + +const parser = new ExpensiMark(); + +const reportIDToNameMap: Record = {}; +const accountIDToNameMap: Record = {}; + +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + callback: (report) => { + if (!report) { + return; + } + + reportIDToNameMap[report.reportID] = report.reportName ?? ''; + }, +}) + +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (personalDetailsList) => { + Object.values(personalDetailsList ?? {}).forEach((personalDetails) => { + if (!personalDetails) { + return; + } + + accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? ''; + }); + }, +}) + + +function parseHtmlToMarkdown(html: string): string { + return parser.htmlToMarkdown(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); +}; + +export default parseHtmlToMarkdown; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index b5cb9d911ef5..efe728148887 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -30,6 +30,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type {Beta, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; +import parseHtmlToMarkdown from '@libs/OnyxAwareParser'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; @@ -232,8 +233,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } const editAction = () => { if (!draftMessage) { - const parser = new ExpensiMark(); - Report.saveReportActionDraft(reportID, reportAction, parser.htmlToMarkdown(getActionHtml(reportAction))); + Report.saveReportActionDraft(reportID, reportAction, parseHtmlToMarkdown(getActionHtml(reportAction))); } else { Report.deleteReportActionDraft(reportID, reportAction); } From 7b3fe65b72567ec3bb7f516f531263b57a5914f8 Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 19 Apr 2024 15:06:04 +0200 Subject: [PATCH 009/565] `parser.htmlTo` -> `parseHtmlTo` --- package-lock.json | 6 +++--- package.json | 2 +- src/hooks/useCopySelectionHelper.ts | 7 +++---- src/hooks/useHtmlPaste/index.ts | 5 ++--- src/libs/OnyxAwareParser.ts | 19 +++++++++++-------- src/libs/ReportUtils.ts | 10 ++++------ src/libs/actions/Report.ts | 5 +++-- .../PrivateNotes/PrivateNotesEditPage.tsx | 7 +++---- src/pages/RoomDescriptionPage.tsx | 5 ++--- .../report/ContextMenu/ContextMenuActions.tsx | 8 +++----- .../ComposerWithSuggestions.tsx | 5 ++--- .../report/ReportActionItemMessageEdit.tsx | 5 ++--- src/pages/tasks/NewTaskDescriptionPage.tsx | 3 ++- src/pages/tasks/NewTaskDetailsPage.tsx | 5 +++-- src/pages/tasks/TaskDescriptionPage.tsx | 5 +++-- .../workspace/WorkspaceInviteMessagePage.tsx | 3 ++- .../WorkspaceProfileDescriptionPage.tsx | 3 ++- src/pages/workspace/taxes/NamePage.tsx | 6 ++---- 18 files changed, 53 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52a975e3a83e..80484c07a6fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", + "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", @@ -20213,8 +20213,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", - "integrity": "sha512-zz0/y0apISP1orxXEQOgn+Uod45O4wVypwwtaqcDPV4dH1tC3i4L98NoLSZvLn7Y17EcceSkfN6QCEsscgFTDQ==", + "resolved": "git+ssh://git@github.com/software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", + "integrity": "sha512-Ns7qkMuJ4SeLj0lrj3i+KqHBzjlym8baDlS7CUIqq2tuNXkgxwO4D+5d6U3ooLOf0CyWb56KaGy5TOTFqpJDZA==", "license": "MIT", "dependencies": { "classnames": "2.5.0", diff --git a/package.json b/package.json index 1cef7d94fcba..50029f0e22c3 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", + "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", diff --git a/src/hooks/useCopySelectionHelper.ts b/src/hooks/useCopySelectionHelper.ts index be7830dc6170..ed379bfcf2e6 100644 --- a/src/hooks/useCopySelectionHelper.ts +++ b/src/hooks/useCopySelectionHelper.ts @@ -1,7 +1,7 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import {useEffect} from 'react'; import Clipboard from '@libs/Clipboard'; import KeyboardShortcut from '@libs/KeyboardShortcut'; +import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser'; import SelectionScraper from '@libs/SelectionScraper'; import CONST from '@src/CONST'; @@ -10,12 +10,11 @@ function copySelectionToClipboard() { if (!selection) { return; } - const parser = new ExpensiMark(); if (!Clipboard.canSetHtml()) { - Clipboard.setString(parser.htmlToMarkdown(selection)); + Clipboard.setString(parseHtmlToMarkdown(selection)); return; } - Clipboard.setHtml(selection, parser.htmlToText(selection)); + Clipboard.setHtml(selection, parseHtmlToText(selection)); } export default function useCopySelectionHelper() { diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index 925a3db518ae..82a52dbb0fef 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -1,6 +1,6 @@ import {useNavigation} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import {useCallback, useEffect} from 'react'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import type UseHtmlPaste from './types'; const insertByCommand = (text: string) => { @@ -62,8 +62,7 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi */ const handlePastedHTML = useCallback( (html: string) => { - const parser = new ExpensiMark(); - paste(parser.htmlToMarkdown(html)); + paste(parseHtmlToMarkdown(html)); }, [paste], ); diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts index 4a8d0045abe8..853f94d7189f 100644 --- a/src/libs/OnyxAwareParser.ts +++ b/src/libs/OnyxAwareParser.ts @@ -1,6 +1,6 @@ -import ONYXKEYS from "@src/ONYXKEYS"; -import ExpensiMark from "expensify-common/lib/ExpensiMark"; -import Onyx from "react-native-onyx"; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; const parser = new ExpensiMark(); @@ -16,7 +16,7 @@ Onyx.connect({ reportIDToNameMap[report.reportID] = report.reportName ?? ''; }, -}) +}); Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, @@ -29,11 +29,14 @@ Onyx.connect({ accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? ''; }); }, -}) - +}); function parseHtmlToMarkdown(html: string): string { return parser.htmlToMarkdown(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); -}; +} + +function parseHtmlToText(html: string): string { + return parser.htmlToText(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); +} -export default parseHtmlToMarkdown; +export {parseHtmlToMarkdown, parseHtmlToText}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a15e1937dbe2..42c14c820f0d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -69,6 +69,7 @@ import ModifiedExpenseMessage from './ModifiedExpenseMessage'; import linkingConfig from './Navigation/linkingConfig'; import Navigation from './Navigation/Navigation'; import * as NumberUtils from './NumberUtils'; +import {parseHtmlToText} from './OnyxAwareParser'; import Permissions from './Permissions'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as PhoneNumber from './PhoneNumber'; @@ -3174,8 +3175,7 @@ function getReportDescriptionText(report: Report): string { return ''; } - const parser = new ExpensiMark(); - return parser.htmlToText(report.description); + return parseHtmlToText(report.description); } function getPolicyDescriptionText(policy: OnyxEntry): string { @@ -3183,12 +3183,10 @@ function getPolicyDescriptionText(policy: OnyxEntry): string { return ''; } - const parser = new ExpensiMark(); - return parser.htmlToText(policy.description); + return parseHtmlToText(policy.description); } function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, actorAccountID?: number): OptimisticReportAction { - const parser = new ExpensiMark(); const commentText = getParsedComment(text ?? ''); const isAttachmentOnly = file && !text; const isTextOnly = text && !file; @@ -3200,7 +3198,7 @@ function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, textForNewComment = CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML; } else if (isTextOnly) { htmlForNewComment = commentText; - textForNewComment = parser.htmlToText(htmlForNewComment); + textForNewComment = parseHtmlToText(htmlForNewComment); } else { htmlForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; textForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index a4afff17d972..c4c140e0a85a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -57,6 +57,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import LocalNotification from '@libs/Notification/LocalNotification'; +import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; @@ -1331,7 +1332,7 @@ function editReportComment(reportID: string, originalReportAction: OnyxEntry ReportActions.getDraftPrivateNote(report.reportID).trim() || parser.htmlToMarkdown(report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '').trim(), + () => ReportActions.getDraftPrivateNote(report.reportID).trim() || parseHtmlToMarkdown(report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '').trim(), ); /** @@ -93,7 +92,7 @@ function PrivateNotesEditPage({route, personalDetailsList, report}: PrivateNotes const originalNote = report?.privateNotes?.[Number(route.params.accountID)]?.note ?? ''; let editedNote = ''; if (privateNote.trim() !== originalNote.trim()) { - editedNote = ReportActions.handleUserDeletedLinksInHtml(privateNote.trim(), parser.htmlToMarkdown(originalNote).trim()); + editedNote = ReportActions.handleUserDeletedLinksInHtml(privateNote.trim(), parseHtmlToMarkdown(originalNote).trim()); ReportActions.updatePrivateNotes(report.reportID, Number(route.params.accountID), editedNote); } diff --git a/src/pages/RoomDescriptionPage.tsx b/src/pages/RoomDescriptionPage.tsx index 9d26de1da9f1..4641ed8c1c4f 100644 --- a/src/pages/RoomDescriptionPage.tsx +++ b/src/pages/RoomDescriptionPage.tsx @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection} from 'react-native-onyx'; @@ -13,6 +12,7 @@ import TextInput from '@components/TextInput'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as ReportUtils from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import * as Report from '@userActions/Report'; @@ -31,8 +31,7 @@ type RoomDescriptionPageProps = { function RoomDescriptionPage({report, policies}: RoomDescriptionPageProps) { const styles = useThemeStyles(); - const parser = new ExpensiMark(); - const [description, setDescription] = useState(() => parser.htmlToMarkdown(report?.description ?? '')); + const [description, setDescription] = useState(() => parseHtmlToMarkdown(report?.description ?? '')); const reportDescriptionInputRef = useRef(null); const focusTimeoutRef = useRef | null>(null); const {translate} = useLocalize(); diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index bd3bedb2a83c..db096e26cd30 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -1,4 +1,3 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import type {MutableRefObject} from 'react'; import React from 'react'; @@ -19,6 +18,7 @@ import getAttachmentDetails from '@libs/fileDownload/getAttachmentDetails'; import * as Localize from '@libs/Localize'; import ModifiedExpenseMessage from '@libs/ModifiedExpenseMessage'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -30,7 +30,6 @@ import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type {Beta, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; -import parseHtmlToMarkdown from '@libs/OnyxAwareParser'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; @@ -42,11 +41,10 @@ function getActionHtml(reportAction: OnyxEntry): string { /** Sets the HTML string to Clipboard */ function setClipboardMessage(content: string) { - const parser = new ExpensiMark(); if (!Clipboard.canSetHtml()) { - Clipboard.setString(parser.htmlToMarkdown(content)); + Clipboard.setString(parseHtmlToMarkdown(content)); } else { - const plainText = parser.htmlToText(content); + const plainText = parseHtmlToText(content); Clipboard.setHtml(content, plainText); } } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 8f42da5a1575..6fc09fb1b15b 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -1,5 +1,4 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef, MutableRefObject, RefAttributes, RefObject} from 'react'; import React, {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; @@ -35,6 +34,7 @@ import * as EmojiUtils from '@libs/EmojiUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; import getPlatform from '@libs/getPlatform'; import * as KeyDownListener from '@libs/KeyboardShortcut/KeyDownPressListener'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -536,8 +536,7 @@ function ComposerWithSuggestions( ) { event.preventDefault(); if (lastReportAction) { - const parser = new ExpensiMark(); - Report.saveReportActionDraft(reportID, lastReportAction, parser.htmlToMarkdown(lastReportAction.message?.at(-1)?.html ?? '')); + Report.saveReportActionDraft(reportID, lastReportAction, parseHtmlToMarkdown(lastReportAction.message?.at(-1)?.html ?? '')); } } }, diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index fc3c92434fc4..cc1d2789a09a 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -1,4 +1,3 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; @@ -28,6 +27,7 @@ import * as EmojiUtils from '@libs/EmojiUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; import type {Selection} from '@libs/focusComposerWithDelay/types'; import focusEditAfterCancelDelete from '@libs/focusEditAfterCancelDelete'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import onyxSubscribe from '@libs/onyxSubscribe'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; @@ -117,8 +117,7 @@ function ReportActionItemMessageEdit( const isCommentPendingSaved = useRef(false); useEffect(() => { - const parser = new ExpensiMark(); - const originalMessage = parser.htmlToMarkdown(action.message?.[0]?.html ?? ''); + const originalMessage = parseHtmlToMarkdown(action.message?.[0]?.html ?? ''); if ( ReportActionsUtils.isDeletedAction(action) || Boolean(action.message && draftMessage === originalMessage) || diff --git a/src/pages/tasks/NewTaskDescriptionPage.tsx b/src/pages/tasks/NewTaskDescriptionPage.tsx index e33241c52f4a..4bca46213671 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.tsx +++ b/src/pages/tasks/NewTaskDescriptionPage.tsx @@ -16,6 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {NewTaskNavigatorParamList} from '@libs/Navigation/types'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import * as TaskActions from '@userActions/Task'; import CONST from '@src/CONST'; @@ -77,7 +78,7 @@ function NewTaskDescriptionPage({task}: NewTaskDescriptionPageProps) { { setTaskTitle(task?.title ?? ''); - setTaskDescription(parser.htmlToMarkdown(parser.replace(task?.description ?? ''))); + setTaskDescription(parseHtmlToMarkdown(parser.replace(task?.description ?? ''))); }, [task]); const validate = (values: FormOnyxValues): FormInputErrors => { @@ -132,7 +133,7 @@ function NewTaskDetailsPage({task}: NewTaskDetailsPageProps) { autoGrowHeight shouldSubmitForm containerStyles={styles.autoGrowHeightMultilineInput} - defaultValue={parser.htmlToMarkdown(parser.replace(taskDescription))} + defaultValue={parseHtmlToMarkdown(parser.replace(taskDescription))} value={taskDescription} onValueChange={setTaskDescription} isMarkdownEnabled diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index 9abe15a5bb80..63bff1073698 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -16,6 +16,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as ReportUtils from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; @@ -46,7 +47,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti const submit = useCallback( (values: FormOnyxValues) => { - if (parser.htmlToMarkdown(parser.replace(values.description)) !== parser.htmlToMarkdown(parser.replace(report?.description ?? '')) && !isEmptyObject(report)) { + if (parseHtmlToMarkdown(parser.replace(values.description)) !== parseHtmlToMarkdown(parser.replace(report?.description ?? '')) && !isEmptyObject(report)) { // Set the description of the report in the store and then call EditTask API // to update the description of the report on the server Task.editTask(report, {description: values.description}); @@ -109,7 +110,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti name={INPUT_IDS.DESCRIPTION} label={translate('newTaskPage.descriptionOptional')} accessibilityLabel={translate('newTaskPage.descriptionOptional')} - defaultValue={parser.htmlToMarkdown((report && parser.replace(report?.description ?? '')) || '')} + defaultValue={parseHtmlToMarkdown((report && parser.replace(report?.description ?? '')) || '')} ref={(element: AnimatedTextInputRef) => { if (!element) { return; diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index 30d66662b996..56e2d5a2c257 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -23,6 +23,7 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; @@ -93,7 +94,7 @@ function WorkspaceInviteMessagePage({ useEffect(() => { if (!isEmptyObject(invitedEmailsToAccountIDsDraft)) { - setWelcomeNote(parser.htmlToMarkdown(getDefaultWelcomeNote())); + setWelcomeNote(parseHtmlToMarkdown(getDefaultWelcomeNote())); return; } Navigation.goBack(ROUTES.WORKSPACE_INVITE.getRoute(route.params.policyID), true); diff --git a/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx b/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx index 8086f2414e42..52066d8313d6 100644 --- a/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx +++ b/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx @@ -12,6 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -27,7 +28,7 @@ function WorkspaceProfileDescriptionPage({policy}: Props) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [description, setDescription] = useState(() => - parser.htmlToMarkdown( + parseHtmlToMarkdown( // policy?.description can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing policy?.description || diff --git a/src/pages/workspace/taxes/NamePage.tsx b/src/pages/workspace/taxes/NamePage.tsx index 5c77295a9664..3e5902b140ac 100644 --- a/src/pages/workspace/taxes/NamePage.tsx +++ b/src/pages/workspace/taxes/NamePage.tsx @@ -1,5 +1,4 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useState} from 'react'; import {View} from 'react-native'; import FormProvider from '@components/Form/FormProvider'; @@ -14,6 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {renamePolicyTax, validateTaxName} from '@libs/actions/TaxRate'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as PolicyUtils from '@libs/PolicyUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; @@ -29,8 +29,6 @@ import INPUT_IDS from '@src/types/form/WorkspaceTaxNameForm'; type NamePageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; -const parser = new ExpensiMark(); - function NamePage({ route: { params: {policyID, taxID}, @@ -42,7 +40,7 @@ function NamePage({ const currentTaxRate = PolicyUtils.getTaxByID(policy, taxID); const {inputCallbackRef} = useAutoFocusInput(); - const [name, setName] = useState(() => parser.htmlToMarkdown(currentTaxRate?.name ?? '')); + const [name, setName] = useState(() => parseHtmlToMarkdown(currentTaxRate?.name ?? '')); const goBack = useCallback(() => Navigation.goBack(ROUTES.WORKSPACE_TAX_EDIT.getRoute(policyID ?? '', taxID)), [policyID, taxID]); From baf2a8c18cd41f58b7fdd098a48b53d022e074fa Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 19 Apr 2024 15:21:18 +0200 Subject: [PATCH 010/565] add fallback when displayName does not exist --- src/libs/OnyxAwareParser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts index 853f94d7189f..586b914216be 100644 --- a/src/libs/OnyxAwareParser.ts +++ b/src/libs/OnyxAwareParser.ts @@ -14,7 +14,7 @@ Onyx.connect({ return; } - reportIDToNameMap[report.reportID] = report.reportName ?? ''; + reportIDToNameMap[report.reportID] = report.reportName ?? report.displayName ?? report.reportID; }, }); @@ -26,7 +26,7 @@ Onyx.connect({ return; } - accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? ''; + accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? String(personalDetails.accountID); }); }, }); From 63a70982f9293675e1ce66070b9f1744b03d41b7 Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 19 Apr 2024 15:25:32 +0200 Subject: [PATCH 011/565] bring back removed trim --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index c4c140e0a85a..734078078126 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1332,7 +1332,7 @@ function editReportComment(reportID: string, originalReportAction: OnyxEntry Date: Tue, 23 Apr 2024 13:00:15 +0200 Subject: [PATCH 012/565] update expensify-common --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c30f6c1a74c..17bb60a22ef1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#9a68635cdcef4c81593c0f816a007bc9c707d46a", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", @@ -20202,8 +20202,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", - "integrity": "sha512-Ns7qkMuJ4SeLj0lrj3i+KqHBzjlym8baDlS7CUIqq2tuNXkgxwO4D+5d6U3ooLOf0CyWb56KaGy5TOTFqpJDZA==", + "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#9a68635cdcef4c81593c0f816a007bc9c707d46a", + "integrity": "sha512-9BHjM3kZs7/dil0oykEQFkEhXjVD5liTttmO7ZYtPZkl4j6g97mubY2p9lYpWwpkWckUfvU7nGuZQjahw9xSFA==", "license": "MIT", "dependencies": { "classnames": "2.5.0", diff --git a/package.json b/package.json index 571b1638e266..39c6132711cc 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#9a68635cdcef4c81593c0f816a007bc9c707d46a", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", From 987f3f783283cc2be16b12346a601c9971d2a759 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 24 Apr 2024 18:30:56 +0700 Subject: [PATCH 013/565] rename variables --- src/components/ValidateCode/JustSignedInModal.tsx | 4 ++-- src/components/ValidateCode/ValidateCodeModal.tsx | 4 ++-- src/styles/index.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/ValidateCode/JustSignedInModal.tsx b/src/components/ValidateCode/JustSignedInModal.tsx index 923ea14a3f51..527493c778cb 100644 --- a/src/components/ValidateCode/JustSignedInModal.tsx +++ b/src/components/ValidateCode/JustSignedInModal.tsx @@ -25,8 +25,8 @@ function JustSignedInModal({is2FARequired}: JustSignedInModalProps) { diff --git a/src/components/ValidateCode/ValidateCodeModal.tsx b/src/components/ValidateCode/ValidateCodeModal.tsx index 07fc8cb7167e..a1d5c4ff3faa 100644 --- a/src/components/ValidateCode/ValidateCodeModal.tsx +++ b/src/components/ValidateCode/ValidateCodeModal.tsx @@ -40,8 +40,8 @@ function ValidateCodeModal({code, accountID, session = {}}: ValidateCodeModalPro diff --git a/src/styles/index.ts b/src/styles/index.ts index a579df46b8e4..bb30e5cf279d 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4364,11 +4364,11 @@ const styles = (theme: ThemeColors) => borderWidth: 2, }, - magicCodeAnimationWidth: { + magicCodeAnimation: { width: variables.modalTopIconWidth, }, - justSignedInModalAnimationHeight: (is2FARequired: boolean) => ({ + justSignedInModalAnimation: (is2FARequired: boolean) => ({ height: is2FARequired ? variables.modalTopIconHeight : variables.modalTopBigIconHeight, }), From e343aa68acc529461e7612572b753169ee736c0e Mon Sep 17 00:00:00 2001 From: burczu Date: Mon, 22 Apr 2024 11:24:12 +0200 Subject: [PATCH 014/565] new persona added --- src/CONST.ts | 1 + src/libs/actions/Report.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 2e14aa7cf21f..0725fb47a85b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1202,6 +1202,7 @@ const CONST = { CHRONOS: 'chronos@expensify.com', CONCIERGE: 'concierge@expensify.com', CONTRIBUTORS: 'contributors@expensify.com', + EXPENSIFY_PERSONA: 'expensify@expensify.com', FIRST_RESPONDER: 'firstresponders@expensify.com', GUIDES_DOMAIN: 'team.expensify.com', HELP: 'help@expensify.com', diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7542ca12c592..8260b6194b21 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3029,7 +3029,7 @@ function completeOnboarding( }, adminsChatReportID?: string, ) { - const targetEmail = CONST.EMAIL.CONCIERGE; + const targetEmail = CONST.EMAIL.EXPENSIFY_PERSONA; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; From 9a58f4fc89739c5a05df7bf8e56d0b1b51904b5d Mon Sep 17 00:00:00 2001 From: burczu Date: Mon, 22 Apr 2024 12:49:50 +0200 Subject: [PATCH 015/565] using expensify persona only for odd account ids --- src/libs/actions/Report.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8260b6194b21..4afdeb5201a4 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3029,7 +3029,12 @@ function completeOnboarding( }, adminsChatReportID?: string, ) { - const targetEmail = CONST.EMAIL.EXPENSIFY_PERSONA; + let targetEmail: string = CONST.EMAIL.CONCIERGE; + if (currentUserAccountID % 2 === 1) { + // for odd accountID, we will use the expensify persona instead of concierge + targetEmail = CONST.EMAIL.EXPENSIFY_PERSONA; + } + const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; From 6da74e4ca18a8963ada6cf7083d7cbb84871ca32 Mon Sep 17 00:00:00 2001 From: burczu Date: Fri, 26 Apr 2024 10:56:25 +0200 Subject: [PATCH 016/565] handling new system report type --- src/CONST.ts | 2 +- src/libs/ReportUtils.ts | 21 +++++++++++++++++++-- src/libs/SidebarUtils.ts | 6 ++++++ src/libs/actions/Report.ts | 10 ++++------ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 0725fb47a85b..1aaea219d3bd 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -53,6 +53,7 @@ const chatTypes = { POLICY_ROOM: 'policyRoom', POLICY_EXPENSE_CHAT: 'policyExpenseChat', SELF_DM: 'selfDM', + SYSTEM: 'system', } as const; // Explicit type annotation is required @@ -1202,7 +1203,6 @@ const CONST = { CHRONOS: 'chronos@expensify.com', CONCIERGE: 'concierge@expensify.com', CONTRIBUTORS: 'contributors@expensify.com', - EXPENSIFY_PERSONA: 'expensify@expensify.com', FIRST_RESPONDER: 'firstresponders@expensify.com', GUIDES_DOMAIN: 'team.expensify.com', HELP: 'help@expensify.com', diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 121518130cb4..1aac5cd716c5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -978,6 +978,10 @@ function isGroupChat(report: OnyxEntry | Partial): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.GROUP; } +function isSystemChat(report: OnyxEntry | Partial): boolean { + return getChatType(report) === CONST.REPORT.CHAT_TYPE.SYSTEM; +} + /** * Only returns true if this is our main 1:1 DM report with Concierge */ @@ -4791,7 +4795,6 @@ function shouldReportBeInOptionList({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing report?.isHidden || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - report?.participantAccountIDs?.includes(CONST.ACCOUNT_ID.NOTIFICATIONS) || (report?.participantAccountIDs?.length === 0 && !isChatThread(report) && !isPublicRoom(report) && @@ -4800,7 +4803,8 @@ function shouldReportBeInOptionList({ !isMoneyRequestReport(report) && !isTaskReport(report) && !isSelfDM(report) && - !isGroupChat(report)) + !isGroupChat(report) && + !isSystemChat(report)) ) { return false; } @@ -4877,6 +4881,18 @@ function shouldReportBeInOptionList({ return true; } +/** + * Returns the system report from the list of reports. + * TODO: this method may not be necessary if the participants list of the system report is filled correctly + */ +function getSystemChat(): OnyxEntry { + if (!allReports) { + return null; + } + + return Object.values(allReports ?? {}).find((report) => report?.chatType === CONST.REPORT.CHAT_TYPE.SYSTEM) ?? null; +} + /** * Attempts to find a report in onyx with the provided list of participants. Does not include threads, task, expense, room, and policy expense chat. */ @@ -6306,6 +6322,7 @@ export { getRoomWelcomeMessage, getRootParentReport, getRouteFromLink, + getSystemChat, getTaskAssigneeChatOnyxData, getTransactionDetails, getTransactionReportName, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index d230f58e46f9..2c52af317be5 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -20,6 +20,7 @@ import * as OptionsListUtils from './OptionsListUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as ReportUtils from './ReportUtils'; import * as TaskUtils from './TaskUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; const visibleReportActionItems: ReportActions = {}; Onyx.connect({ @@ -245,6 +246,11 @@ function getOptionData({ participantAccountIDs = [report.ownerAccountID ?? 0]; } + // TODO: this is added for the testing purposes only - should be removed once participants list of the system report is filled + if (report.chatType === CONST.REPORT.CHAT_TYPE.SYSTEM) { + participantAccountIDs = [report.ownerAccountID ?? 0, ...PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.NOTIFICATIONS])]; + } + const participantPersonalDetailList = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails)) as PersonalDetails[]; const personalDetail = participantPersonalDetailList[0] ?? {}; const hasErrors = Object.keys(result.allReportErrors ?? {}).length !== 0; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4afdeb5201a4..4d5e99966323 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3029,14 +3029,12 @@ function completeOnboarding( }, adminsChatReportID?: string, ) { - let targetEmail: string = CONST.EMAIL.CONCIERGE; - if (currentUserAccountID % 2 === 1) { - // for odd accountID, we will use the expensify persona instead of concierge - targetEmail = CONST.EMAIL.EXPENSIFY_PERSONA; - } + const isAccountIDOdd = currentUserAccountID % 2 === 1; + const targetEmail = isAccountIDOdd ? CONST.EMAIL.NOTIFICATIONS : CONST.EMAIL.CONCIERGE; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; - const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); + // TODO: using getSystemChat is rather not necessary if we could have participants list filled correctly + const targetChatReport= isAccountIDOdd ? ReportUtils.getSystemChat() : ReportUtils.getChatByParticipants([actorAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Mention message From aaa3cd4c9bf2b136b430a61f9655308df57e88cc Mon Sep 17 00:00:00 2001 From: burczu Date: Fri, 26 Apr 2024 11:35:07 +0200 Subject: [PATCH 017/565] navigating to system chat at the end of the onboarding process added --- src/libs/AccountUtils.ts | 4 +++- src/libs/SidebarUtils.ts | 2 +- src/libs/actions/Report.ts | 18 ++++++++++++++++-- .../BaseOnboardingPersonalDetails.tsx | 7 ++++++- .../OnboardingWork/BaseOnboardingWork.tsx | 7 ++++++- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/libs/AccountUtils.ts b/src/libs/AccountUtils.ts index d903584e15b4..8bc7037e9682 100644 --- a/src/libs/AccountUtils.ts +++ b/src/libs/AccountUtils.ts @@ -5,4 +5,6 @@ import type {Account} from '@src/types/onyx'; const isValidateCodeFormSubmitting = (account: OnyxEntry) => !!account?.isLoading && account.loadingForm === (account.requiresTwoFactorAuth ? CONST.FORMS.VALIDATE_TFA_CODE_FORM : CONST.FORMS.VALIDATE_CODE_FORM); -export default {isValidateCodeFormSubmitting}; +const isAccountIDOddNumber = (accountID: number) => accountID % 2 === 1; + +export default {isValidateCodeFormSubmitting, isAccountIDOddNumber}; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 2c52af317be5..50d9cea26efe 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -17,10 +17,10 @@ import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as OptionsListUtils from './OptionsListUtils'; +import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as ReportUtils from './ReportUtils'; import * as TaskUtils from './TaskUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; const visibleReportActionItems: ReportActions = {}; Onyx.connect({ diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4d5e99966323..714cf2182db9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -8,6 +8,7 @@ import Onyx from 'react-native-onyx'; import type {PartialDeep, ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; +import AccountUtils from '@libs/AccountUtils'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; import type { @@ -1930,6 +1931,18 @@ function navigateToConciergeChat(shouldDismissModal = false, checkIfCurrentPageA } } +/** + * Navigates to the 1:1 system chat + */ +function navigateToSystemChat() { + // TODO: when system report participants list is filled, we could just use `ReportUtils.getChatByParticipants()` method insted `getSystemChat()` + const systemChatReport = ReportUtils.getSystemChat(); + + if (systemChatReport && systemChatReport.reportID) { + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(systemChatReport.reportID)); + } +} + /** Add a policy report (workspace room) optimistically and navigate to it. */ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { const createdReportAction = ReportUtils.buildOptimisticCreatedReportAction(CONST.POLICY.OWNER_EMAIL_FAKE); @@ -3029,12 +3042,12 @@ function completeOnboarding( }, adminsChatReportID?: string, ) { - const isAccountIDOdd = currentUserAccountID % 2 === 1; + const isAccountIDOdd = AccountUtils.isAccountIDOddNumber(currentUserAccountID ?? 0); const targetEmail = isAccountIDOdd ? CONST.EMAIL.NOTIFICATIONS : CONST.EMAIL.CONCIERGE; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; // TODO: using getSystemChat is rather not necessary if we could have participants list filled correctly - const targetChatReport= isAccountIDOdd ? ReportUtils.getSystemChat() : ReportUtils.getChatByParticipants([actorAccountID]); + const targetChatReport = isAccountIDOdd ? ReportUtils.getSystemChat() : ReportUtils.getChatByParticipants([actorAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Mention message @@ -3736,6 +3749,7 @@ export { saveReportActionDraftNumberOfLines, deleteReportComment, navigateToConciergeChat, + navigateToSystemChat, addPolicyReport, deleteReport, navigateToConciergeChatAndDeleteReport, diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 7049b04cc293..0082ede92c4f 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -7,6 +7,7 @@ import type {FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import OfflineIndicator from '@components/OfflineIndicator'; +import {useSession} from '@components/OnyxProvider'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; @@ -16,6 +17,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import AccountUtils from '@libs/AccountUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; @@ -37,6 +39,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const {shouldUseNarrowLayout} = useOnboardingLayout(); const {inputCallbackRef} = useAutoFocusInput(); const [shouldValidateOnChange, setShouldValidateOnChange] = useState(false); + const {accountID} = useSession(); useDisableModalDismissOnEscape(); @@ -69,6 +72,8 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat // Otherwise stay on the chats screen. if (isSmallScreenWidth) { Navigation.navigate(ROUTES.HOME); + } else if (AccountUtils.isAccountIDOddNumber(accountID ?? 0)) { + Report.navigateToSystemChat(); } else { Report.navigateToConciergeChat(); } @@ -79,7 +84,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat Navigation.navigate(ROUTES.WELCOME_VIDEO_ROOT); }, variables.welcomeVideoDelay); }, - [currentUserPersonalDetails.login, isSmallScreenWidth, onboardingPurposeSelected], + [currentUserPersonalDetails.login, isSmallScreenWidth, onboardingPurposeSelected, accountID], ); const validate = (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index 151c1bb35ea2..d2e583d778c2 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -7,6 +7,7 @@ import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import OfflineIndicator from '@components/OfflineIndicator'; +import {useSession} from '@components/OnyxProvider'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; @@ -15,6 +16,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import AccountUtils from '@libs/AccountUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; @@ -32,6 +34,7 @@ function BaseOnboardingWork({currentUserPersonalDetails, shouldUseNativeStyles, const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useOnboardingLayout(); + const {accountID} = useSession(); useDisableModalDismissOnEscape(); @@ -62,6 +65,8 @@ function BaseOnboardingWork({currentUserPersonalDetails, shouldUseNativeStyles, // Otherwise stay on the chats screen. if (isSmallScreenWidth) { Navigation.navigate(ROUTES.HOME); + } else if (AccountUtils.isAccountIDOddNumber(accountID ?? 0)) { + Report.navigateToSystemChat(); } else { Report.navigateToConciergeChat(); } @@ -72,7 +77,7 @@ function BaseOnboardingWork({currentUserPersonalDetails, shouldUseNativeStyles, Navigation.navigate(ROUTES.WELCOME_VIDEO_ROOT); }, variables.welcomeVideoDelay); }, - [currentUserPersonalDetails.firstName, currentUserPersonalDetails.lastName, currentUserPersonalDetails.login, isSmallScreenWidth, onboardingPurposeSelected], + [currentUserPersonalDetails.firstName, currentUserPersonalDetails.lastName, currentUserPersonalDetails.login, isSmallScreenWidth, onboardingPurposeSelected, accountID], ); const validate = (values: FormOnyxValues<'onboardingWorkForm'>) => { From 282490a17937d7928230d573dc2af8834e5fcd80 Mon Sep 17 00:00:00 2001 From: burczu Date: Fri, 26 Apr 2024 11:41:47 +0200 Subject: [PATCH 018/565] addional todo comment added --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1aac5cd716c5..e58dd3e54b12 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4804,6 +4804,7 @@ function shouldReportBeInOptionList({ !isTaskReport(report) && !isSelfDM(report) && !isGroupChat(report) && + // TODO: this shouldn't be necessary if the system report has participants list filled !isSystemChat(report)) ) { return false; From 46a1f6e72cd3b3b069cdb1fcd72e845c63ee3716 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 29 Apr 2024 14:04:57 +0200 Subject: [PATCH 019/565] fix failing typecheck --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cb9f449c14f9..ad683903fd5c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3324,7 +3324,7 @@ function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, textForNewComment = parseHtmlToText(htmlForNewComment); } else { htmlForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; - textForNewComment = `${parser.htmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; + textForNewComment = `${parseHtmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; } const isAttachment = !text && file !== undefined; From 0410d8ff2bb5ac120aeb3bf9fe5d98a5b579132f Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 30 Apr 2024 16:01:49 +0200 Subject: [PATCH 020/565] fix: minor fix --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c9213d5460ea..cbce4a48928d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4983,7 +4983,7 @@ function shouldReportBeInOptionList({ !isTaskReport(report) && !isSelfDM(report) && !isGroupChat(report) && - !isInvoiceRoom(report)) && + !isInvoiceRoom(report) && // TODO: this shouldn't be necessary if the system report has participants list filled !isSystemChat(report)) ) { From 179575fb7ff9e91b9f7a87f90162d03191875c09 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 30 Apr 2024 17:03:58 +0200 Subject: [PATCH 021/565] fix: remove comment --- src/libs/ReportUtils.ts | 1 - src/libs/actions/Report.ts | 24 +++++++++++++++---- .../BaseOnboardingPersonalDetails.tsx | 2 ++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cbce4a48928d..967004c7184d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5064,7 +5064,6 @@ function shouldReportBeInOptionList({ /** * Returns the system report from the list of reports. - * TODO: this method may not be necessary if the participants list of the system report is filled correctly */ function getSystemChat(): OnyxEntry { if (!allReports) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9fe8f8d9fffa..4ef0b9e22d02 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -871,7 +871,11 @@ function openReport( if (isFromDeepLink) { // eslint-disable-next-line rulesdir/no-api-side-effects-method - API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData}).finally(() => { + API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.OPEN_REPORT, parameters, { + optimisticData, + successData, + failureData, + }).finally(() => { Onyx.set(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, false); }); } else { @@ -1851,7 +1855,10 @@ function updateDescription(reportID: string, previousValue: string, newValue: st { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: {description: parsedDescription, pendingFields: {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}, + value: { + description: parsedDescription, + pendingFields: {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, }, ]; const failureData: OnyxUpdate[] = [ @@ -1930,7 +1937,6 @@ function navigateToConciergeChat(shouldDismissModal = false, checkIfCurrentPageA * Navigates to the 1:1 system chat */ function navigateToSystemChat() { - // TODO: when system report participants list is filled, we could just use `ReportUtils.getChatByParticipants()` method insted `getSystemChat()` const systemChatReport = ReportUtils.getSystemChat(); if (systemChatReport && systemChatReport.reportID) { @@ -2215,7 +2221,10 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi // If this notification was delayed and the user saw the message already, don't show it if (action && report?.lastReadTime && report.lastReadTime >= action.created) { - Log.info(`${tag} No notification because the comment was already read`, false, {created: action.created, lastReadTime: report.lastReadTime}); + Log.info(`${tag} No notification because the comment was already read`, false, { + created: action.created, + lastReadTime: report.lastReadTime, + }); return false; } @@ -2243,7 +2252,10 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi const report = allReports?.[reportID] ?? null; if (!report) { - Log.hmmm("[LocalNotification] couldn't show report action notification because the report wasn't found", {reportID, reportActionID: reportAction.reportActionID}); + Log.hmmm("[LocalNotification] couldn't show report action notification because the report wasn't found", { + reportID, + reportActionID: reportAction.reportActionID, + }); return; } @@ -3027,6 +3039,8 @@ function completeOnboarding( const isAccountIDOdd = AccountUtils.isAccountIDOddNumber(currentUserAccountID ?? 0); const targetEmail = isAccountIDOdd ? CONST.EMAIL.NOTIFICATIONS : CONST.EMAIL.CONCIERGE; + console.log({currentUserAccountID}); + const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; // TODO: using getSystemChat is rather not necessary if we could have participants list filled correctly const targetChatReport = isAccountIDOdd ? ReportUtils.getSystemChat() : ReportUtils.getChatByParticipants([actorAccountID]); diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 036408226e71..eb4d4e50ffbd 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -42,6 +42,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const {accountID} = useSession(); useDisableModalDismissOnEscape(); + console.log({accountID}); const completeEngagement = useCallback( (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { @@ -67,6 +68,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat }); Navigation.dismissModal(); + console.log({accountID}); // Only navigate to concierge chat when central pane is visible // Otherwise stay on the chats screen. From 9cba84a26366e0eb611ead798c877a0419b5eb4a Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 30 Apr 2024 17:12:31 +0200 Subject: [PATCH 022/565] fix: remove unnecessary condition --- src/libs/ReportUtils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 967004c7184d..ed9639ab765a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4983,9 +4983,7 @@ function shouldReportBeInOptionList({ !isTaskReport(report) && !isSelfDM(report) && !isGroupChat(report) && - !isInvoiceRoom(report) && - // TODO: this shouldn't be necessary if the system report has participants list filled - !isSystemChat(report)) + !isInvoiceRoom(report)) ) { return false; } From ba3aa46bd40fbb1be8be752448c2ff254860e463 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 30 Apr 2024 17:25:53 +0200 Subject: [PATCH 023/565] fix: remove console log --- src/libs/actions/Report.ts | 12 ++++++++---- .../BaseOnboardingPersonalDetails.tsx | 2 -- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4ef0b9e22d02..7d7e93463af2 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -351,7 +351,10 @@ function subscribeToReportTypingEvents(reportID: string) { delete typingWatchTimers[reportUserIdentifier]; }, 1500); }).catch((error) => { - Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', {errorType: error.type, pusherChannelName}); + Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', { + errorType: error.type, + pusherChannelName, + }); }); } @@ -382,7 +385,10 @@ function subscribeToReportLeavingEvents(reportID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, true); }).catch((error) => { - Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', {errorType: error.type, pusherChannelName}); + Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', { + errorType: error.type, + pusherChannelName, + }); }); } @@ -3039,8 +3045,6 @@ function completeOnboarding( const isAccountIDOdd = AccountUtils.isAccountIDOddNumber(currentUserAccountID ?? 0); const targetEmail = isAccountIDOdd ? CONST.EMAIL.NOTIFICATIONS : CONST.EMAIL.CONCIERGE; - console.log({currentUserAccountID}); - const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; // TODO: using getSystemChat is rather not necessary if we could have participants list filled correctly const targetChatReport = isAccountIDOdd ? ReportUtils.getSystemChat() : ReportUtils.getChatByParticipants([actorAccountID]); diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index eb4d4e50ffbd..036408226e71 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -42,7 +42,6 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const {accountID} = useSession(); useDisableModalDismissOnEscape(); - console.log({accountID}); const completeEngagement = useCallback( (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { @@ -68,7 +67,6 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat }); Navigation.dismissModal(); - console.log({accountID}); // Only navigate to concierge chat when central pane is visible // Otherwise stay on the chats screen. From 433b7b8051f5e3532dd0b519adbb4c473d78e484 Mon Sep 17 00:00:00 2001 From: Ren Jones <153645623+ren-jones@users.noreply.github.com> Date: Wed, 1 May 2024 13:56:54 -0500 Subject: [PATCH 024/565] DOCS: Update and rename Profile.md to Add-profile-photo.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Article rewrite—we're splitting the previous version of this doc into multiple different articles --- .../settings/Add-profile-photo.md | 21 +++++ .../new-expensify/settings/Profile.md | 92 ------------------- 2 files changed, 21 insertions(+), 92 deletions(-) create mode 100644 docs/articles/new-expensify/settings/Add-profile-photo.md delete mode 100644 docs/articles/new-expensify/settings/Profile.md diff --git a/docs/articles/new-expensify/settings/Add-profile-photo.md b/docs/articles/new-expensify/settings/Add-profile-photo.md new file mode 100644 index 000000000000..60e56deaafbc --- /dev/null +++ b/docs/articles/new-expensify/settings/Add-profile-photo.md @@ -0,0 +1,21 @@ +--- +title: Add profile photo +description: Add an image to your profile +--- +
+ +{% include selector.html values="desktop, mobile" %} + +{% include option.html value="desktop" %} +1. Click your profile image or icon in the bottom left menu. +2. Click the Edit pencil icon next to your profile image or icon and select **Upload Image** to choose a new image from your saved files. +{% include end-option.html %} + +{% include option.html value="mobile" %} +1. Tap your profile image or icon at the bottom of the screen. +2. Tap the Edit pencil icon next to your profile image or icon and select **Upload Image** to choose a new image from your saved files. +{% include end-option.html %} + +{% include end-selector.html %} + +
diff --git a/docs/articles/new-expensify/settings/Profile.md b/docs/articles/new-expensify/settings/Profile.md deleted file mode 100644 index 908cf39c7ac6..000000000000 --- a/docs/articles/new-expensify/settings/Profile.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: Profile -description: How to manage your Expensify Profile ---- -# Overview -Your Profile in Expensify allows you to: -- Set your public profile photo -- Set a display name -- Manage your contact methods -- Communicate your current status -- Set your pronouns -- Configure your timezone -- Store your personal details (for travel and payment purposes) - -# How to set your public profile photo - -To set or update your profile photo: -1. Go to **Settings > Profile** -2. Tap on the default or your existing profile photo, -3. You can either either upload (to set a new profile photo), remove or view your profile photo - -Your profile photo is visible to all Expensify users. - -# How to set a display name - -To set or update your display name: -1. Go to **Settings > Profile** -2. Tap on **Display name** -3. Set a first name and a last name, then **Save** - -Your display name is public to all Expensify users. - -# How to add or remove contact methods (email address and phone number) - -Your contact methods allow people to contact you (using your email address or phone number), and allow you to forward receipts to receipts@expensify.com from multiple email addresses. - -To manage your contact methods: -1. Go to **Settings > Profile** -2. Tap on **Contact method** -3. Tap **New contact method** to add a new email or phone number - -Your default contact method (email address or phone number) will be visible to "known" users, with whom you have interacted or are part of your team. - -To change the email address or phone number that's displayed on your Expensify account, add a new contact method, then tap on that email address and tap **Set as default**. - -# How to communicate your current status - -You can use your status emoji to communicate your mood, focus or current activity. You can optionally add a status message too! - -To set your status emoji and status message: -1. Go to **Settings > Profile** -2. Tap on **Status** then **Status** -3. Choose a status emoji, and optionally set a status message -4. Tap on **Save** - -Your status emoji will be visible next to your name in Expensify, and your status emoji and status message will appear in your profile (which is public to all Expensify users). On a computer, your status message will also be visible by hovering your mouse over your name. - -You can also remove your current status: -1. Go to **Settings > Profile** -2. Tap on **Status** -3. Tap on **Clear status** - -# How to set your pronouns - -To set your pronouns: -1. Go to **Settings > Profile** -2. Tap on **Pronouns** -3. Search for your preferred pronouns, then tap on your choice - -Your pronouns will be visible to "known" users, with whom you have interacted or are part of your team. - -# How to configure your timezone - -Your timezone is automatically set using an estimation based on your IP address. - -To set your timezone manually: -1. Go to **Settings > Profile** -2. Tap on **Timezone** -3. Disable **Automatically determine your location** -4. Tap on **Timezone** -5. Search for your preferred timezone, then tap on your choice - -Your timezone will be visible to "known" users, with whom you have interacted or are part of your team. - -# How to store your personal details (for travel and payment purposes) - -Your personal details can be used in Expensify for travel and payment purposes. These will not be shared with any other Expensify user. - -To set your timezone manually: -1. Go to **Settings > Profile** -2. Tap on **Personal details** -3. Tap on **Legal name**, **Date of birth**, and **Address** to set your personal details From 5c2e6d2b3f1319d52e79f0726dcc7e671a1f4e8f Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 2 May 2024 11:36:37 +0200 Subject: [PATCH 025/565] fix: remove unnecessary change --- src/libs/actions/Report.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7d7e93463af2..270c126fbcad 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -351,10 +351,7 @@ function subscribeToReportTypingEvents(reportID: string) { delete typingWatchTimers[reportUserIdentifier]; }, 1500); }).catch((error) => { - Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', { - errorType: error.type, - pusherChannelName, - }); + Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', {errorType: error.type, pusherChannelName}); }); } @@ -385,10 +382,7 @@ function subscribeToReportLeavingEvents(reportID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, true); }).catch((error) => { - Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', { - errorType: error.type, - pusherChannelName, - }); + Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', {errorType: error.type, pusherChannelName}); }); } From 36842b8d038974c945751ce7ad51fc026fed7c81 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 2 May 2024 11:42:50 +0200 Subject: [PATCH 026/565] fix: apply requested changes --- src/libs/ReportUtils.ts | 1 - src/libs/SidebarUtils.ts | 5 ----- src/libs/actions/Report.ts | 24 +++++------------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ed9639ab765a..1f3ececf17bd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4973,7 +4973,6 @@ function shouldReportBeInOptionList({ report?.reportName === undefined || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing report?.isHidden || - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (report?.participantAccountIDs?.length === 0 && !isChatThread(report) && !isPublicRoom(report) && diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 3d0b214d8377..2360366dde73 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -245,11 +245,6 @@ function getOptionData({ participantAccountIDs = [report.ownerAccountID ?? 0]; } - // TODO: this is added for the testing purposes only - should be removed once participants list of the system report is filled - if (report.chatType === CONST.REPORT.CHAT_TYPE.SYSTEM) { - participantAccountIDs = [report.ownerAccountID ?? 0, ...PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.NOTIFICATIONS])]; - } - const participantPersonalDetailList = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails)) as PersonalDetails[]; const personalDetail = participantPersonalDetailList[0] ?? {}; const hasErrors = Object.keys(result.allReportErrors ?? {}).length !== 0; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 270c126fbcad..bb3855dd893a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -871,11 +871,7 @@ function openReport( if (isFromDeepLink) { // eslint-disable-next-line rulesdir/no-api-side-effects-method - API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.OPEN_REPORT, parameters, { - optimisticData, - successData, - failureData, - }).finally(() => { + API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData}).finally(() => { Onyx.set(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, false); }); } else { @@ -1855,10 +1851,7 @@ function updateDescription(reportID: string, previousValue: string, newValue: st { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - description: parsedDescription, - pendingFields: {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, + value: {description: parsedDescription, pendingFields: {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}, }, ]; const failureData: OnyxUpdate[] = [ @@ -2221,10 +2214,7 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi // If this notification was delayed and the user saw the message already, don't show it if (action && report?.lastReadTime && report.lastReadTime >= action.created) { - Log.info(`${tag} No notification because the comment was already read`, false, { - created: action.created, - lastReadTime: report.lastReadTime, - }); + Log.info(`${tag} No notification because the comment was already read`, false, {created: action.created, lastReadTime: report.lastReadTime}); return false; } @@ -2252,10 +2242,7 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi const report = allReports?.[reportID] ?? null; if (!report) { - Log.hmmm("[LocalNotification] couldn't show report action notification because the report wasn't found", { - reportID, - reportActionID: reportAction.reportActionID, - }); + Log.hmmm("[LocalNotification] couldn't show report action notification because the report wasn't found", {reportID, reportActionID: reportAction.reportActionID}); return; } @@ -3040,8 +3027,7 @@ function completeOnboarding( const targetEmail = isAccountIDOdd ? CONST.EMAIL.NOTIFICATIONS : CONST.EMAIL.CONCIERGE; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; - // TODO: using getSystemChat is rather not necessary if we could have participants list filled correctly - const targetChatReport = isAccountIDOdd ? ReportUtils.getSystemChat() : ReportUtils.getChatByParticipants([actorAccountID]); + const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Mention message From a6b6593adaf35c4ea1ec5c8282845fc5e3845c17 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 2 May 2024 13:56:59 +0200 Subject: [PATCH 027/565] fix: lint --- src/libs/SidebarUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 2360366dde73..c0d0c9020a64 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -18,7 +18,6 @@ import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as OptionsListUtils from './OptionsListUtils'; -import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as ReportUtils from './ReportUtils'; import * as TaskUtils from './TaskUtils'; From 7f2598766711bd79a98054b6cf5a35dfa6540564 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Tue, 7 May 2024 09:45:18 +0200 Subject: [PATCH 028/565] Add focus trap to report context menu --- .../FocusTrapForScreen/index.web.tsx | 5 +- .../BaseReportActionContextMenu.tsx | 119 +++++++++--------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx index 2e9a59096819..3dd12f232d25 100644 --- a/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx +++ b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx @@ -1,6 +1,6 @@ import {useFocusEffect, useIsFocused, useRoute} from '@react-navigation/native'; import FocusTrapOriginal from 'focus-trap-react'; -import React, {useMemo, useRef} from 'react'; +import React, {useMemo} from 'react'; import BOTTOM_TAB_SCREENS from '@components/FocusTrap/BOTTOM_TAB_SCREENS'; import SCREENS_WITH_AUTOFOCUS from '@components/FocusTrap/SCREENS_WITH_AUTOFOCUS'; import sharedTrapStack from '@components/FocusTrap/sharedTrapStack'; @@ -32,11 +32,8 @@ function FocusTrap({children}: FocusTrapProps) { activeRouteName = route.name; }); - const focusTrapRef = useRef(null); - return ( - {filteredContextMenuActions.map((contextAction, index) => { - const closePopup = !isMini; - const payload: ContextMenuActionPayload = { - reportAction: reportAction as ReportAction, - reportID, - draftMessage, - selection, - close: () => setShouldKeepOpen(false), - openContextMenu: () => setShouldKeepOpen(true), - interceptAnonymousUser, - openOverflowMenu, - setIsEmojiPickerActive, - }; - - if ('renderContent' in contextAction) { - return contextAction.renderContent(closePopup, payload); - } - - const {textTranslateKey} = contextAction; - const isKeyInActionUpdateKeys = - textTranslateKey === 'reportActionContextMenu.editAction' || - textTranslateKey === 'reportActionContextMenu.deleteAction' || - textTranslateKey === 'reportActionContextMenu.deleteConfirmation'; - const text = textTranslateKey && (isKeyInActionUpdateKeys ? translate(textTranslateKey, {action: reportAction}) : translate(textTranslateKey)); - const transactionPayload = textTranslateKey === 'reportActionContextMenu.copyToClipboard' && transaction && {transaction}; - const isMenuAction = textTranslateKey === 'reportActionContextMenu.menu'; - - return ( - { - menuItemRefs.current[index] = ref; - }} - buttonRef={isMenuAction ? threedotRef : {current: null}} - icon={contextAction.icon} - text={text ?? ''} - successIcon={contextAction.successIcon} - successText={contextAction.successTextTranslateKey ? translate(contextAction.successTextTranslateKey) : undefined} - isMini={isMini} - key={contextAction.textTranslateKey} - onPress={(event) => - interceptAnonymousUser( - () => contextAction.onPress?.(closePopup, {...payload, ...transactionPayload, event, ...(isMenuAction ? {anchorRef: threedotRef} : {})}), - contextAction.isAnonymousAction, - ) - } - description={contextAction.getDescription?.(selection) ?? ''} - isAnonymousAction={contextAction.isAnonymousAction} - isFocused={focusedIndex === index} - shouldPreventDefaultFocusOnPress={contextAction.shouldPreventDefaultFocusOnPress} - onFocus={() => setFocusedIndex(index)} - /> - ); - })} -
+ + + {filteredContextMenuActions.map((contextAction, index) => { + const closePopup = !isMini; + const payload: ContextMenuActionPayload = { + reportAction: reportAction as ReportAction, + reportID, + draftMessage, + selection, + close: () => setShouldKeepOpen(false), + openContextMenu: () => setShouldKeepOpen(true), + interceptAnonymousUser, + openOverflowMenu, + setIsEmojiPickerActive, + }; + + if ('renderContent' in contextAction) { + return contextAction.renderContent(closePopup, payload); + } + + const {textTranslateKey} = contextAction; + const isKeyInActionUpdateKeys = + textTranslateKey === 'reportActionContextMenu.editAction' || + textTranslateKey === 'reportActionContextMenu.deleteAction' || + textTranslateKey === 'reportActionContextMenu.deleteConfirmation'; + const text = textTranslateKey && (isKeyInActionUpdateKeys ? translate(textTranslateKey, {action: reportAction}) : translate(textTranslateKey)); + const transactionPayload = textTranslateKey === 'reportActionContextMenu.copyToClipboard' && transaction && {transaction}; + const isMenuAction = textTranslateKey === 'reportActionContextMenu.menu'; + + return ( + { + menuItemRefs.current[index] = ref; + }} + buttonRef={isMenuAction ? threedotRef : {current: null}} + icon={contextAction.icon} + text={text ?? ''} + successIcon={contextAction.successIcon} + successText={contextAction.successTextTranslateKey ? translate(contextAction.successTextTranslateKey) : undefined} + isMini={isMini} + key={contextAction.textTranslateKey} + onPress={(event) => + interceptAnonymousUser( + () => contextAction.onPress?.(closePopup, {...payload, ...transactionPayload, event, ...(isMenuAction ? {anchorRef: threedotRef} : {})}), + contextAction.isAnonymousAction, + ) + } + description={contextAction.getDescription?.(selection) ?? ''} + isAnonymousAction={contextAction.isAnonymousAction} + isFocused={focusedIndex === index} + shouldPreventDefaultFocusOnPress={contextAction.shouldPreventDefaultFocusOnPress} + onFocus={() => setFocusedIndex(index)} + /> + ); + })} + + ) ); } From ca6201f94fdf16b0977ea208b746ee0da495dde0 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 7 May 2024 15:13:55 +0200 Subject: [PATCH 029/565] refactor: wip - connect all the steps --- .../ModalStackNavigators/index.tsx | 4 +- .../AddBankAccount/AddBankAccount.tsx | 2 +- src/pages/EnablePayments/EnablePayments.tsx | 102 ++++++++++++++++++ .../PersonalInfo/PersonalInfo.tsx | 30 +++--- .../VerifyIdentity/VerifyIdentity.tsx | 16 +-- 5 files changed, 126 insertions(+), 28 deletions(-) create mode 100644 src/pages/EnablePayments/EnablePayments.tsx diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index ce8ea271182d..b9c1e94f0497 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -216,9 +216,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Wallet/ChooseTransferAccountPage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS]: () => require('../../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_REFACTOR]: () => require('../../../../pages/EnablePayments/PersonalInfo/PersonalInfo').default as React.ComponentType, - // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_TEMPORARY_TERMS]: () => require('../../../../pages/EnablePayments/FeesAndTerms/FeesAndTerms').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_REFACTOR]: () => require('../../../../pages/EnablePayments/EnablePayments').default as React.ComponentType, [SCREENS.SETTINGS.ADD_DEBIT_CARD]: () => require('../../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: () => require('../../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, [SCREENS.SETTINGS.ADD_BANK_ACCOUNT_REFACTOR]: () => require('../../../../pages/EnablePayments/AddBankAccount/AddBankAccount').default as React.ComponentType, diff --git a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx index 79c91af178c3..9fde5456226b 100644 --- a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx +++ b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx @@ -44,7 +44,7 @@ function AddBankAccount({personalBankAccount, plaidData, personalBankAccountDraf if (selectedPlaidBankAccount) { BankAccounts.addPersonalBankAccount(selectedPlaidBankAccount); - Navigation.navigate(ROUTES.SETTINGS_ENABLE_PAYMENTS); + Navigation.navigate(ROUTES.SETTINGS_ENABLE_PAYMENTS_REFACTOR); } }, [personalBankAccountDraft?.plaidAccountID, plaidData?.bankAccounts]); diff --git a/src/pages/EnablePayments/EnablePayments.tsx b/src/pages/EnablePayments/EnablePayments.tsx new file mode 100644 index 000000000000..6df56afced57 --- /dev/null +++ b/src/pages/EnablePayments/EnablePayments.tsx @@ -0,0 +1,102 @@ +import React, {useEffect} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; +import Navigation from '@libs/Navigation/Navigation'; +import FeesAndTerms from '@pages/EnablePayments/FeesAndTerms/FeesAndTerms'; +import PersonalInfo from '@pages/EnablePayments/PersonalInfo/PersonalInfo'; +import VerifyIdentity from '@pages/EnablePayments/VerifyIdentity/VerifyIdentity'; +import * as Wallet from '@userActions/Wallet'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type {UserWallet} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import ActivateStep from './ActivateStep'; +import FailedKYC from './FailedKYC'; + +type EnablePaymentsPageOnyxProps = { + /** The user's wallet */ + userWallet: OnyxEntry; +}; + +type EnablePaymentsPageProps = EnablePaymentsPageOnyxProps; + +function EnablePaymentsPage({userWallet}: EnablePaymentsPageProps) { + const {translate} = useLocalize(); + const {isOffline} = useNetwork(); + + const {isPendingOnfidoResult, hasFailedOnfido} = userWallet ?? {}; + + useEffect(() => { + if (isOffline) { + return; + } + + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (isPendingOnfidoResult || hasFailedOnfido) { + Navigation.navigate(ROUTES.SETTINGS_WALLET, CONST.NAVIGATION.TYPE.UP); + return; + } + + Wallet.openEnablePaymentsPage(); + }, [isOffline, isPendingOnfidoResult, hasFailedOnfido]); + + if (isEmptyObject(userWallet)) { + return ; + } + + return ( + + {() => { + if (userWallet?.errorCode === CONST.WALLET.ERROR.KYC) { + return ( + <> + Navigation.goBack(ROUTES.SETTINGS_WALLET)} + /> + + + ); + } + + const currentStep = userWallet?.currentStep || CONST.WALLET.STEP.ADDITIONAL_DETAILS; + + switch (currentStep) { + case CONST.WALLET.STEP.ADDITIONAL_DETAILS: + case CONST.WALLET.STEP.ADDITIONAL_DETAILS_KBA: + return ; + case CONST.WALLET.STEP.ONFIDO: + return ; + case CONST.WALLET.STEP.TERMS: + return ; + case CONST.WALLET.STEP.ACTIVATE: + return ; + default: + return null; + } + }} + + ); +} + +EnablePaymentsPage.displayName = 'EnablePaymentsPage'; + +export default withOnyx({ + userWallet: { + key: ONYXKEYS.USER_WALLET, + + // We want to refresh the wallet each time the user attempts to activate the wallet so we won't use the + // stored values here. + initWithStoredValues: false, + }, +})(EnablePaymentsPage); diff --git a/src/pages/EnablePayments/PersonalInfo/PersonalInfo.tsx b/src/pages/EnablePayments/PersonalInfo/PersonalInfo.tsx index c23b8bcb12fa..22e9a3731581 100644 --- a/src/pages/EnablePayments/PersonalInfo/PersonalInfo.tsx +++ b/src/pages/EnablePayments/PersonalInfo/PersonalInfo.tsx @@ -9,8 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import useSubStep from '@hooks/useSubStep'; import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; -// TODO: uncomment after connecting steps https://github.com/Expensify/App/issues/36648 -// import {parsePhoneNumber} from '@libs/PhoneNumber'; +import {parsePhoneNumber} from '@libs/PhoneNumber'; import Navigation from '@navigation/Navigation'; import getInitialSubstepForPersonalInfo from '@pages/EnablePayments/utils/getInitialSubstepForPersonalInfo'; import getSubstepValues from '@pages/EnablePayments/utils/getSubstepValues'; @@ -47,22 +46,19 @@ function PersonalInfoPage({walletAdditionalDetails, walletAdditionalDetailsDraft const values = useMemo(() => getSubstepValues(PERSONAL_INFO_STEP_KEYS, walletAdditionalDetailsDraft, walletAdditionalDetails), [walletAdditionalDetails, walletAdditionalDetailsDraft]); const submit = () => { - // TODO: uncomment after connecting steps https://github.com/Expensify/App/issues/36648 - // const personalDetails = { - // phoneNumber: (values.phoneNumber && parsePhoneNumber(values.phoneNumber, {regionCode: CONST.COUNTRY.US}).number?.significant) ?? '', - // legalFirstName: values?.[PERSONAL_INFO_STEP_KEYS.FIRST_NAME] ?? '', - // legalLastName: values?.[PERSONAL_INFO_STEP_KEYS.LAST_NAME] ?? '', - // addressStreet: values?.[PERSONAL_INFO_STEP_KEYS.STREET] ?? '', - // addressCity: values?.[PERSONAL_INFO_STEP_KEYS.CITY] ?? '', - // addressState: values?.[PERSONAL_INFO_STEP_KEYS.STATE] ?? '', - // addressZip: values?.[PERSONAL_INFO_STEP_KEYS.ZIP_CODE] ?? '', - // dob: values?.[PERSONAL_INFO_STEP_KEYS.DOB] ?? '', - // ssn: values?.[PERSONAL_INFO_STEP_KEYS.SSN_LAST_4] ?? '', - // }; + const personalDetails = { + phoneNumber: (values.phoneNumber && parsePhoneNumber(values.phoneNumber, {regionCode: CONST.COUNTRY.US}).number?.significant) ?? '', + legalFirstName: values?.[PERSONAL_INFO_STEP_KEYS.FIRST_NAME] ?? '', + legalLastName: values?.[PERSONAL_INFO_STEP_KEYS.LAST_NAME] ?? '', + addressStreet: values?.[PERSONAL_INFO_STEP_KEYS.STREET] ?? '', + addressCity: values?.[PERSONAL_INFO_STEP_KEYS.CITY] ?? '', + addressState: values?.[PERSONAL_INFO_STEP_KEYS.STATE] ?? '', + addressZip: values?.[PERSONAL_INFO_STEP_KEYS.ZIP_CODE] ?? '', + dob: values?.[PERSONAL_INFO_STEP_KEYS.DOB] ?? '', + ssn: values?.[PERSONAL_INFO_STEP_KEYS.SSN_LAST_4] ?? '', + }; // Attempt to set the personal details - // Wallet.updatePersonalDetails(personalDetails); - Navigation.goBack(ROUTES.SETTINGS_WALLET); - Wallet.resetWalletAdditionalDetailsDraft(); + Wallet.updatePersonalDetails(personalDetails); }; const startFrom = useMemo(() => getInitialSubstepForPersonalInfo(values), [values]); diff --git a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx index f402f52255ab..40aab5ba8b35 100644 --- a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx +++ b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx @@ -13,6 +13,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Growl from '@libs/Growl'; import * as BankAccounts from '@userActions/BankAccounts'; +import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalBankAccount, WalletOnfido} from '@src/types/onyx'; @@ -36,14 +37,11 @@ type VerifyIdentityOnyxProps = { walletOnfidoData: OnyxEntry; }; -type VerifyIdentityProps = VerifyIdentityOnyxProps & { - /** Goes to the previous step */ - onBackButtonPress: () => void; -}; +type VerifyIdentityProps = VerifyIdentityOnyxProps; const ONFIDO_ERROR_DISPLAY_DURATION = 10000; -function VerifyIdentity({personalBankAccount, onBackButtonPress, onfidoApplicantID, onfidoToken, walletOnfidoData = DEFAULT_WALLET_ONFIDO_DATA}: VerifyIdentityProps) { +function VerifyIdentity({personalBankAccount, onfidoApplicantID, onfidoToken, walletOnfidoData = DEFAULT_WALLET_ONFIDO_DATA}: VerifyIdentityProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -72,16 +70,20 @@ function VerifyIdentity({personalBankAccount, onBackButtonPress, onfidoApplicant // BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); }; + const handleBackButtonPress = () => { + Wallet.updateCurrentStep(CONST.WALLET.STEP.ADDITIONAL_DETAILS); + }; + return ( From 65ae4fc83b29659687fbe6dc35cbcac4146289d0 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 8 May 2024 18:46:01 +0800 Subject: [PATCH 030/565] set the initial value of the status bar --- web/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/index.html b/web/index.html index 115803573bbd..013d87ec64a4 100644 --- a/web/index.html +++ b/web/index.html @@ -4,7 +4,7 @@ New Expensify - + From 97408438b6830a544d9e5fb504f9e32dfa98f0a6 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Wed, 8 May 2024 14:56:44 +0200 Subject: [PATCH 031/565] Fix focus and tab navigation in Context Menu --- src/components/ContextMenuItem.tsx | 5 +++ src/components/MenuItem.tsx | 6 ++- src/components/ScreenWrapper.tsx | 39 ++++++++++--------- src/hooks/useScreenWrapperTransitionStatus.ts | 6 +++ src/hooks/useSyncFocus/index.ts | 6 ++- .../BaseReportActionContextMenu.tsx | 2 + 6 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/components/ContextMenuItem.tsx b/src/components/ContextMenuItem.tsx index f252cc5b734f..2cc6a0ecec44 100644 --- a/src/components/ContextMenuItem.tsx +++ b/src/components/ContextMenuItem.tsx @@ -52,6 +52,9 @@ type ContextMenuItemProps = { /** Handles what to do when the item is focused */ onFocus?: () => void; + + /** Handles what to do when the item loose focus */ + onBlur?: () => void; }; type ContextMenuItemHandle = { @@ -74,6 +77,7 @@ function ContextMenuItem( shouldPreventDefaultFocusOnPress = true, buttonRef = {current: null}, onFocus = () => {}, + onBlur = () => {}, }: ContextMenuItemProps, ref: ForwardedRef, ) { @@ -130,6 +134,7 @@ function ContextMenuItem( focused={isFocused} interactive={isThrottledButtonActive} onFocus={onFocus} + onBlur={onBlur} /> ); } diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 42de7e2fb7f4..b796337813ef 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -265,6 +265,9 @@ type MenuItemBaseProps = { /** Handles what to do when the item is focused */ onFocus?: () => void; + + /** Handles what to do when the item loose focus */ + onBlur?: () => void; }; type MenuItemProps = (IconProps | AvatarProps | NoIcon) & MenuItemBaseProps; @@ -342,6 +345,7 @@ function MenuItem( isPaneMenu = false, shouldPutLeftPaddingWhenNoIcon = false, onFocus, + onBlur, }: MenuItemProps, ref: PressableRef, ) { @@ -438,7 +442,7 @@ function MenuItem( }; return ( - + {!!label && !isLabelHoverable && ( {label} diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index f7f5a90aa765..111b1b583e30 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -100,7 +100,9 @@ type ScreenWrapperProps = { shouldShowOfflineIndicatorInWideScreen?: boolean; }; -const ScreenWrapperStatusContext = createContext({didScreenTransitionEnd: false}); +type ScreenWrapperStatusContextType = {didScreenTransitionEnd: boolean}; + +const ScreenWrapperStatusContext = createContext(undefined); function ScreenWrapper( { @@ -257,24 +259,23 @@ function ScreenWrapper( {isDevelopment && } - - { - // If props.children is a function, call it to provide the insets to the children. - typeof children === 'function' - ? children({ - insets, - safeAreaPaddingBottomStyle, - didScreenTransitionEnd, - }) - : children - } - {isSmallScreenWidth && shouldShowOfflineIndicator && } - {!isSmallScreenWidth && shouldShowOfflineIndicatorInWideScreen && ( - - )} + { + // If props.children is a function, call it to provide the insets to the children. + typeof children === 'function' + ? children({ + insets, + safeAreaPaddingBottomStyle, + didScreenTransitionEnd, + }) + : children + } + {isSmallScreenWidth && shouldShowOfflineIndicator && } + {!isSmallScreenWidth && shouldShowOfflineIndicatorInWideScreen && ( + + )} diff --git a/src/hooks/useScreenWrapperTransitionStatus.ts b/src/hooks/useScreenWrapperTransitionStatus.ts index b9e94abfc024..8692677fdb68 100644 --- a/src/hooks/useScreenWrapperTransitionStatus.ts +++ b/src/hooks/useScreenWrapperTransitionStatus.ts @@ -15,3 +15,9 @@ export default function useScreenWrapperTranstionStatus() { return value; } + +function useScreenWrapperTransitionStatusUnsafe() { + return useContext(ScreenWrapperStatusContext); +} + +export {useScreenWrapperTransitionStatusUnsafe}; diff --git a/src/hooks/useSyncFocus/index.ts b/src/hooks/useSyncFocus/index.ts index afc4e7e351ab..e0c68c584b82 100644 --- a/src/hooks/useSyncFocus/index.ts +++ b/src/hooks/useSyncFocus/index.ts @@ -1,7 +1,7 @@ import {useLayoutEffect} from 'react'; import type {RefObject} from 'react'; import type {View} from 'react-native'; -import useScreenWrapperTranstionStatus from '@hooks/useScreenWrapperTransitionStatus'; +import {useScreenWrapperTransitionStatusUnsafe} from '@hooks/useScreenWrapperTransitionStatus'; /** * Custom React hook created to handle sync of focus on an element when the user navigates through the app with keyboard. @@ -9,7 +9,9 @@ import useScreenWrapperTranstionStatus from '@hooks/useScreenWrapperTransitionSt * To maintain consistency when an element is focused in the app, the focus() method is additionally called on the focused element to eliminate the difference between native browser focus and application focus. */ const useSyncFocus = (ref: RefObject, isFocused: boolean, shouldSyncFocus = true) => { - const {didScreenTransitionEnd} = useScreenWrapperTranstionStatus(); + const contextValue = useScreenWrapperTransitionStatusUnsafe(); + + const didScreenTransitionEnd = contextValue ? contextValue.didScreenTransitionEnd : true; useLayoutEffect(() => { if (!isFocused || !shouldSyncFocus || !didScreenTransitionEnd) { diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index fe872ee1681a..7a95c371f179 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -164,6 +164,7 @@ function BaseReportActionContextMenu({ disabledIndexes, maxIndex: filteredContextMenuActions.length - 1, isActive: shouldEnableArrowNavigation, + disableCyclicTraversal: true, }); /** @@ -285,6 +286,7 @@ function BaseReportActionContextMenu({ isFocused={focusedIndex === index} shouldPreventDefaultFocusOnPress={contextAction.shouldPreventDefaultFocusOnPress} onFocus={() => setFocusedIndex(index)} + onBlur={() => (index === filteredContextMenuActions.length - 1 || index === 1) && setFocusedIndex(-1)} /> ); })} From 368610e6e9a346aa7717e8b1919826cdc4126ccb Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Wed, 8 May 2024 16:43:12 +0200 Subject: [PATCH 032/565] Point to new search screen in autofocus --- src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts b/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts index 439c45a8bd00..b88e51f6caaf 100644 --- a/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts +++ b/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts @@ -2,7 +2,7 @@ import SCREENS from '@src/SCREENS'; const SCREENS_WITH_AUTOFOCUS: string[] = [ SCREENS.WORKSPACE_SWITCHER.ROOT, - SCREENS.SEARCH_ROOT, + SCREENS.SEARCH.CENTRAL_PANE, SCREENS.REPORT, SCREENS.REPORT_DESCRIPTION_ROOT, SCREENS.PRIVATE_NOTES.EDIT, From 5ced21b8a4908373fd14a7bc76eca0cf09525e88 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Thu, 9 May 2024 09:35:49 +0200 Subject: [PATCH 033/565] Fix linter --- src/components/HeaderWithBackButton/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 2f32de92e80a..70ddb0888972 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -6,7 +6,6 @@ import Header from '@components/Header'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PinButton from '@components/PinButton'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; import Tooltip from '@components/Tooltip'; From 9928153fe28701f9305e48432234d2693e8f5892 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Thu, 9 May 2024 10:49:35 +0200 Subject: [PATCH 034/565] Apply review comment --- src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts b/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts index b62c3b563594..2056073eb054 100644 --- a/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts +++ b/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts @@ -1,5 +1,6 @@ +import type { BottomTabName } from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; -const BOTTOM_TAB_SCREENS: string[] = [SCREENS.HOME, SCREENS.SETTINGS.ROOT]; +const BOTTOM_TAB_SCREENS: BottomTabName[] = [SCREENS.HOME, SCREENS.SETTINGS.ROOT]; export default BOTTOM_TAB_SCREENS; From 4400aca0b2948f8a2945e16335975152851df5b6 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 10 May 2024 12:13:24 +0200 Subject: [PATCH 035/565] Removing compose.ts --- src/components/LocaleContextProvider.tsx | 12 +++---- src/components/MapView/MapView.tsx | 8 ++--- src/components/TestToolMenu.tsx | 8 ++--- .../withReportAndReportActionOrNotFound.tsx | 8 ++--- .../WorkspaceRateAndUnitPage/InitialPage.tsx | 31 +++++++++---------- .../WorkspaceRateAndUnitPage/RatePage.tsx | 12 +++---- .../WorkspaceRateAndUnitPage/UnitPage.tsx | 13 +++----- .../withPolicyAndFullscreenLoading.tsx | 8 ++--- 8 files changed, 42 insertions(+), 58 deletions(-) diff --git a/src/components/LocaleContextProvider.tsx b/src/components/LocaleContextProvider.tsx index eb7d9324d2ab..e0e30d14d2a2 100644 --- a/src/components/LocaleContextProvider.tsx +++ b/src/components/LocaleContextProvider.tsx @@ -2,7 +2,6 @@ import React, {createContext, useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import compose from '@libs/compose'; import DateUtils from '@libs/DateUtils'; import * as LocaleDigitUtils from '@libs/LocaleDigitUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; @@ -125,18 +124,17 @@ function LocaleContextProvider({preferredLocale, currentUserPersonalDetails = {} return {children}; } -const Provider = compose( +const Provider = withCurrentUserPersonalDetails( withOnyx({ preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, selector: (preferredLocale) => preferredLocale, }, - }), - withCurrentUserPersonalDetails, -)(LocaleContextProvider); + })(LocaleContextProvider), +); Provider.displayName = 'withOnyx(LocaleContextProvider)'; -export {Provider as LocaleContextProvider, LocaleContext}; +export {LocaleContext, Provider as LocaleContextProvider}; -export type {LocaleContextProps, Locale}; +export type {Locale, LocaleContextProps}; diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index b1021350435c..05a7cd221c1e 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -6,7 +6,6 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import useThemeStyles from '@hooks/useThemeStyles'; import setUserLocation from '@libs/actions/UserLocation'; -import compose from '@libs/compose'; import getCurrentPosition from '@libs/getCurrentPosition'; import CONST from '@src/CONST'; import useLocalize from '@src/hooks/useLocalize'; @@ -196,11 +195,10 @@ const MapView = forwardRef( }, ); -export default compose( +export default memo( withOnyx({ userLocation: { key: ONYXKEYS.USER_LOCATION, }, - }), - memo, -)(MapView); + })(MapView), +); diff --git a/src/components/TestToolMenu.tsx b/src/components/TestToolMenu.tsx index e4d33957f7f1..85a2a975fc76 100644 --- a/src/components/TestToolMenu.tsx +++ b/src/components/TestToolMenu.tsx @@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ApiUtils from '@libs/ApiUtils'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as Network from '@userActions/Network'; import * as Session from '@userActions/Session'; @@ -109,11 +108,10 @@ function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) { TestToolMenu.displayName = 'TestToolMenu'; -export default compose( +export default withNetwork()( withOnyx({ user: { key: ONYXKEYS.USER, }, - }), - withNetwork(), -)(TestToolMenu); + })(TestToolMenu), +); diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index e5e203fb5030..123f7b12cb22 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -7,7 +7,6 @@ import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import withWindowDimensions from '@components/withWindowDimensions'; import type {WindowDimensionsProps} from '@components/withWindowDimensions/types'; -import compose from '@libs/compose'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import type {FlagCommentNavigatorParamList, SplitDetailsNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; @@ -103,7 +102,7 @@ export default function , OnyxProps>({ report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, @@ -138,9 +137,8 @@ export default function ({ - // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM - reimbursementAccount: { - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - }, - workspaceRateAndUnit: { - key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, - }, - }), - withPolicy, - withNetwork(), -)(WorkspaceRateAndUnitPage); +export default withNetwork()( + withPolicy( + withOnyx({ + // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, + workspaceRateAndUnit: { + key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, + }, + })(WorkspaceRateAndUnitPage), + ), +); diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx index 8685cd3b1aee..e265e261b6b8 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx @@ -1,17 +1,16 @@ import React, {useCallback, useEffect, useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import AmountForm from '@components/AmountForm'; import FormProvider from '@components/Form/FormProvider'; import InputWrapperWithRef from '@components/Form/InputWrapper'; import type {FormOnyxValues} from '@components/Form/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import {validateRateValue} from '@libs/PolicyDistanceRatesUtils'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -99,11 +98,10 @@ function WorkspaceRatePage(props: WorkspaceRatePageProps) { WorkspaceRatePage.displayName = 'WorkspaceRatePage'; -export default compose( +export default withPolicy( withOnyx({ workspaceRateAndUnit: { key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, }, - }), - withPolicy, -)(WorkspaceRatePage); + })(WorkspaceRatePage), +); diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx index 1d30c068e30d..6337fa2c461e 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx @@ -1,15 +1,14 @@ import React, {useEffect, useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import Text from '@components/Text'; import type {UnitItemType} from '@components/UnitPicker'; import UnitPicker from '@components/UnitPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -70,12 +69,10 @@ function WorkspaceUnitPage(props: WorkspaceUnitPageProps) { } WorkspaceUnitPage.displayName = 'WorkspaceUnitPage'; - -export default compose( +export default withPolicy( withOnyx({ workspaceRateAndUnit: { key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, }, - }), - withPolicy, -)(WorkspaceUnitPage); + })(WorkspaceUnitPage), +); diff --git a/src/pages/workspace/withPolicyAndFullscreenLoading.tsx b/src/pages/workspace/withPolicyAndFullscreenLoading.tsx index 161320441843..2467136a382b 100644 --- a/src/pages/workspace/withPolicyAndFullscreenLoading.tsx +++ b/src/pages/workspace/withPolicyAndFullscreenLoading.tsx @@ -4,7 +4,6 @@ import React, {forwardRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import compose from '@libs/compose'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList} from '@src/types/onyx'; import type {WithPolicyOnyxProps, WithPolicyProps} from './withPolicy'; @@ -49,7 +48,7 @@ export default function withPolicyAndFullscreenLoading, WithPolicyAndFullscreenLoadingOnyxProps>({ isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, @@ -57,9 +56,8 @@ export default function withPolicyAndFullscreenLoading Date: Fri, 10 May 2024 12:20:17 +0200 Subject: [PATCH 036/565] Refactor LHNOptionsList to use useOnyx --- .../LHNOptionsList/LHNOptionsList.tsx | 58 +++++-------------- src/components/LHNOptionsList/types.ts | 30 +--------- 2 files changed, 15 insertions(+), 73 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 8c43ae542932..b0ef2a4edaef 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -4,7 +4,7 @@ import {FlashList} from '@shopify/flash-list'; import type {ReactElement} from 'react'; import React, {memo, useCallback, useContext, useEffect, useMemo, useRef} from 'react'; import {StyleSheet, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import BlockingView from '@components/BlockingViews/BlockingView'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -23,31 +23,24 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import OptionRowLHNData from './OptionRowLHNData'; -import type {LHNOptionsListOnyxProps, LHNOptionsListProps, RenderItemProps} from './types'; +import type {LHNOptionsListProps, RenderItemProps} from './types'; const keyExtractor = (item: string) => `report_${item}`; -function LHNOptionsList({ - style, - contentContainerStyles, - data, - onSelectRow, - optionMode, - shouldDisableFocusOptions = false, - reports = {}, - reportActions = {}, - policy = {}, - preferredLocale = CONST.LOCALES.DEFAULT, - personalDetails = {}, - transactions = {}, - draftComments = {}, - transactionViolations = {}, - onFirstItemRendered = () => {}, -}: LHNOptionsListProps) { +function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optionMode, shouldDisableFocusOptions = false, onFirstItemRendered = () => {}}: LHNOptionsListProps) { const {saveScrollOffset, getScrollOffset} = useContext(ScrollOffsetContext); const flashListRef = useRef>(null); const route = useRoute(); + const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); + const [policy] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE, {initialValue: CONST.LOCALES.DEFAULT}); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); + const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); + const theme = useTheme(); const styles = useThemeStyles(); const {canUseViolations} = usePermissions(); @@ -246,31 +239,6 @@ function LHNOptionsList({ LHNOptionsList.displayName = 'LHNOptionsList'; -export default withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - reportActions: { - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - }, - policy: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - preferredLocale: { - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - transactions: { - key: ONYXKEYS.COLLECTION.TRANSACTION, - }, - draftComments: { - key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, - }, - transactionViolations: { - key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, - }, -})(memo(LHNOptionsList)); +export default memo(LHNOptionsList); export type {LHNOptionsListProps}; diff --git a/src/components/LHNOptionsList/types.ts b/src/components/LHNOptionsList/types.ts index 0f0c921747b4..7248742654d2 100644 --- a/src/components/LHNOptionsList/types.ts +++ b/src/components/LHNOptionsList/types.ts @@ -10,32 +10,6 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; type OptionMode = ValueOf; -type LHNOptionsListOnyxProps = { - /** The policy which the user has access to and which the report could be tied to */ - policy: OnyxCollection; - - /** All reports shared with the user */ - reports: OnyxCollection; - - /** Array of report actions for this report */ - reportActions: OnyxCollection; - - /** Indicates which locale the user currently has selected */ - preferredLocale: OnyxEntry; - - /** List of users' personal details */ - personalDetails: OnyxEntry; - - /** The transaction from the parent report action */ - transactions: OnyxCollection; - - /** List of draft comments */ - draftComments: OnyxCollection; - - /** The list of transaction violations */ - transactionViolations: OnyxCollection; -}; - type CustomLHNOptionsListProps = { /** Wrapper style for the section list */ style?: StyleProp; @@ -59,7 +33,7 @@ type CustomLHNOptionsListProps = { onFirstItemRendered: () => void; }; -type LHNOptionsListProps = CustomLHNOptionsListProps & LHNOptionsListOnyxProps; +type LHNOptionsListProps = CustomLHNOptionsListProps; type OptionRowLHNDataProps = { /** Whether row should be focused */ @@ -141,4 +115,4 @@ type OptionRowLHNProps = { type RenderItemProps = {item: string}; -export type {LHNOptionsListProps, OptionRowLHNDataProps, OptionRowLHNProps, LHNOptionsListOnyxProps, RenderItemProps}; +export type {LHNOptionsListProps, OptionRowLHNDataProps, OptionRowLHNProps, RenderItemProps}; From 575b0be19bdd958a0de0d4d73443418bf90fb885 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 10 May 2024 16:51:38 +0200 Subject: [PATCH 037/565] fix SidebarTest --- tests/unit/SidebarTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index c037c1ced3f0..10f9b4afd4dd 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -53,9 +53,9 @@ describe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) // When Onyx is updated with the data and the sidebar re-renders .then(() => { const reportCollection: ReportCollectionDataSet = { @@ -105,9 +105,9 @@ describe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) // When Onyx is updated with the data and the sidebar re-renders .then(() => { const reportCollection: ReportCollectionDataSet = { From e903c1df50dc13098e32a28e03c12d17ac569c56 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 10 May 2024 17:02:02 +0200 Subject: [PATCH 038/565] fix SidebarOrderTest --- tests/unit/SidebarOrderTest.ts | 109 ++++++++++++++++----------------- 1 file changed, 53 insertions(+), 56 deletions(-) diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 644bba5a589b..a5285fe186a2 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -62,33 +62,29 @@ describe('Sidebar', () => { expect(screen.toJSON()).toBe(null); }); - it('is rendered with an empty list when personal details exist', () => { - // Given the sidebar is rendered with default props - LHNTestUtils.getDefaultRenderedSidebarLinks(); - - return ( - waitForBatchedUpdates() - // When Onyx is updated with some personal details - .then(() => - Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, - [ONYXKEYS.IS_LOADING_APP]: false, - }), - ) - - // Then the component should be rendered with an empty list since it will get past the early return - .then(() => { - expect(screen.toJSON()).not.toBe(null); - const navigatesToChatHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - expect(screen.queryAllByAccessibilityHint(navigatesToChatHintText)).toHaveLength(0); - }) - ); - }); + it('is rendered with an empty list when personal details exist', () => + waitForBatchedUpdates() + // Given the sidebar is rendered with default props + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) + + // When Onyx is updated with some personal details + .then(() => + Onyx.multiSet({ + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.IS_LOADING_APP]: false, + }), + ) + + // Then the component should be rendered with an empty list since it will get past the early return + .then(() => { + expect(screen.toJSON()).not.toBe(null); + const navigatesToChatHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); + expect(screen.queryAllByAccessibilityHint(navigatesToChatHintText)).toHaveLength(0); + })); it('contains one report when a report is in Onyx', () => { // Given a single report const report = LHNTestUtils.getFakeReport([1, 2]); - LHNTestUtils.getDefaultRenderedSidebarLinks(report.reportID); const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, @@ -96,6 +92,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks(report.reportID)) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -114,8 +112,6 @@ describe('Sidebar', () => { }); it('orders items with most recently updated on top', () => { - LHNTestUtils.getDefaultRenderedSidebarLinks(); - // Given three unread reports in the recently updated order of 3, 2, 1 const report1 = LHNTestUtils.getFakeReport([1, 2], 3); const report2 = LHNTestUtils.getFakeReport([3, 4], 2); @@ -134,6 +130,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks()) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -173,8 +171,6 @@ describe('Sidebar', () => { Report.addComment(report3.reportID, 'Hi, this is a comment'); const currentReportId = report1.reportID; - LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -183,6 +179,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId)) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -211,8 +209,6 @@ describe('Sidebar', () => { }); it('reorders the reports to always have the most recently updated one on top', () => { - LHNTestUtils.getDefaultRenderedSidebarLinks(); - // Given three reports in the recently updated order of 3, 2, 1 const report1 = LHNTestUtils.getFakeReport([1, 2], 3); const report2 = LHNTestUtils.getFakeReport([3, 4], 2); @@ -231,6 +227,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks()) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -282,8 +280,6 @@ describe('Sidebar', () => { Report.addComment(report2.reportID, 'Hi, this is a comment'); Report.addComment(report3.reportID, 'Hi, this is a comment'); - LHNTestUtils.getDefaultRenderedSidebarLinks(taskReport.reportID); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -293,6 +289,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks(taskReport.reportID)) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -346,8 +344,6 @@ describe('Sidebar', () => { Report.addComment(report2.reportID, 'Hi, this is a comment'); Report.addComment(report3.reportID, 'Hi, this is a comment'); - LHNTestUtils.getDefaultRenderedSidebarLinks(report3.reportID); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -357,6 +353,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks(report3.reportID)) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -413,8 +411,6 @@ describe('Sidebar', () => { Report.addComment(report2.reportID, 'Hi, this is a comment'); Report.addComment(report3.reportID, 'Hi, this is a comment'); - LHNTestUtils.getDefaultRenderedSidebarLinks(report3.reportID); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -424,6 +420,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks(report3.reportID)) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -464,7 +462,6 @@ describe('Sidebar', () => { Report.addComment(report3.reportID, 'Hi, this is a comment'); const currentReportId = report2.reportID; - LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId); const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, @@ -474,6 +471,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId)) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -507,8 +506,6 @@ describe('Sidebar', () => { }); it('removes the pencil icon when draft is removed', () => { - LHNTestUtils.getDefaultRenderedSidebarLinks(); - // Given a single report // And the report has a draft const report: OnyxTypes.Report = { @@ -521,6 +518,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks()) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -548,8 +547,6 @@ describe('Sidebar', () => { }); it('removes the pin icon when chat is unpinned', () => { - LHNTestUtils.getDefaultRenderedSidebarLinks(); - // Given a single report // And the report is pinned const report: OnyxTypes.Report = { @@ -563,6 +560,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks()) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -624,8 +623,6 @@ describe('Sidebar', () => { const currentReportId = report2.reportID; const currentlyLoggedInUserAccountID = 9; - LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -635,6 +632,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId)) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -683,8 +682,6 @@ describe('Sidebar', () => { isPinned: true, }; - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -693,6 +690,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -745,8 +744,6 @@ describe('Sidebar', () => { ...LHNTestUtils.getFakeReport([7, 8], 0), }; - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -761,6 +758,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -824,8 +823,6 @@ describe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -834,6 +831,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -860,8 +859,6 @@ describe('Sidebar', () => { describe('in #focus mode', () => { it('alphabetizes chats', () => { - LHNTestUtils.getDefaultRenderedSidebarLinks(); - const report1 = LHNTestUtils.getFakeReport([1, 2], 3, true); const report2 = LHNTestUtils.getFakeReport([3, 4], 2, true); const report3 = LHNTestUtils.getFakeReport([5, 6], 1, true); @@ -875,6 +872,7 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) // Given the sidebar is rendered in #focus mode (hides read chats) // with all reports having unread comments .then(() => @@ -926,8 +924,6 @@ describe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -936,6 +932,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -1060,8 +1058,6 @@ describe('Sidebar', () => { const currentlyLoggedInUserAccountID = 13; - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -1077,6 +1073,7 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ @@ -1123,8 +1120,6 @@ describe('Sidebar', () => { Report.addComment(report2.reportID, 'Hi, this is a comment'); Report.addComment(report3.reportID, 'Hi, this is a comment'); - LHNTestUtils.getDefaultRenderedSidebarLinks('0'); - const reportCollectionDataSet: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, @@ -1133,6 +1128,8 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks('0')) + // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ From 41862adbc879c1e44d6ee980b3fc87ac70779a28 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Mon, 13 May 2024 09:23:45 +0200 Subject: [PATCH 039/565] Fix ts --- src/components/FocusTrap/FocusTrapForScreen/index.web.tsx | 2 +- src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx index 3dd12f232d25..afcc185ff18c 100644 --- a/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx +++ b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx @@ -17,7 +17,7 @@ function FocusTrap({children}: FocusTrapProps) { const isActive = useMemo(() => { // Focus trap can't be active on bottom tab screens because it would block access to the tab bar. - if (BOTTOM_TAB_SCREENS.includes(route.name)) { + if (BOTTOM_TAB_SCREENS.find((screen) => screen === route.name)) { return false; } diff --git a/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts b/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts index 6815dcc455a9..e1277d8d4409 100644 --- a/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts +++ b/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts @@ -12,9 +12,7 @@ const WIDE_LAYOUT_INACTIVE_SCREENS: string[] = [ SCREENS.SETTINGS.WALLET.ROOT, SCREENS.SETTINGS.ABOUT, SCREENS.SETTINGS.WORKSPACES, - SCREENS.WORKSPACE.INITIAL, - SCREENS.WORKSPACE.PROFILE, SCREENS.WORKSPACE.CARD, SCREENS.WORKSPACE.WORKFLOWS, From 6e3ba8ca2e731198963df2f7c31a1603f70574af Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Mon, 13 May 2024 14:15:07 +0200 Subject: [PATCH 040/565] Fix prettier --- src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts b/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts index 2056073eb054..e6af466b12c5 100644 --- a/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts +++ b/src/components/FocusTrap/BOTTOM_TAB_SCREENS.ts @@ -1,4 +1,4 @@ -import type { BottomTabName } from '@libs/Navigation/types'; +import type {BottomTabName} from '@libs/Navigation/types'; import SCREENS from '@src/SCREENS'; const BOTTOM_TAB_SCREENS: BottomTabName[] = [SCREENS.HOME, SCREENS.SETTINGS.ROOT]; From 98c0acfaae9c511e2eb871b968023e7fe0c752da Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 13 May 2024 15:33:53 +0200 Subject: [PATCH 041/565] remove leftovers --- src/pages/tasks/TaskDescriptionPage.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index 4a9a7f870230..7b3dd8813540 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -30,8 +29,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; type TaskDescriptionPageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps; -const parser = new ExpensiMark(); - function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescriptionPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -48,7 +45,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti const submit = useCallback( (values: FormOnyxValues) => { - if (values.description !== parseHtmlToMarkdown(parser.replace(report?.description ?? '')) && !isEmptyObject(report)) { + if (values.description !== parseHtmlToMarkdown(report?.description ?? '') && !isEmptyObject(report)) { // Set the description of the report in the store and then call EditTask API // to update the description of the report on the server Task.editTask(report, {description: values.description}); From 7556c6027f6529a49f688b40f98d0a24968f2ff0 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 13 May 2024 16:09:38 +0200 Subject: [PATCH 042/565] removing compose.ts --- src/libs/compose.ts | 71 --------------------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 src/libs/compose.ts diff --git a/src/libs/compose.ts b/src/libs/compose.ts deleted file mode 100644 index dadc586d0f0d..000000000000 --- a/src/libs/compose.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-types */ -/* eslint-disable import/export */ - -/** - * This is a utility function taken directly from Redux. (We don't want to add Redux as a dependency) - * It enables functional composition, useful for the chaining/composition of HOCs. - * - * For example, instead of: - * - * export default hoc1(config1, hoc2(config2, hoc3(config3)))(Component); - * - * Use this instead: - * - * export default compose( - * hoc1(config1), - * hoc2(config2), - * hoc3(config3), - * )(Component) - */ -export default function compose(): (a: R) => R; - -export default function compose(f: F): F; - -/* two functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2; - -/* three functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (...args: A) => R3; - -/* four functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (...args: A) => R4; - -/* five functions */ -export default function compose( - f1: (...args: A) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, -): (...args: A) => R5; - -/* six functions */ -export default function compose( - f1: (...args: A) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, - f6: (a: R5) => R6, -): (...args: A) => R6; - -/* rest */ -export default function compose(f1: (a: unknown) => R, ...funcs: Function[]): (...args: unknown[]) => R; - -export default function compose(...funcs: Function[]): Function { - if (funcs.length === 0) { - // infer the argument type so it is usable in inference down the line - return (arg: T) => arg; - } - - if (funcs.length === 1) { - return funcs[0]; - } - - return funcs.reduce( - (a, b) => - (...args: unknown[]) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - a(b(...args)), - ); -} From 1ac3596a90e6cef9484ac783c0d13075a7f146ab Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Tue, 14 May 2024 12:19:44 +0200 Subject: [PATCH 043/565] useCallback --- .../FocusTrap/FocusTrapForScreen/index.web.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx index afcc185ff18c..e4b2c7f8d39f 100644 --- a/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx +++ b/src/components/FocusTrap/FocusTrapForScreen/index.web.tsx @@ -1,6 +1,6 @@ import {useFocusEffect, useIsFocused, useRoute} from '@react-navigation/native'; import FocusTrapOriginal from 'focus-trap-react'; -import React, {useMemo} from 'react'; +import React, {useCallback, useMemo} from 'react'; import BOTTOM_TAB_SCREENS from '@components/FocusTrap/BOTTOM_TAB_SCREENS'; import SCREENS_WITH_AUTOFOCUS from '@components/FocusTrap/SCREENS_WITH_AUTOFOCUS'; import sharedTrapStack from '@components/FocusTrap/sharedTrapStack'; @@ -28,9 +28,11 @@ function FocusTrap({children}: FocusTrapProps) { return true; }, [isSmallScreenWidth, route]); - useFocusEffect(() => { - activeRouteName = route.name; - }); + useFocusEffect( + useCallback(() => { + activeRouteName = route.name; + }, [route]), + ); return ( Date: Tue, 14 May 2024 18:11:30 +0200 Subject: [PATCH 044/565] replace htmlToText in `OptionRowLHN` --- src/components/LHNOptionsList/OptionRowLHN.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index b665c305f5cf..fe234eb629a4 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useRef, useState} from 'react'; import type {GestureResponderEvent, ViewStyle} from 'react-native'; import {StyleSheet, View} from 'react-native'; @@ -20,6 +19,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import DateUtils from '@libs/DateUtils'; import DomUtils from '@libs/DomUtils'; +import {parseHtmlToText} from '@libs/OnyxAwareParser'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; @@ -29,8 +29,6 @@ import CONST from '@src/CONST'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {OptionRowLHNProps} from './types'; -const parser = new ExpensiMark(); - function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, optionItem, viewMode = 'default', style, onLayout = () => {}, hasDraftComment}: OptionRowLHNProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -228,7 +226,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti numberOfLines={1} accessibilityLabel={translate('accessibilityHints.lastChatMessagePreview')} > - {parser.htmlToText(optionItem.alternateText)} + {parseHtmlToText(optionItem.alternateText)}
) : null}
From 296e9a184287cf15901190e8ebb1069b9441bead Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Wed, 15 May 2024 09:20:32 +0200 Subject: [PATCH 045/565] Workspace screens in autofocus --- src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts | 3 ++- .../AppNavigator/Navigators/FullScreenNavigator.tsx | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts b/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts index b88e51f6caaf..7fcaa8a15045 100644 --- a/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts +++ b/src/components/FocusTrap/SCREENS_WITH_AUTOFOCUS.ts @@ -1,7 +1,8 @@ +import {CENTRAL_PANE_WORKSPACE_SCREENS} from '@libs/Navigation/AppNavigator/Navigators/FullScreenNavigator'; import SCREENS from '@src/SCREENS'; const SCREENS_WITH_AUTOFOCUS: string[] = [ - SCREENS.WORKSPACE_SWITCHER.ROOT, + ...Object.keys(CENTRAL_PANE_WORKSPACE_SCREENS), SCREENS.SEARCH.CENTRAL_PANE, SCREENS.REPORT, SCREENS.REPORT_DESCRIPTION_ROOT, diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 41ed26b5ff1b..64485872544f 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -15,7 +15,7 @@ const RootStack = createCustomFullScreenNavigator(); type Screens = Partial React.ComponentType>>; -const centralPaneWorkspaceScreens = { +const CENTRAL_PANE_WORKSPACE_SCREENS = { [SCREENS.WORKSPACE.PROFILE]: () => require('../../../../pages/workspace/WorkspaceProfilePage').default as React.ComponentType, [SCREENS.WORKSPACE.CARD]: () => require('../../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS]: () => require('../../../../pages/workspace/workflows/WorkspaceWorkflowsPage').default as React.ComponentType, @@ -47,7 +47,7 @@ function FullScreenNavigator() { options={screenOptions.homeScreen} getComponent={loadWorkspaceInitialPage} /> - {Object.entries(centralPaneWorkspaceScreens).map(([screenName, componentGetter]) => ( + {Object.entries(CENTRAL_PANE_WORKSPACE_SCREENS).map(([screenName, componentGetter]) => ( Date: Wed, 15 May 2024 14:40:43 +0200 Subject: [PATCH 046/565] feat: add verify identity intro page --- src/languages/en.ts | 2 + src/languages/es.ts | 2 + .../VerifyIdentity/VerifyIdentity.tsx | 50 ++++++++++++++++--- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index c174c2182ef2..b079a7ecc624 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1571,6 +1571,8 @@ export default { facialScan: 'Onfido’s Facial Scan Policy and Release', tryAgain: 'Try again', verifyIdentity: 'Verify identity', + letsVerifyIdentity: "Let's verify your identity.", + butFirst: `But first, the boring stuff. Read up on the legalese in the next step and click "Accept" when you're ready.`, genericError: 'There was an error while processing this step. Please try again.', cameraPermissionsNotGranted: 'Enable camera access', cameraRequestMessage: 'We need access to your camera to complete bank account verification. Please enable via Settings > New Expensify.', diff --git a/src/languages/es.ts b/src/languages/es.ts index e9e1ed6eb815..eca0e3ac16ad 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1590,6 +1590,8 @@ export default { facialScan: 'Política y lanzamiento de la exploración facial de Onfido', tryAgain: 'Intentar otra vez', verifyIdentity: 'Verificar identidad', + letsVerifyIdentity: '¡Vamos a verificar tu identidad!', + butFirst: 'Pero primero, lo aburrido. Lee la jerga legal en el siguiente paso y haz clic en "Aceptar" cuando estés listo.', genericError: 'Hubo un error al procesar este paso. Inténtalo de nuevo.', cameraPermissionsNotGranted: 'Permiso para acceder a la cámara', cameraRequestMessage: 'Necesitamos acceso a tu cámara para completar la verificación de tu cuenta de banco. Por favor habilita los permisos en Configuración > Nuevo Expensify.', diff --git a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx index 40aab5ba8b35..0bcb0beb675f 100644 --- a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx +++ b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx @@ -3,14 +3,20 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import FixedFooter from '@components/FixedFooter'; +import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Icon from '@components/Icon'; +import * as Illustrations from '@components/Icon/Illustrations'; import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; import Onfido from '@components/Onfido'; import type {OnfidoData} from '@components/Onfido/types'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; +import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; import Growl from '@libs/Growl'; import * as BankAccounts from '@userActions/BankAccounts'; import * as Wallet from '@userActions/Wallet'; @@ -45,6 +51,8 @@ function VerifyIdentity({personalBankAccount, onfidoApplicantID, onfidoToken, wa const styles = useThemeStyles(); const {translate} = useLocalize(); + console.log({personalBankAccount, onfidoApplicantID, onfidoToken, walletOnfidoData}); + const handleOnfidoSuccess = useCallback( (onfidoData: OnfidoData) => { BankAccounts.verifyIdentity({ @@ -58,6 +66,8 @@ function VerifyIdentity({personalBankAccount, onfidoApplicantID, onfidoToken, wa [personalBankAccount, onfidoApplicantID], ); + const onfidoError = ErrorUtils.getLatestErrorMessage(walletOnfidoData) ?? ''; + const handleOnfidoError = () => { // In case of any unexpected error we log it to the server, show a growl, and return the user back to the requestor step so they can try again. Growl.error(translate('onfidoStep.genericError'), ONFIDO_ERROR_DISPLAY_DURATION); @@ -74,6 +84,8 @@ function VerifyIdentity({personalBankAccount, onfidoApplicantID, onfidoToken, wa Wallet.updateCurrentStep(CONST.WALLET.STEP.ADDITIONAL_DETAILS); }; + const {isLoading = false, hasAcceptedPrivacyPolicy} = walletOnfidoData; + return ( - + {hasAcceptedPrivacyPolicy ? ( + + ) : ( + <> + + + {translate('onfidoStep.letsVerifyIdentity')} + {translate('onfidoStep.butFirst')} + + + {}} + onFixTheErrorsLinkPressed={() => {}} + message={onfidoError} + isLoading={isLoading} + buttonText={onfidoError ? translate('onfidoStep.tryAgain') : translate('common.continue')} + containerStyles={[styles.mh0, styles.mv0, styles.mb0]} + /> + + + )} From 7c78aaafe99891d588cea3f08127e736cfdf777e Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Wed, 15 May 2024 17:38:45 +0200 Subject: [PATCH 047/565] Fix --- src/components/MenuItem.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index e53e0783843b..fe187f49a357 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -32,6 +32,7 @@ import * as Expensicons from './Icon/Expensicons'; import * as defaultWorkspaceAvatars from './Icon/WorkspaceDefaultAvatars'; import {MenuItemGroupContext} from './MenuItemGroup'; import MultipleAvatars from './MultipleAvatars'; +import type {PressableRef} from './Pressable/GenericPressable/types'; import PressableWithSecondaryInteraction from './PressableWithSecondaryInteraction'; import RenderHTML from './RenderHTML'; import SelectCircle from './SelectCircle'; @@ -267,7 +268,7 @@ type MenuItemBaseProps = { /** Handles what to do when the item loose focus */ onBlur?: () => void; - + /** Optional account id if it's user avatar or policy id if it's workspace avatar */ avatarID?: number | string; }; @@ -350,6 +351,7 @@ function MenuItem( onBlur, avatarID, }: MenuItemProps, + ref: PressableRef, ) { const theme = useTheme(); const styles = useThemeStyles(); From 2ec341172ba1daa9bd17d29659b9bfbbf62270f4 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 16 May 2024 20:43:38 +0200 Subject: [PATCH 048/565] fix: get token and applicantID from walletOnfidoData --- .../VerifyIdentity/VerifyIdentity.tsx | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx index 0bcb0beb675f..c2023077af50 100644 --- a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx +++ b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx @@ -47,11 +47,15 @@ type VerifyIdentityProps = VerifyIdentityOnyxProps; const ONFIDO_ERROR_DISPLAY_DURATION = 10000; -function VerifyIdentity({personalBankAccount, onfidoApplicantID, onfidoToken, walletOnfidoData = DEFAULT_WALLET_ONFIDO_DATA}: VerifyIdentityProps) { +function VerifyIdentity({personalBankAccount, walletOnfidoData = DEFAULT_WALLET_ONFIDO_DATA}: VerifyIdentityProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - console.log({personalBankAccount, onfidoApplicantID, onfidoToken, walletOnfidoData}); + const openOnfidoFlow = () => { + BankAccounts.openOnfidoFlow(); + }; + + const {isLoading = false, hasAcceptedPrivacyPolicy, sdkToken, applicantID} = walletOnfidoData; const handleOnfidoSuccess = useCallback( (onfidoData: OnfidoData) => { @@ -63,7 +67,7 @@ function VerifyIdentity({personalBankAccount, onfidoApplicantID, onfidoToken, wa }); BankAccounts.updateAddPersonalBankAccountDraft({isOnfidoSetupComplete: true}); }, - [personalBankAccount, onfidoApplicantID], + [personalBankAccount, applicantID], ); const onfidoError = ErrorUtils.getLatestErrorMessage(walletOnfidoData) ?? ''; @@ -84,8 +88,6 @@ function VerifyIdentity({personalBankAccount, onfidoApplicantID, onfidoToken, wa Wallet.updateCurrentStep(CONST.WALLET.STEP.ADDITIONAL_DETAILS); }; - const {isLoading = false, hasAcceptedPrivacyPolicy} = walletOnfidoData; - return ( {hasAcceptedPrivacyPolicy ? ( {}} + onSubmit={openOnfidoFlow} onFixTheErrorsLinkPressed={() => {}} message={onfidoError} isLoading={isLoading} @@ -145,12 +147,6 @@ export default withOnyx({ personalBankAccount: { key: ONYXKEYS.PERSONAL_BANK_ACCOUNT, }, - onfidoApplicantID: { - key: ONYXKEYS.ONFIDO_APPLICANT_ID, - }, - onfidoToken: { - key: ONYXKEYS.ONFIDO_TOKEN, - }, walletOnfidoData: { key: ONYXKEYS.WALLET_ONFIDO, From 6b33432a88f50a2c7501ebaaade4bafa74dc9a96 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 17 May 2024 19:23:06 +0800 Subject: [PATCH 049/565] don't set pointer events to none for deleted parent action --- src/pages/home/report/ReportActionItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 95bc4f5231d2..2bb5d59f674b 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -933,7 +933,7 @@ function ReportActionItem({ isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onSecondaryInteraction={showPopover} From 1ef6f75d96cb2c4ea2d3b952afb1485ff7486ca0 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 20 May 2024 16:43:17 +0700 Subject: [PATCH 050/565] fix User has to click twice to select currency --- src/components/SelectionList/BaseListItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index 5876e476afa2..329cd6504f17 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -78,7 +78,7 @@ function BaseListItem({ hoverDimmingValue={1} hoverStyle={[!item.isDisabled && styles.hoveredComponentBG, hoverStyle]} dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} - onMouseDown={shouldPreventDefaultFocusOnSelectRow ? (e) => e.preventDefault() : undefined} + onMouseDown={(e) => e.preventDefault()} id={keyForList ?? ''} style={pressableStyle} onFocus={onFocus} From 26fbf601f003672cb06e6b37de012850c33d67d9 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 20 May 2024 17:00:42 +0700 Subject: [PATCH 051/565] fix lint --- src/components/SelectionList/BaseListItem.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index 329cd6504f17..526262870f56 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -17,7 +17,6 @@ function BaseListItem({ wrapperStyle, containerStyle, isDisabled = false, - shouldPreventDefaultFocusOnSelectRow = false, shouldPreventEnterKeySubmit = false, canSelectMultiple = false, onSelectRow, From 4706357c1d51789d9f747f5f0b90ac472e04952b Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 20 May 2024 18:09:32 +0200 Subject: [PATCH 052/565] feat: change wallet agreement url for bancorp --- src/CONST.ts | 1 + .../FeesAndTerms/substeps/TermsStep.tsx | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 6517ece4276d..af5f32808034 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -574,6 +574,7 @@ const CONST = { LICENSES_URL: `${USE_EXPENSIFY_URL}/licenses`, ACH_TERMS_URL: `${USE_EXPENSIFY_URL}/achterms`, WALLET_AGREEMENT_URL: `${USE_EXPENSIFY_URL}/walletagreement`, + BANCORP_WALLET_AGREEMENT_URL: `${USE_EXPENSIFY_URL}/bancorp-bank-wallet-terms-of-service`, HELP_LINK_URL: `${USE_EXPENSIFY_URL}/usa-patriot-act`, ELECTRONIC_DISCLOSURES_URL: `${USE_EXPENSIFY_URL}/esignagreement`, GITHUB_RELEASE_URL: 'https://api.github.com/repos/expensify/app/releases/latest', diff --git a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx index fe17ea7e1afb..270f0c1f1699 100644 --- a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx @@ -9,8 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@navigation/Navigation'; -// TODO: uncomment at the end of the refactor https://github.com/Expensify/App/issues/36648 -// import * as BankAccounts from '@userActions/BankAccounts'; +import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -28,13 +27,15 @@ function HaveReadAndAgreeLabel() { function AgreeToTheLabel() { const {translate} = useLocalize(); + const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); + const walletAgreementUrl = userWallet?.walletProgramID === CONST.WALLET.MTL_WALLET_PROGRAM_ID ? CONST.WALLET_AGREEMENT_URL : CONST.BANCORP_WALLET_AGREEMENT_URL; return ( {`${translate('termsStep.agreeToThe')} `} {`${translate('common.privacy')} `} {`${translate('common.and')} `} - {`${translate('termsStep.walletAgreement')}.`} + {`${translate('termsStep.walletAgreement')}.`} ); } @@ -93,11 +94,10 @@ function TermsStep() { } setError(false); - // TODO: uncomment at the end of the refactor https://github.com/Expensify/App/issues/36648 - // BankAccounts.acceptWalletTerms({ - // hasAcceptedTerms: hasAcceptedDisclosure && hasAcceptedPrivacyPolicyAndWalletAgreement, - // reportID: walletTerms?.chatReportID ?? '', - // }); + BankAccounts.acceptWalletTerms({ + hasAcceptedTerms: hasAcceptedDisclosure && hasAcceptedPrivacyPolicyAndWalletAgreement, + reportID: walletTerms?.chatReportID ?? '', + }); Navigation.navigate(ROUTES.SETTINGS_WALLET); }} message={errorMessage} From 8f019fd2507569145f0fcf179c767d46d249e3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barbara=20Gawe=C5=82-Kucab?= Date: Wed, 22 May 2024 15:11:28 +0200 Subject: [PATCH 053/565] enable no-unsafe-call rule --- .eslintrc.js | 1 - .../javascript/getGraphiteString/getGraphiteString.ts | 3 ++- .../ComposerWithSuggestions/ComposerWithSuggestions.tsx | 6 +++++- tests/unit/markPullRequestsAsDeployedTest.ts | 2 +- workflow_tests/utils/ExtendedAct.ts | 3 ++- workflow_tests/utils/preGenerateTest.ts | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index eb50d1fcc5f3..2cf5ba68aa9a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -100,7 +100,6 @@ module.exports = { __DEV__: 'readonly', }, rules: { - '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', diff --git a/.github/actions/javascript/getGraphiteString/getGraphiteString.ts b/.github/actions/javascript/getGraphiteString/getGraphiteString.ts index c486fdbd39f3..15b5e885c921 100644 --- a/.github/actions/javascript/getGraphiteString/getGraphiteString.ts +++ b/.github/actions/javascript/getGraphiteString/getGraphiteString.ts @@ -32,7 +32,8 @@ const run = () => { } if (current.name && current.meanDuration && current.meanCount && timestamp) { - const formattedName = current.name.split(' ').join('-'); + const currentName = current.name as string; + const formattedName = currentName.split(' ').join('-'); const renderDurationString = `${GRAPHITE_PATH}.${formattedName}.renderDuration ${current.meanDuration} ${timestamp}`; const renderCountString = `${GRAPHITE_PATH}.${formattedName}.renderCount ${current.meanCount} ${timestamp}`; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 0b585de8f059..1a490a26867a 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -182,7 +182,11 @@ type SwitchToCurrentReportProps = { callback: () => void; }; -const {RNTextInputReset} = NativeModules; +type RNTextInputResetProps = { + resetKeyboardInput: (nodeHandle: number | null) => void; +}; + +const RNTextInputReset: RNTextInputResetProps = NativeModules.RNTextInputReset; const isIOSNative = getPlatform() === CONST.PLATFORM.IOS; diff --git a/tests/unit/markPullRequestsAsDeployedTest.ts b/tests/unit/markPullRequestsAsDeployedTest.ts index 24a6733d1244..8d8b25968a28 100644 --- a/tests/unit/markPullRequestsAsDeployedTest.ts +++ b/tests/unit/markPullRequestsAsDeployedTest.ts @@ -35,7 +35,7 @@ type CommitData = { }; }; -let run; +let run: () => Promise; const mockGetInput = jest.fn(); const mockGetPullRequest = jest.fn(); diff --git a/workflow_tests/utils/ExtendedAct.ts b/workflow_tests/utils/ExtendedAct.ts index e2bb12ec8e01..7b35eb260ba3 100644 --- a/workflow_tests/utils/ExtendedAct.ts +++ b/workflow_tests/utils/ExtendedAct.ts @@ -17,7 +17,8 @@ type ActOptions = { // @ts-expect-error Override shouldn't be done on private methods wait until https://github.com/kiegroup/act-js/issues/77 is resolved or try to create a params workaround class ExtendedAct extends Act { async parseRunOpts(opts?: ExtendedActOpts): Promise { - const {cwd, actArguments, proxy} = await super['parseRunOpts'](opts); + const parseSuperRunOpts: (opts?: ExtendedActOpts) => Promise = super['parseRunOpts']; + const {cwd, actArguments, proxy} = await parseSuperRunOpts(opts); if (opts?.actor) { actArguments.push('--actor', opts.actor); diff --git a/workflow_tests/utils/preGenerateTest.ts b/workflow_tests/utils/preGenerateTest.ts index 1e7e7bb04184..eb1d2e8f9b69 100644 --- a/workflow_tests/utils/preGenerateTest.ts +++ b/workflow_tests/utils/preGenerateTest.ts @@ -219,7 +219,8 @@ const getMockFileContent = (workflowName: string, jobs: Record { - const stepMockName = `${workflowName.toUpperCase()}__${jobId.toUpperCase()}__${step.name + const stepName = step.name as string; + const stepMockName = `${workflowName.toUpperCase()}__${jobId.toUpperCase()}__${stepName .replaceAll(' ', '_') .replaceAll('-', '_') .replaceAll(',', '') From c6028f762608b9dc68b863b24e7e32006b50c1e2 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 22 May 2024 20:39:27 +0200 Subject: [PATCH 054/565] refactor: cleanup --- src/pages/EnablePayments/EnablePayments.tsx | 20 ++------- .../FeesAndTerms/FeesAndTerms.tsx | 16 ++++++- .../FeesAndTerms/substeps/TermsStep.tsx | 6 +-- .../VerifyIdentity/VerifyIdentity.tsx | 42 ++++++------------- src/types/form/PersonalBankAccountForm.ts | 6 ++- 5 files changed, 36 insertions(+), 54 deletions(-) diff --git a/src/pages/EnablePayments/EnablePayments.tsx b/src/pages/EnablePayments/EnablePayments.tsx index 6df56afced57..fdee18e8147d 100644 --- a/src/pages/EnablePayments/EnablePayments.tsx +++ b/src/pages/EnablePayments/EnablePayments.tsx @@ -7,17 +7,16 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import Navigation from '@libs/Navigation/Navigation'; -import FeesAndTerms from '@pages/EnablePayments/FeesAndTerms/FeesAndTerms'; -import PersonalInfo from '@pages/EnablePayments/PersonalInfo/PersonalInfo'; -import VerifyIdentity from '@pages/EnablePayments/VerifyIdentity/VerifyIdentity'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {UserWallet} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import ActivateStep from './ActivateStep'; import FailedKYC from './FailedKYC'; +import FeesAndTerms from './FeesAndTerms/FeesAndTerms'; +import PersonalInfo from './PersonalInfo/PersonalInfo'; +import VerifyIdentity from './VerifyIdentity/VerifyIdentity'; type EnablePaymentsPageOnyxProps = { /** The user's wallet */ @@ -30,21 +29,13 @@ function EnablePaymentsPage({userWallet}: EnablePaymentsPageProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {isPendingOnfidoResult, hasFailedOnfido} = userWallet ?? {}; - useEffect(() => { if (isOffline) { return; } - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (isPendingOnfidoResult || hasFailedOnfido) { - Navigation.navigate(ROUTES.SETTINGS_WALLET, CONST.NAVIGATION.TYPE.UP); - return; - } - Wallet.openEnablePaymentsPage(); - }, [isOffline, isPendingOnfidoResult, hasFailedOnfido]); + }, [isOffline]); if (isEmptyObject(userWallet)) { return ; @@ -73,14 +64,11 @@ function EnablePaymentsPage({userWallet}: EnablePaymentsPageProps) { switch (currentStep) { case CONST.WALLET.STEP.ADDITIONAL_DETAILS: - case CONST.WALLET.STEP.ADDITIONAL_DETAILS_KBA: return ; case CONST.WALLET.STEP.ONFIDO: return ; case CONST.WALLET.STEP.TERMS: return ; - case CONST.WALLET.STEP.ACTIVATE: - return ; default: return null; } diff --git a/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx index b54564eff254..64e99bd9be05 100644 --- a/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -8,7 +9,10 @@ import useSubStep from '@hooks/useSubStep'; import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import * as BankAccounts from '@userActions/BankAccounts'; +import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import FeesStep from './substeps/FeesStep'; import TermsStep from './substeps/TermsStep'; @@ -18,13 +22,21 @@ const termsAndFeesSubsteps: Array> = [FeesStep function FeesAndTerms() { const {translate} = useLocalize(); const styles = useThemeStyles(); + const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); - const submit = () => {}; + const submit = () => { + BankAccounts.acceptWalletTerms({ + hasAcceptedTerms: true, + reportID: walletTerms?.chatReportID ?? '', + }); + BankAccounts.clearPersonalBankAccount(); + // TODO: clear wallet draft + Navigation.navigate(ROUTES.SETTINGS_WALLET); + }; const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo} = useSubStep({bodyContent: termsAndFeesSubsteps, startFrom: 0, onFinished: submit}); const handleBackButtonPress = () => { if (screenIndex === 0) { - // TODO: temporary for refactor https://github.com/Expensify/App/issues/36648 Navigation.navigate(ROUTES.SETTINGS_WALLET); return; } diff --git a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx index 270f0c1f1699..afbd6d594274 100644 --- a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx @@ -28,6 +28,7 @@ function HaveReadAndAgreeLabel() { function AgreeToTheLabel() { const {translate} = useLocalize(); const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); + const walletAgreementUrl = userWallet?.walletProgramID === CONST.WALLET.MTL_WALLET_PROGRAM_ID ? CONST.WALLET_AGREEMENT_URL : CONST.BANCORP_WALLET_AGREEMENT_URL; return ( @@ -94,11 +95,6 @@ function TermsStep() { } setError(false); - BankAccounts.acceptWalletTerms({ - hasAcceptedTerms: hasAcceptedDisclosure && hasAcceptedPrivacyPolicyAndWalletAgreement, - reportID: walletTerms?.chatReportID ?? '', - }); - Navigation.navigate(ROUTES.SETTINGS_WALLET); }} message={errorMessage} isAlertVisible={error || Boolean(errorMessage)} diff --git a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx index c2023077af50..0c27fb5980af 100644 --- a/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx +++ b/src/pages/EnablePayments/VerifyIdentity/VerifyIdentity.tsx @@ -22,23 +22,16 @@ import * as BankAccounts from '@userActions/BankAccounts'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalBankAccount, WalletOnfido} from '@src/types/onyx'; +import type {WalletOnfido} from '@src/types/onyx'; const DEFAULT_WALLET_ONFIDO_DATA = { - loading: false, + isLoading: false, hasAcceptedPrivacyPolicy: false, + sdkToken: '', + applicantID: '', }; type VerifyIdentityOnyxProps = { - /** Reimbursement account from ONYX */ - personalBankAccount: OnyxEntry; - - /** Onfido applicant ID from ONYX */ - onfidoApplicantID: OnyxEntry; - - /** The token required to initialize the Onfido SDK */ - onfidoToken: OnyxEntry; - /** The wallet onfido data */ walletOnfidoData: OnyxEntry; }; @@ -47,7 +40,7 @@ type VerifyIdentityProps = VerifyIdentityOnyxProps; const ONFIDO_ERROR_DISPLAY_DURATION = 10000; -function VerifyIdentity({personalBankAccount, walletOnfidoData = DEFAULT_WALLET_ONFIDO_DATA}: VerifyIdentityProps) { +function VerifyIdentity({walletOnfidoData = DEFAULT_WALLET_ONFIDO_DATA}: VerifyIdentityProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -55,8 +48,6 @@ function VerifyIdentity({personalBankAccount, walletOnfidoData = DEFAULT_WALLET_ BankAccounts.openOnfidoFlow(); }; - const {isLoading = false, hasAcceptedPrivacyPolicy, sdkToken, applicantID} = walletOnfidoData; - const handleOnfidoSuccess = useCallback( (onfidoData: OnfidoData) => { BankAccounts.verifyIdentity({ @@ -67,24 +58,16 @@ function VerifyIdentity({personalBankAccount, walletOnfidoData = DEFAULT_WALLET_ }); BankAccounts.updateAddPersonalBankAccountDraft({isOnfidoSetupComplete: true}); }, - [personalBankAccount, applicantID], + [walletOnfidoData?.applicantID], ); const onfidoError = ErrorUtils.getLatestErrorMessage(walletOnfidoData) ?? ''; const handleOnfidoError = () => { - // In case of any unexpected error we log it to the server, show a growl, and return the user back to the requestor step so they can try again. Growl.error(translate('onfidoStep.genericError'), ONFIDO_ERROR_DISPLAY_DURATION); - BankAccounts.clearOnfidoToken(); - // BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); - }; - - const handleOnfidoUserExit = () => { - BankAccounts.clearOnfidoToken(); - // BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); }; - const handleBackButtonPress = () => { + const goBack = () => { Wallet.updateCurrentStep(CONST.WALLET.STEP.ADDITIONAL_DETAILS); }; @@ -92,7 +75,7 @@ function VerifyIdentity({personalBankAccount, walletOnfidoData = DEFAULT_WALLET_ - {hasAcceptedPrivacyPolicy ? ( + {walletOnfidoData?.hasAcceptedPrivacyPolicy ? ( @@ -125,9 +108,8 @@ function VerifyIdentity({personalBankAccount, walletOnfidoData = DEFAULT_WALLET_ {}} message={onfidoError} - isLoading={isLoading} + isLoading={walletOnfidoData?.isLoading} buttonText={onfidoError ? translate('onfidoStep.tryAgain') : translate('common.continue')} containerStyles={[styles.mh0, styles.mv0, styles.mb0]} /> diff --git a/src/types/form/PersonalBankAccountForm.ts b/src/types/form/PersonalBankAccountForm.ts index 1a1922bf8013..a5b95b20e684 100644 --- a/src/types/form/PersonalBankAccountForm.ts +++ b/src/types/form/PersonalBankAccountForm.ts @@ -32,7 +32,11 @@ type PlaidAccountProps = { [INPUT_IDS.BANK_INFO_STEP.SELECTED_PLAID_ACCOUNT_ID]: string; }; -type PersonalBankAccountForm = Form; +type OnfidoStepProps = { + isOnfidoSetupComplete: boolean; +}; + +type PersonalBankAccountForm = Form & OnfidoStepProps; export type {BankAccountStepProps, PlaidAccountProps, PersonalBankAccountForm}; From ec1846c2568cd745facfbbb103c9f402443c83c1 Mon Sep 17 00:00:00 2001 From: dragnoir Date: Thu, 23 May 2024 12:35:47 +0100 Subject: [PATCH 055/565] Fix; add spacing between comment and Uploading attachment... --- .../HTMLEngineProvider/BaseHTMLEngineProvider.tsx | 7 ++++++- src/libs/ReportUtils.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 51f9981f1524..406e5e1cbc8b 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -77,8 +77,13 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim mixedUAStyles: {...styles.textSupporting, ...styles.textLineThrough}, contentModel: HTMLContentModel.textual, }), + 'uploading-attachment': HTMLElementModel.fromCustomModel({ + tagName: 'uploading-attachment', + mixedUAStyles: {...styles.mt3}, + contentModel: HTMLContentModel.block, + }), }), - [styles.formError, styles.mb0, styles.colorMuted, styles.textLabelSupporting, styles.lh16, styles.textSupporting, styles.textLineThrough], + [styles.formError, styles.mb0, styles.colorMuted, styles.textLabelSupporting, styles.lh16, styles.textSupporting, styles.textLineThrough, styles.mt3], ); /* eslint-enable @typescript-eslint/naming-convention */ diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b8e4c448fdc2..ecd0a94230c0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3450,7 +3450,7 @@ function buildOptimisticAddCommentReportAction( htmlForNewComment = commentText; textForNewComment = parser.htmlToText(htmlForNewComment); } else { - htmlForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; + htmlForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; textForNewComment = `${parser.htmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; } From 9764c602fbf58b74790457f1b4836bf6c4c32da4 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 May 2024 16:56:27 +0200 Subject: [PATCH 056/565] chore: clean wallet draft after finishing the flow --- src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx index 64e99bd9be05..5b462c278509 100644 --- a/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx @@ -30,7 +30,7 @@ function FeesAndTerms() { reportID: walletTerms?.chatReportID ?? '', }); BankAccounts.clearPersonalBankAccount(); - // TODO: clear wallet draft + Wallet.resetWalletAdditionalDetailsDraft(); Navigation.navigate(ROUTES.SETTINGS_WALLET); }; const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo} = useSubStep({bodyContent: termsAndFeesSubsteps, startFrom: 0, onFinished: submit}); From aa7d744a1736bf3cde203caacc2dd2eabb7ba307 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 May 2024 18:50:24 +0200 Subject: [PATCH 057/565] fix: remove old add plaid bank account layout --- src/components/AddPlaidBankAccount.tsx | 68 +++++-------------- src/pages/AddPersonalBankAccountPage.tsx | 1 + .../BankInfo/substeps/Plaid.tsx | 1 - 3 files changed, 18 insertions(+), 52 deletions(-) diff --git a/src/components/AddPlaidBankAccount.tsx b/src/components/AddPlaidBankAccount.tsx index 366f14ec9780..fddfe5b8e8d9 100644 --- a/src/components/AddPlaidBankAccount.tsx +++ b/src/components/AddPlaidBankAccount.tsx @@ -59,9 +59,6 @@ type AddPlaidBankAccountProps = AddPlaidBankAccountOnyxProps & { /** Are we adding a withdrawal account? */ allowDebit?: boolean; - /** Is displayed in new VBBA */ - isDisplayedInNewVBBA?: boolean; - /** Is displayed in new enable wallet flow */ isDisplayedInWalletFlow?: boolean; @@ -84,7 +81,6 @@ function AddPlaidBankAccount({ bankAccountID = 0, allowDebit = false, isPlaidDisabled, - isDisplayedInNewVBBA = false, errorText = '', onInputChange = () => {}, isDisplayedInWalletFlow = false, @@ -259,62 +255,32 @@ function AddPlaidBankAccount({ return {renderPlaidLink()}; } - if (isDisplayedInNewVBBA || isDisplayedInWalletFlow) { - return ( - - {translate(isDisplayedInWalletFlow ? 'walletPage.chooseYourBankAccount' : 'bankAccount.chooseAnAccount')} - {!!text && {text}} - - - - {bankName} - {selectedPlaidAccountMask.length > 0 && ( - {`${translate('bankAccount.accountEnding')} ${selectedPlaidAccountMask}`} - )} - - - {`${translate('bankAccount.chooseAnAccountBelow')}:`} - - - - ); - } - - // Plaid bank accounts view return ( - {!!text && {text}} - + {translate(isDisplayedInWalletFlow ? 'walletPage.chooseYourBankAccount' : 'bankAccount.chooseAnAccount')} + {!!text && {text}} + - {bankName} - - - + + {bankName} + {selectedPlaidAccountMask.length > 0 && ( + {`${translate('bankAccount.accountEnding')} ${selectedPlaidAccountMask}`} + )} + + {`${translate('bankAccount.chooseAnAccountBelow')}:`} + + ); } diff --git a/src/pages/AddPersonalBankAccountPage.tsx b/src/pages/AddPersonalBankAccountPage.tsx index 5cd0f3ef8026..98506477eaf9 100644 --- a/src/pages/AddPersonalBankAccountPage.tsx +++ b/src/pages/AddPersonalBankAccountPage.tsx @@ -90,6 +90,7 @@ function AddPersonalBankAccountPage({personalBankAccount, plaidData}: AddPersona Navigation.goBack()} receivedRedirectURI={getPlaidOAuthReceivedRedirectURI()} selectedPlaidAccountID={selectedPlaidAccountId} diff --git a/src/pages/ReimbursementAccount/BankInfo/substeps/Plaid.tsx b/src/pages/ReimbursementAccount/BankInfo/substeps/Plaid.tsx index c58c2f40aa07..03409e4adddf 100644 --- a/src/pages/ReimbursementAccount/BankInfo/substeps/Plaid.tsx +++ b/src/pages/ReimbursementAccount/BankInfo/substeps/Plaid.tsx @@ -88,7 +88,6 @@ function Plaid({reimbursementAccount, reimbursementAccountDraft, onNext, plaidDa allowDebit bankAccountID={bankAccountID} selectedPlaidAccountID={selectedPlaidAccountID} - isDisplayedInNewVBBA inputID={BANK_INFO_STEP_KEYS.SELECTED_PLAID_ACCOUNT_ID} defaultValue={selectedPlaidAccountID} /> From 3a2f068f2f0d6ead4a085f695e083265f03c36a9 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Fri, 24 May 2024 01:47:53 +0700 Subject: [PATCH 058/565] ordered mention suggestions --- .../ReportActionCompose/SuggestionMention.tsx | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 05e1163da200..cef1c0990c59 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -1,7 +1,8 @@ import Str from 'expensify-common/lib/str'; +import lodashMapValues from 'lodash/mapValues'; import lodashSortBy from 'lodash/sortBy'; import type {ForwardedRef} from 'react'; -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {useOnyx} from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -9,11 +10,13 @@ import type {Mention} from '@components/MentionSuggestions'; import MentionSuggestions from '@components/MentionSuggestions'; import {usePersonalDetails} from '@components/OnyxProvider'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; +import useCurrentReportID from '@hooks/useCurrentReportID'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebounce from '@hooks/useDebounce'; import useLocalize from '@hooks/useLocalize'; import * as LoginUtils from '@libs/LoginUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; +import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import * as UserUtils from '@libs/UserUtils'; @@ -21,7 +24,7 @@ import {isValidRoomName} from '@libs/ValidationUtils'; import * as ReportUserActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetailsList, Report} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, Report} from '@src/types/onyx'; import type {SuggestionsRef} from './ReportActionCompose'; import type {SuggestionProps} from './Suggestions'; @@ -46,6 +49,14 @@ const defaultSuggestionsValues: SuggestionValues = { prefixType: '', }; +type SuggestionPersonalDetailsList = Record< + string, + | (PersonalDetails & { + weight: number; + }) + | null +>; + function SuggestionMention( {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused, isGroupPolicyReport, policyID}: SuggestionProps, ref: ForwardedRef, @@ -59,6 +70,36 @@ function SuggestionMention( const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const isMentionSuggestionsMenuVisible = !!suggestionValues.suggestedMentions.length && suggestionValues.shouldShowSuggestionMenu; + const currentReportID = useCurrentReportID(); + const currentReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${currentReportID?.currentReportID}`] ?? null; + // Smaller weight means higher order in suggestion list + const getPersonalDetailsWeight = useCallback( + (detail: PersonalDetails, policyEmployeeAccountIDs: number[]): number => { + if (ReportUtils.isReportParticipant(detail.accountID, currentReport)) { + return 0; + } + if (policyEmployeeAccountIDs.includes(detail.accountID)) { + return 1; + } + return 2; + }, + [currentReport], + ); + const weightedPersonalDetails: PersonalDetailsList | SuggestionPersonalDetailsList = useMemo(() => { + const policyEmployeeAccountIDs = getPolicyEmployeeAccountIDs(policyID); + if (!ReportUtils.isGroupChat(currentReport) && !ReportUtils.doesReportBelongToWorkspace(currentReport, policyEmployeeAccountIDs, policyID)) { + return personalDetails; + } + return lodashMapValues(personalDetails, (detail) => + detail + ? { + ...detail, + weight: getPersonalDetailsWeight(detail, policyEmployeeAccountIDs), + } + : null, + ); + }, [policyID, currentReport, personalDetails, getPersonalDetailsWeight]); + const [highlightedMentionIndex, setHighlightedMentionIndex] = useArrowKeyFocusManager({ isActive: isMentionSuggestionsMenuVisible, maxIndex: suggestionValues.suggestedMentions.length - 1, @@ -191,7 +232,7 @@ function SuggestionMention( ); const getUserMentionOptions = useCallback( - (personalDetailsParam: PersonalDetailsList, searchValue = ''): Mention[] => { + (personalDetailsParam: PersonalDetailsList | SuggestionPersonalDetailsList, searchValue = ''): Mention[] => { const suggestions = []; if (CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT.includes(searchValue.toLowerCase())) { @@ -232,8 +273,7 @@ function SuggestionMention( return true; }); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing cannot be used if left side can be empty string - const sortedPersonalDetails = lodashSortBy(filteredPersonalDetails, (detail) => detail?.displayName || detail?.login); + const sortedPersonalDetails = lodashSortBy(filteredPersonalDetails, ['weight', 'displayName', 'login']); sortedPersonalDetails.slice(0, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length).forEach((detail) => { suggestions.push({ text: formatLoginPrivateDomain(PersonalDetailsUtils.getDisplayNameOrDefault(detail), detail?.login), @@ -320,7 +360,7 @@ function SuggestionMention( }; if (isMentionCode(suggestionWord) && prefixType === '@') { - const suggestions = getUserMentionOptions(personalDetails, prefix); + const suggestions = getUserMentionOptions(weightedPersonalDetails, prefix); nextState.suggestedMentions = suggestions; nextState.shouldShowSuggestionMenu = !!suggestions.length; } @@ -340,7 +380,7 @@ function SuggestionMention( })); setHighlightedMentionIndex(0); }, - [isComposerFocused, value, isGroupPolicyReport, setHighlightedMentionIndex, resetSuggestions, getUserMentionOptions, personalDetails, getRoomMentionOptions, reports], + [isComposerFocused, value, isGroupPolicyReport, setHighlightedMentionIndex, resetSuggestions, getUserMentionOptions, weightedPersonalDetails, getRoomMentionOptions, reports], ); useEffect(() => { From f189a8cc3cbf50a5c7758d004a46011454cb5853 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 May 2024 21:23:02 +0200 Subject: [PATCH 059/565] fix: include add bank acount to the flow --- src/CONST.ts | 1 + src/pages/AddPersonalBankAccountPage.tsx | 1 + src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx | 4 ++-- src/pages/EnablePayments/EnablePayments.tsx | 5 ++++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index af5f32808034..6ef98d656e14 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1423,6 +1423,7 @@ const CONST = { }, STEP: { // In the order they appear in the Wallet flow + ADD_BANK_ACCOUNT: 'AddBankAccountStep', ADDITIONAL_DETAILS: 'AdditionalDetailsStep', ADDITIONAL_DETAILS_KBA: 'AdditionalDetailsKBAStep', ONFIDO: 'OnfidoStep', diff --git a/src/pages/AddPersonalBankAccountPage.tsx b/src/pages/AddPersonalBankAccountPage.tsx index 98506477eaf9..59046e103c6b 100644 --- a/src/pages/AddPersonalBankAccountPage.tsx +++ b/src/pages/AddPersonalBankAccountPage.tsx @@ -89,6 +89,7 @@ function AddPersonalBankAccountPage({personalBankAccount, plaidData}: AddPersona > Navigation.goBack()} diff --git a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx index 9fde5456226b..149d6a41aac7 100644 --- a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx +++ b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx @@ -12,9 +12,9 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as BankAccounts from '@userActions/BankAccounts'; import * as PaymentMethods from '@userActions/PaymentMethods'; +import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type {PersonalBankAccountForm} from '@src/types/form'; import type {PersonalBankAccount, PlaidData} from '@src/types/onyx'; import SetupMethod from './SetupMethod'; @@ -44,7 +44,7 @@ function AddBankAccount({personalBankAccount, plaidData, personalBankAccountDraf if (selectedPlaidBankAccount) { BankAccounts.addPersonalBankAccount(selectedPlaidBankAccount); - Navigation.navigate(ROUTES.SETTINGS_ENABLE_PAYMENTS_REFACTOR); + Wallet.updateCurrentStep(CONST.WALLET.STEP.ADDITIONAL_DETAILS); } }, [personalBankAccountDraft?.plaidAccountID, plaidData?.bankAccounts]); diff --git a/src/pages/EnablePayments/EnablePayments.tsx b/src/pages/EnablePayments/EnablePayments.tsx index fdee18e8147d..752705cd6d27 100644 --- a/src/pages/EnablePayments/EnablePayments.tsx +++ b/src/pages/EnablePayments/EnablePayments.tsx @@ -7,6 +7,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import Navigation from '@libs/Navigation/Navigation'; +import AddBankAccount from '@pages/EnablePayments/AddBankAccount/AddBankAccount'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -60,9 +61,11 @@ function EnablePaymentsPage({userWallet}: EnablePaymentsPageProps) { ); } - const currentStep = userWallet?.currentStep || CONST.WALLET.STEP.ADDITIONAL_DETAILS; + const currentStep = userWallet?.currentStep || (userWallet?.bankAccountID ? CONST.WALLET.STEP.ADDITIONAL_DETAILS : CONST.WALLET.STEP.ADD_BANK_ACCOUNT); switch (currentStep) { + case CONST.WALLET.STEP.ADD_BANK_ACCOUNT: + return ; case CONST.WALLET.STEP.ADDITIONAL_DETAILS: return ; case CONST.WALLET.STEP.ONFIDO: From 034b82df570a5822c65be0d8a1f475ef4104cf8a Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 24 May 2024 11:55:44 +0200 Subject: [PATCH 060/565] fix: show loading when confirming personal info, fetch userWallet only when missing data --- src/pages/EnablePayments/EnablePayments.tsx | 10 ++++------ .../PersonalInfo/substeps/ConfirmationStep.tsx | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/pages/EnablePayments/EnablePayments.tsx b/src/pages/EnablePayments/EnablePayments.tsx index 752705cd6d27..5d7dd52c1e89 100644 --- a/src/pages/EnablePayments/EnablePayments.tsx +++ b/src/pages/EnablePayments/EnablePayments.tsx @@ -35,8 +35,10 @@ function EnablePaymentsPage({userWallet}: EnablePaymentsPageProps) { return; } - Wallet.openEnablePaymentsPage(); - }, [isOffline]); + if (isEmptyObject(userWallet)) { + Wallet.openEnablePaymentsPage(); + } + }, [isOffline, userWallet]); if (isEmptyObject(userWallet)) { return ; @@ -85,9 +87,5 @@ EnablePaymentsPage.displayName = 'EnablePaymentsPage'; export default withOnyx({ userWallet: { key: ONYXKEYS.USER_WALLET, - - // We want to refresh the wallet each time the user attempts to activate the wallet so we won't use the - // stored values here. - initWithStoredValues: false, }, })(EnablePaymentsPage); diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx index b24400face07..ea1b70cd6074 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/ConfirmationStep.tsx @@ -29,7 +29,7 @@ function ConfirmationStep({onNext, onMove}: SubStepProps) { const [walletAdditionalDetails] = useOnyx(ONYXKEYS.WALLET_ADDITIONAL_DETAILS); const [walletAdditionalDetailsDraft] = useOnyx(ONYXKEYS.FORMS.WALLET_ADDITIONAL_DETAILS_DRAFT); - const isLoading = walletAdditionalDetailsDraft?.isLoading ?? false; + const isLoading = walletAdditionalDetails?.isLoading ?? false; const error = ErrorUtils.getLatestErrorMessage(walletAdditionalDetails ?? {}); const values = useMemo(() => getSubstepValues(PERSONAL_INFO_STEP_KEYS, walletAdditionalDetailsDraft, walletAdditionalDetails), [walletAdditionalDetails, walletAdditionalDetailsDraft]); From 549e647063a14d8435aaf5dd2df13e930e9b929d Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Fri, 24 May 2024 12:32:38 +0200 Subject: [PATCH 061/565] Show all policies in the workspace list --- src/libs/PolicyUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 6a26b1a6cfc2..37dafafb3e5e 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -116,7 +116,7 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf, isOffline: boolean): boolean { return ( !!policy && - (policy?.isPolicyExpenseChatEnabled || Boolean(policy?.isJoinRequestPending)) && + Boolean(policy?.isJoinRequestPending) && (isOffline || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) ); } From ee6a0129eb5c6ff06d8e547a81787711b84e5214 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Fri, 24 May 2024 12:32:52 +0200 Subject: [PATCH 062/565] Do not show personal policies in the list --- src/libs/PolicyUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 37dafafb3e5e..a1dc5534df8c 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -116,6 +116,7 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf, isOffline: boolean): boolean { return ( !!policy && + policy?.type !== CONST.POLICY.TYPE.PERSONAL && Boolean(policy?.isJoinRequestPending) && (isOffline || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) ); From 8b2b02e411703c5e43633baea5994935787a4d39 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Fri, 24 May 2024 12:50:10 +0200 Subject: [PATCH 063/565] Remove the isPolicyExpenseChatEnabled check from the AccessOrNotFoundPage --- src/libs/PolicyUtils.ts | 4 ++-- src/pages/workspace/AccessOrNotFoundWrapper.tsx | 2 +- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 021dbb5abcff..b5092f8244eb 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -116,8 +116,8 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry): ValueOf, isOffline: boolean): boolean { return ( !!policy && - policy?.type !== CONST.POLICY.TYPE.PERSONAL && - Boolean(policy?.isJoinRequestPending) && + (policy?.type !== CONST.POLICY.TYPE.PERSONAL || + Boolean(policy?.isJoinRequestPending)) && (isOffline || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) ); } diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx index b90a9cb38151..da33238793df 100644 --- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx @@ -18,7 +18,7 @@ import callOrReturn from '@src/types/utils/callOrReturn'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; const POLICY_ACCESS_VARIANTS = { - [CONST.POLICY.ACCESS_VARIANTS.PAID]: (policy: OnyxEntry) => PolicyUtils.isPaidGroupPolicy(policy) && !!policy?.isPolicyExpenseChatEnabled, + [CONST.POLICY.ACCESS_VARIANTS.PAID]: (policy: OnyxEntry) => PolicyUtils.isPaidGroupPolicy(policy), [CONST.POLICY.ACCESS_VARIANTS.ADMIN]: (policy: OnyxEntry) => PolicyUtils.isPolicyAdmin(policy), } as const satisfies Record boolean>; diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 60e471deb328..4028f2f18341 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -309,7 +309,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: if (isEmptyObject(policies)) { return []; } - + console.log("policies: ", policies); return Object.values(policies) .filter((policy): policy is PolicyType => PolicyUtils.shouldShowPolicy(policy, !!isOffline)) .map((policy): WorkspaceItem => { From c5291d7abd9521f9ea0b6d8afb7853b8f049c1fd Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 24 May 2024 13:25:22 +0200 Subject: [PATCH 064/565] fix: proper submit/go back handling for the terms step --- .../FeesAndTerms/FeesAndTerms.tsx | 2 +- .../FeesAndTerms/substeps/TermsStep.tsx | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx index 5b462c278509..512abc57a990 100644 --- a/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/FeesAndTerms.tsx @@ -37,7 +37,7 @@ function FeesAndTerms() { const handleBackButtonPress = () => { if (screenIndex === 0) { - Navigation.navigate(ROUTES.SETTINGS_WALLET); + Wallet.updateCurrentStep(CONST.WALLET.STEP.ONFIDO); return; } prevScreen(); diff --git a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx index afbd6d594274..12fce0b2ec50 100644 --- a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx @@ -6,13 +6,11 @@ import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; +import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; -import Navigation from '@navigation/Navigation'; -import * as BankAccounts from '@userActions/BankAccounts'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; function HaveReadAndAgreeLabel() { const {translate} = useLocalize(); @@ -41,7 +39,7 @@ function AgreeToTheLabel() { ); } -function TermsStep() { +function TermsStep({onNext}: SubStepProps) { const styles = useThemeStyles(); const [hasAcceptedDisclosure, setHasAcceptedDisclosure] = useState(false); const [hasAcceptedPrivacyPolicyAndWalletAgreement, setHasAcceptedPrivacyPolicyAndWalletAgreement] = useState(false); @@ -60,6 +58,15 @@ function TermsStep() { setHasAcceptedPrivacyPolicyAndWalletAgreement(!hasAcceptedPrivacyPolicyAndWalletAgreement); }; + const submit = () => { + if (!hasAcceptedDisclosure || !hasAcceptedPrivacyPolicyAndWalletAgreement) { + setError(true); + return; + } + setError(false); + onNext(); + }; + /** clear error */ useEffect(() => { if (!hasAcceptedDisclosure || !hasAcceptedPrivacyPolicyAndWalletAgreement) { @@ -88,14 +95,7 @@ function TermsStep() { { - if (!hasAcceptedDisclosure || !hasAcceptedPrivacyPolicyAndWalletAgreement) { - setError(true); - return; - } - - setError(false); - }} + onSubmit={submit} message={errorMessage} isAlertVisible={error || Boolean(errorMessage)} isLoading={!!walletTerms?.isLoading} From 3538c6951b2b98a88ed5430d60f657061b1d9220 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 24 May 2024 14:43:47 +0200 Subject: [PATCH 065/565] fix: properly show the first step --- src/pages/EnablePayments/EnablePayments.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pages/EnablePayments/EnablePayments.tsx b/src/pages/EnablePayments/EnablePayments.tsx index 5d7dd52c1e89..ff0aa7b353bb 100644 --- a/src/pages/EnablePayments/EnablePayments.tsx +++ b/src/pages/EnablePayments/EnablePayments.tsx @@ -7,13 +7,13 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import Navigation from '@libs/Navigation/Navigation'; -import AddBankAccount from '@pages/EnablePayments/AddBankAccount/AddBankAccount'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {UserWallet} from '@src/types/onyx'; +import type {BankAccountList, UserWallet} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import AddBankAccount from './AddBankAccount/AddBankAccount'; import FailedKYC from './FailedKYC'; import FeesAndTerms from './FeesAndTerms/FeesAndTerms'; import PersonalInfo from './PersonalInfo/PersonalInfo'; @@ -22,11 +22,14 @@ import VerifyIdentity from './VerifyIdentity/VerifyIdentity'; type EnablePaymentsPageOnyxProps = { /** The user's wallet */ userWallet: OnyxEntry; + + /** The list of bank accounts */ + bankAccountList: OnyxEntry; }; type EnablePaymentsPageProps = EnablePaymentsPageOnyxProps; -function EnablePaymentsPage({userWallet}: EnablePaymentsPageProps) { +function EnablePaymentsPage({userWallet, bankAccountList}: EnablePaymentsPageProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -63,7 +66,7 @@ function EnablePaymentsPage({userWallet}: EnablePaymentsPageProps) { ); } - const currentStep = userWallet?.currentStep || (userWallet?.bankAccountID ? CONST.WALLET.STEP.ADDITIONAL_DETAILS : CONST.WALLET.STEP.ADD_BANK_ACCOUNT); + const currentStep = userWallet?.currentStep || (isEmptyObject(bankAccountList) ? CONST.WALLET.STEP.ADD_BANK_ACCOUNT : CONST.WALLET.STEP.ADDITIONAL_DETAILS); switch (currentStep) { case CONST.WALLET.STEP.ADD_BANK_ACCOUNT: @@ -88,4 +91,7 @@ export default withOnyx({ userWallet: { key: ONYXKEYS.USER_WALLET, }, + bankAccountList: { + key: ONYXKEYS.BANK_ACCOUNT_LIST, + }, })(EnablePaymentsPage); From c1bea82678c9a16796fdef26b914c48a2795691f Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 24 May 2024 15:02:55 +0200 Subject: [PATCH 066/565] fix: remove unnecessary comment --- src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx index 149d6a41aac7..574034159e06 100644 --- a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx +++ b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx @@ -54,7 +54,6 @@ function AddBankAccount({personalBankAccount, plaidData, personalBankAccountDraf const exitFlow = (shouldContinue = false) => { const exitReportID = personalBankAccount?.exitReportID; - // TODO: https://github.com/Expensify/App/issues/36648 This should be updated to the correct route once the refactor is complete const onSuccessFallbackRoute = personalBankAccount?.onSuccessFallbackRoute ?? ''; if (exitReportID) { From f3287b8e9acfc57977821d6ccd918fb3a46a5d9f Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 24 May 2024 15:27:06 +0200 Subject: [PATCH 067/565] feat: routes cleanup --- src/ROUTES.ts | 5 ----- src/SCREENS.ts | 3 --- .../AppNavigator/ModalStackNavigators/index.tsx | 5 +---- src/libs/Navigation/linkingConfig/config.ts | 14 -------------- .../EnablePayments/AddBankAccount/SetupMethod.tsx | 4 ++-- src/pages/EnablePayments/EnablePayments.tsx | 2 +- .../settings/Wallet/WalletPage/WalletPage.tsx | 2 +- 7 files changed, 5 insertions(+), 30 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 2bc04c4a99ea..20520a2eb9ce 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -123,12 +123,7 @@ const ROUTES = { }, SETTINGS_ADD_DEBIT_CARD: 'settings/wallet/add-debit-card', SETTINGS_ADD_BANK_ACCOUNT: 'settings/wallet/add-bank-account', - SETTINGS_ADD_BANK_ACCOUNT_REFACTOR: 'settings/wallet/add-bank-account-refactor', SETTINGS_ENABLE_PAYMENTS: 'settings/wallet/enable-payments', - // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - SETTINGS_ENABLE_PAYMENTS_REFACTOR: 'settings/wallet/enable-payments-refactor', - // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - SETTINGS_ENABLE_PAYMENTS_TEMPORARY_TERMS: 'settings/wallet/enable-payments-temporary-terms', SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS: { route: 'settings/wallet/card/:domain/digital-details/update-address', getRoute: (domain: string) => `settings/wallet/card/${domain}/digital-details/update-address` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f74002312623..fc57724614b5 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -42,7 +42,6 @@ const SCREENS = { APP_DOWNLOAD_LINKS: 'Settings_App_Download_Links', ADD_DEBIT_CARD: 'Settings_Add_Debit_Card', ADD_BANK_ACCOUNT: 'Settings_Add_Bank_Account', - ADD_BANK_ACCOUNT_REFACTOR: 'Settings_Add_Bank_Account_Refactor', CLOSE: 'Settings_Close', TWO_FACTOR_AUTH: 'Settings_TwoFactorAuth', REPORT_CARD_LOST_OR_DAMAGED: 'Settings_ReportCardLostOrDamaged', @@ -91,8 +90,6 @@ const SCREENS = { ENABLE_PAYMENTS: 'Settings_Wallet_EnablePayments', // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 ENABLE_PAYMENTS_REFACTOR: 'Settings_Wallet_EnablePayments_Refactor', - // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - ENABLE_PAYMENTS_TEMPORARY_TERMS: 'Settings_Wallet_EnablePayments_Temporary_Terms', CARD_ACTIVATE: 'Settings_Wallet_Card_Activate', REPORT_VIRTUAL_CARD_FRAUD: 'Settings_Wallet_ReportVirtualCardFraud', CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS: 'Settings_Wallet_Cards_Digital_Details_Update_Address', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index ca46a61787b9..f19a9f5be1ad 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -203,12 +203,9 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Wallet/Card/GetPhysicalCardConfirm').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.TRANSFER_BALANCE]: () => require('../../../../pages/settings/Wallet/TransferBalancePage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.CHOOSE_TRANSFER_ACCOUNT]: () => require('../../../../pages/settings/Wallet/ChooseTransferAccountPage').default as React.ComponentType, - [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS]: () => require('../../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, - // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_REFACTOR]: () => require('../../../../pages/EnablePayments/EnablePayments').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS]: () => require('../../../../pages/EnablePayments/EnablePayments').default as React.ComponentType, [SCREENS.SETTINGS.ADD_DEBIT_CARD]: () => require('../../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: () => require('../../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, - [SCREENS.SETTINGS.ADD_BANK_ACCOUNT_REFACTOR]: () => require('../../../../pages/EnablePayments/AddBankAccount/AddBankAccount').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.STATUS]: () => require('../../../../pages/settings/Profile/CustomStatus/StatusPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER]: () => require('../../../../pages/settings/Profile/CustomStatus/StatusClearAfterPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE]: () => require('../../../../pages/settings/Profile/CustomStatus/SetDatePage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index a093b778360e..d9982a0bf00c 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -161,16 +161,6 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_ENABLE_PAYMENTS, exact: true, }, - // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_REFACTOR]: { - path: ROUTES.SETTINGS_ENABLE_PAYMENTS_REFACTOR, - exact: true, - }, - // TODO: Added temporarily for testing purposes, remove after refactor - https://github.com/Expensify/App/issues/36648 - [SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS_TEMPORARY_TERMS]: { - path: ROUTES.SETTINGS_ENABLE_PAYMENTS_TEMPORARY_TERMS, - exact: true, - }, [SCREENS.SETTINGS.WALLET.TRANSFER_BALANCE]: { path: ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE, exact: true, @@ -199,10 +189,6 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT, exact: true, }, - [SCREENS.SETTINGS.ADD_BANK_ACCOUNT_REFACTOR]: { - path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT_REFACTOR, - exact: true, - }, [SCREENS.SETTINGS.PROFILE.PRONOUNS]: { path: ROUTES.SETTINGS_PRONOUNS, exact: true, diff --git a/src/pages/EnablePayments/AddBankAccount/SetupMethod.tsx b/src/pages/EnablePayments/AddBankAccount/SetupMethod.tsx index e70d5979e92e..f58c158ba448 100644 --- a/src/pages/EnablePayments/AddBankAccount/SetupMethod.tsx +++ b/src/pages/EnablePayments/AddBankAccount/SetupMethod.tsx @@ -28,7 +28,7 @@ type SetupMethodOnyxProps = { type SetupMethodProps = SetupMethodOnyxProps; const plaidDesktopMessage = getPlaidDesktopMessage(); -const bankAccountRoute = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.SETTINGS_ADD_BANK_ACCOUNT_REFACTOR}`; +const enablePayments = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.SETTINGS_ENABLE_PAYMENTS}`; function SetupMethod({isPlaidDisabled, user}: SetupMethodProps) { const styles = useThemeStyles(); @@ -46,7 +46,7 @@ function SetupMethod({isPlaidDisabled, user}: SetupMethodProps) {
{!!plaidDesktopMessage && ( - {translate(plaidDesktopMessage)} + {translate(plaidDesktopMessage)} )}