From 18d46518d7626e8f0966bc51c408aaa6392fbe09 Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Wed, 22 Nov 2023 00:55:01 -0600 Subject: [PATCH 001/225] fix: check if the parent report is the archived room --- src/libs/actions/Task.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index c91f6d1a2eec..e59debd08a1d 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -877,6 +877,11 @@ function canModifyTask(taskReport, sessionAccountID) { return false; } + const parentReport = ReportUtils.getParentReport(taskReport); + if (ReportUtils.isArchivedRoom(parentReport)) { + return false; + } + if (sessionAccountID === getTaskOwnerAccountID(taskReport) || sessionAccountID === getTaskAssigneeAccountID(taskReport)) { return true; } @@ -884,7 +889,6 @@ function canModifyTask(taskReport, sessionAccountID) { // If you don't have access to the task report (maybe haven't opened it yet), check if you can access the parent report // - If the parent report is an #admins only room // - If you are a policy admin - const parentReport = ReportUtils.getParentReport(taskReport); return ReportUtils.isAllowedToComment(parentReport); } From 6fb66b91cafb483f4739f739816828fcaa01d455 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 1 Dec 2023 13:13:03 +0700 Subject: [PATCH 002/225] go back to correct page in referral page --- src/pages/ReferralDetailsPage.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/pages/ReferralDetailsPage.js b/src/pages/ReferralDetailsPage.js index 60b5d23b39da..2f773d16a8ff 100644 --- a/src/pages/ReferralDetailsPage.js +++ b/src/pages/ReferralDetailsPage.js @@ -17,7 +17,6 @@ import Navigation from '@libs/Navigation/Navigation'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -58,27 +57,13 @@ function ReferralDetailsPage({route, account}) { return `${CONST.REFERRAL_PROGRAM.LINK}/?thanks=${encodeURIComponent(email)}`; } - function getFallbackRoute() { - const fallbackRoutes = { - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(CONST.IOU.TYPE.REQUEST), - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(CONST.IOU.TYPE.SEND), - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: ROUTES.NEW_CHAT, - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: ROUTES.SEARCH, - }; - - return fallbackRoutes[contentType]; - } - return ( - Navigation.goBack(getFallbackRoute())} - /> + Navigation.goBack(getFallbackRoute())} + onPress={() => Navigation.goBack()} pressOnEnter enterKeyEventListenerPriority={1} /> From 1f22bec902d8e96699377a9d10168752e4f8fc61 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 4 Dec 2023 13:46:19 +0700 Subject: [PATCH 003/225] add fallback route for confirm button --- src/pages/ReferralDetailsPage.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pages/ReferralDetailsPage.js b/src/pages/ReferralDetailsPage.js index 2f773d16a8ff..2b6bc9249bc6 100644 --- a/src/pages/ReferralDetailsPage.js +++ b/src/pages/ReferralDetailsPage.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, {useMemo} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -17,6 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -53,6 +54,17 @@ function ReferralDetailsPage({route, account}) { const shouldShowBody2 = isShareCode; const shouldShowClipboard = contentType === CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND || isShareCode; + const fallBackRoute = useMemo(() => { + const fallbackRoutes = { + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(CONST.IOU.TYPE.REQUEST), + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(CONST.IOU.TYPE.SEND), + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: ROUTES.NEW_CHAT, + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: ROUTES.SEARCH, + }; + + return fallbackRoutes[contentType]; + }, [contentType]); + function generateReferralURL(email) { return `${CONST.REFERRAL_PROGRAM.LINK}/?thanks=${encodeURIComponent(email)}`; } @@ -93,7 +105,7 @@ function ReferralDetailsPage({route, account}) { success style={[styles.w100]} text={translate('common.buttonConfirm')} - onPress={() => Navigation.goBack()} + onPress={() => Navigation.goBack(fallBackRoute)} pressOnEnter enterKeyEventListenerPriority={1} /> From 958bb7027f075cf71c6241434b9ced2cc6b8dc75 Mon Sep 17 00:00:00 2001 From: s-alves10 Date: Wed, 20 Dec 2023 04:54:23 -0600 Subject: [PATCH 004/225] fix: remove redundant code --- src/libs/actions/Task.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 03b94d5f82de..50172158efdb 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -882,8 +882,6 @@ function canModifyTask(taskReport, sessionAccountID, policyRole = '') { return true; } - const parentReport = ReportUtils.getParentReport(taskReport); - if (policyRole && (ReportUtils.isChatRoom(parentReport) || ReportUtils.isPolicyExpenseChat(parentReport)) && policyRole !== CONST.POLICY.ROLE.ADMIN) { return false; } From 2c2b068bf13be2ebef31382f42e2c830679c8814 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 25 Dec 2023 16:20:11 +0700 Subject: [PATCH 005/225] refactor referral route --- src/ROUTES.ts | 20 ++++++++++++++++ src/SCREENS.ts | 5 ++++ .../OptionsSelector/BaseOptionsSelector.js | 6 +++-- .../AppNavigator/ModalStackNavigators.tsx | 5 ++++ src/libs/Navigation/linkingConfig.ts | 5 ++++ src/pages/NewChatPage.js | 2 ++ src/pages/ReferralDetailsPage.js | 23 +++++++++++++++++-- src/pages/SearchPage.js | 2 ++ src/pages/ShareCodePage.js | 2 +- ...yForRefactorRequestParticipantsSelector.js | 14 +++++++++++ .../step/IOURequestStepParticipants.js | 2 ++ 11 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ca1fe9f0e81a..64de8064ba81 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -463,6 +463,26 @@ const ROUTES = { route: 'referral/:contentType', getRoute: (contentType: string) => `referral/${contentType}` as const, }, + REFERRAL_DETAILS_MODAL_REQUEST: { + route: 'create/request/participants/:transactionID/:reportID/referral/:contentType', + getRoute: (transactionID: string, reportID: string, contentType: string) => `create/request/participants/${transactionID}/${reportID}/referral/${contentType}` as const, + }, + REFERRAL_DETAILS_MODAL_START_CHAT: { + route: 'new/referral/:contentType', + getRoute: (contentType: string) => `new/referral/${contentType}` as const, + }, + REFERRAL_DETAILS_MODAL_SEND_MONEY: { + route: 'send/new/participants/referral/:contentType', + getRoute: (contentType: string) => `send/new/participants/referral/${contentType}` as const, + }, + REFERRAL_DETAILS_MODAL_REFER_FRIEND: { + route: 'search/referral/:contentType', + getRoute: (contentType: string) => `search/referral/${contentType}` as const, + }, + REFERRAL_DETAILS_MODAL_SHARE_CODE: { + route: 'settings/shareCode/referral/:contentType', + getRoute: (contentType: string) => `settings/shareCode/referral/${contentType}` as const, + }, // These are some one-off routes that will be removed once they're no longer needed (see GH issues for details) SAASTR: 'saastr', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2cd263237866..b8282ce2d8b7 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -235,6 +235,11 @@ const SCREENS = { REIMBURSEMENT_ACCOUNT: 'ReimbursementAccount', GET_ASSISTANCE: 'GetAssistance', REFERRAL_DETAILS: 'Referral_Details', + REFERRAL_DETAILS_MONEY_REQUEST: 'Referral_Details_Money_Request', + REFERRAL_DETAILS_START_CHAT: 'Referral_Details_Start_Chat', + REFERRAL_DETAILS_SEND_MONEY: 'Referral_Details_Send_Money', + REFERRAL_DETAILS_REFER_FRIEND: 'Referral_Details_Refer_Friend', + REFERRAL_DETAILS_SHARE_CODE: 'Referral_Details_Share_Code', KEYBOARD_SHORTCUTS: 'KeyboardShortcuts', } as const; diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 3c40b3cf1144..725d55cf5925 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -24,7 +24,6 @@ import KeyboardShortcut from '@libs/KeyboardShortcut'; import Navigation from '@libs/Navigation/Navigation'; import setSelection from '@libs/setSelection'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; import {defaultProps as optionsSelectorDefaultProps, propTypes as optionsSelectorPropTypes} from './optionsSelectorPropTypes'; const propTypes = { @@ -49,6 +48,9 @@ const propTypes = { /** Referral content type */ referralContentType: PropTypes.string, + /** Referral route */ + referralRoute: PropTypes.string, + ...optionsSelectorPropTypes, ...withLocalizePropTypes, ...withThemeStylesPropTypes, @@ -621,7 +623,7 @@ class BaseOptionsSelector extends Component { { - Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(this.props.referralContentType)); + Navigation.navigate(this.props.referralRoute); }} style={[ this.props.themeStyles.p5, diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 256ea6d4eceb..37c751235086 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -277,6 +277,11 @@ const SignInModalStackNavigator = createModalStackNavigator({ [SCREENS.REFERRAL_DETAILS]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, + [SCREENS.REFERRAL_DETAILS_MONEY_REQUEST]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, + [SCREENS.REFERRAL_DETAILS_START_CHAT]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, + [SCREENS.REFERRAL_DETAILS_SEND_MONEY]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, + [SCREENS.REFERRAL_DETAILS_REFER_FRIEND]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, + [SCREENS.REFERRAL_DETAILS_SHARE_CODE]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, }); export { diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 0383455a5946..e5979bb78075 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -487,6 +487,11 @@ const linkingConfig: LinkingOptions = { [SCREENS.RIGHT_MODAL.REFERRAL]: { screens: { [SCREENS.REFERRAL_DETAILS]: ROUTES.REFERRAL_DETAILS_MODAL.route, + [SCREENS.REFERRAL_DETAILS_MONEY_REQUEST]: ROUTES.REFERRAL_DETAILS_MODAL_REQUEST.route, + [SCREENS.REFERRAL_DETAILS_START_CHAT]: ROUTES.REFERRAL_DETAILS_MODAL_START_CHAT.route, + [SCREENS.REFERRAL_DETAILS_SEND_MONEY]: ROUTES.REFERRAL_DETAILS_MODAL_SEND_MONEY.route, + [SCREENS.REFERRAL_DETAILS_REFER_FRIEND]: ROUTES.REFERRAL_DETAILS_MODAL_REFER_FRIEND.route, + [SCREENS.REFERRAL_DETAILS_SHARE_CODE]: ROUTES.REFERRAL_DETAILS_MODAL_SHARE_CODE.route, }, }, }, diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index d7abbab6e93f..60f4d7c1334a 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -22,6 +22,7 @@ import variables from '@styles/variables'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; @@ -253,6 +254,7 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i shouldShowConfirmButton shouldShowReferralCTA referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT} + referralRoute={ROUTES.REFERRAL_DETAILS_MODAL_START_CHAT.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT)} confirmButtonText={selectedOptions.length > 1 ? translate('newChatPage.createGroup') : translate('newChatPage.createChat')} textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''} onConfirmSelection={createGroup} diff --git a/src/pages/ReferralDetailsPage.js b/src/pages/ReferralDetailsPage.js index fb02778db72d..49671a59a204 100644 --- a/src/pages/ReferralDetailsPage.js +++ b/src/pages/ReferralDetailsPage.js @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; -import React, {useMemo, useRef} from 'react'; -import {View} from 'react-native'; +import React, {useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import ContextMenuItem from '@components/ContextMenuItem'; @@ -15,9 +14,11 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; +import Navigation from '@libs/Navigation/Navigation'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import * as ReportActionContextMenu from './home/report/ContextMenu/ReportActionContextMenu'; @@ -25,6 +26,10 @@ const propTypes = { /** Navigation route context info provided by react navigation */ route: PropTypes.shape({ params: PropTypes.shape({ + /** The ID of the transaction being configured */ + transactionID: PropTypes.string, + /** The report ID of the IOU */ + reportID: PropTypes.string, /** The type of the content from where CTA was called */ contentType: PropTypes.string, }), @@ -48,6 +53,7 @@ function ReferralDetailsPage({route, account}) { const popoverAnchor = useRef(null); const {isExecuting, singleExecution} = useSingleExecution(); let {contentType} = route.params; + const {transactionID, reportID} = route.params; if (!_.includes(_.values(CONST.REFERRAL_PROGRAM.CONTENT_TYPES), contentType)) { contentType = CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND; @@ -59,6 +65,18 @@ function ReferralDetailsPage({route, account}) { const shouldShowClipboard = contentType === CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND || isShareCode; const referralLink = `${CONST.REFERRAL_PROGRAM.LINK}/?thanks=${encodeURIComponent(account.primaryLogin)}`; + function getFallbackRoute() { + const fallbackRoutes = { + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(CONST.IOU.TYPE.REQUEST, transactionID, reportID), + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(CONST.IOU.TYPE.SEND), + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: ROUTES.NEW_CHAT, + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: ROUTES.SEARCH, + [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE]: ROUTES.SETTINGS_SHARE_CODE, + }; + + return fallbackRoutes[contentType]; + } + return ( Navigation.navigate(getFallbackRoute())} > {contentHeader} {contentBody} diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 061f43e73de8..da04fe70fba6 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -16,6 +16,7 @@ import * as Report from '@userActions/Report'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; @@ -181,6 +182,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports}) { textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} shouldShowReferralCTA referralContentType={CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND} + referralRoute={ROUTES.REFERRAL_DETAILS_MODAL_REFER_FRIEND.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND)} textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''} onLayout={searchRendered} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index 1f062a42f8bf..65c952016b5a 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -121,7 +121,7 @@ class ShareCodePage extends React.Component { Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE))} + onPress={() => Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL_SHARE_CODE.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE))} /> diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 4db9c4ce3fb7..c8d94d247278 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -20,6 +20,7 @@ import personalDetailsPropType from '@pages/personalDetailsPropType'; import reportPropTypes from '@pages/reportPropTypes'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; const propTypes = { /** Beta features list */ @@ -63,6 +64,12 @@ const propTypes = { /** Whether we are searching for reports in the server */ isSearchingForReports: PropTypes.bool, + /** The report ID of the IOU */ + reportID: PropTypes.string.isRequired, + + /** The ID of the transaction being configured */ + transactionID: PropTypes.string.isRequired, + ...withLocalizePropTypes, }; @@ -89,6 +96,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ iouType, iouRequestType, isSearchingForReports, + reportID, + transactionID, }) { const styles = useThemeStyles(); const [searchTerm, setSearchTerm] = useState(''); @@ -321,6 +330,11 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ shouldShowOptions={isOptionsDataReady} shouldShowReferralCTA referralContentType={iouType === 'send' ? CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY : CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST} + referralRoute={ + iouType === 'send' + ? ROUTES.REFERRAL_DETAILS_MODAL_SEND_MONEY.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY) + : ROUTES.REFERRAL_DETAILS_MODAL_REQUEST.getRoute(transactionID, reportID, CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST) + } shouldPreventDefaultFocusOnSelectRow={!Browser.isMobile()} shouldDelayFocus footerContent={isAllowedToSplit && footerContent} diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index ec670b828146..4bb676cab7af 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -95,6 +95,8 @@ function IOURequestStepParticipants({ onParticipantsAdded={addParticipant} onFinish={goToNextStep} iouType={iouType} + reportID={reportID} + transactionID={transactionID} iouRequestType={iouRequestType} /> From 7c01d82bbc3e5b749cb45758b915af0254d0100b Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 10 Jan 2024 23:10:50 +0700 Subject: [PATCH 006/225] migrate to ts --- src/CONST.ts | 4 +- src/components/AddPaymentMethodMenu.js | 13 +- src/components/KYCWall/index.js | 3 +- src/libs/CardUtils.ts | 10 +- src/libs/PaymentUtils.ts | 2 +- src/libs/actions/BankAccounts.ts | 2 +- src/libs/actions/PaymentMethods.ts | 14 +- ...entMethodList.js => PaymentMethodList.tsx} | 214 +++++++----------- ...lletEmptyState.js => WalletEmptyState.tsx} | 10 +- .../{CardDetails.js => CardDetails.tsx} | 67 +++--- .../{WalletPage.js => WalletPage.tsx} | 163 +++++++------ .../{index.native.js => index.native.tsx} | 0 .../Wallet/WalletPage/{index.js => index.tsx} | 0 src/pages/settings/Wallet/WalletPage/types.ts | 31 +++ .../Wallet/WalletPage/walletPagePropTypes.js | 52 ----- src/types/onyx/AccountData.ts | 2 + src/types/onyx/Card.ts | 4 +- src/types/onyx/index.ts | 2 + 18 files changed, 271 insertions(+), 322 deletions(-) rename src/pages/settings/Wallet/{PaymentMethodList.js => PaymentMethodList.tsx} (73%) rename src/pages/settings/Wallet/{WalletEmptyState.js => WalletEmptyState.tsx} (87%) rename src/pages/settings/Wallet/WalletPage/{CardDetails.js => CardDetails.tsx} (75%) rename src/pages/settings/Wallet/WalletPage/{WalletPage.js => WalletPage.tsx} (87%) rename src/pages/settings/Wallet/WalletPage/{index.native.js => index.native.tsx} (100%) rename src/pages/settings/Wallet/WalletPage/{index.js => index.tsx} (100%) create mode 100644 src/pages/settings/Wallet/WalletPage/types.ts delete mode 100644 src/pages/settings/Wallet/WalletPage/walletPagePropTypes.js diff --git a/src/CONST.ts b/src/CONST.ts index c6849db630f2..c0eecf6858e3 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1350,7 +1350,9 @@ const CONST = { CLOSED: 6, STATE_SUSPENDED: 7, }, - ACTIVE_STATES: [2, 3, 4, 7], + get ACTIVE_STATES() { + return [2, 3, 4, 7]; + }, }, AVATAR_ROW_SIZE: { DEFAULT: 4, diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index 4abe5655e307..a936b0efdfa1 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -122,11 +122,8 @@ AddPaymentMethodMenu.propTypes = propTypes; AddPaymentMethodMenu.defaultProps = defaultProps; AddPaymentMethodMenu.displayName = 'AddPaymentMethodMenu'; -export default compose( - withWindowDimensions, - withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - }), -)(AddPaymentMethodMenu); +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(AddPaymentMethodMenu); diff --git a/src/components/KYCWall/index.js b/src/components/KYCWall/index.js index 49329c73d474..87f1496a494a 100644 --- a/src/components/KYCWall/index.js +++ b/src/components/KYCWall/index.js @@ -2,12 +2,13 @@ import React from 'react'; import BaseKYCWall from './BaseKYCWall'; import {defaultProps, propTypes} from './kycWallPropTypes'; -function KYCWall(props) { +function KYCWall({children, ...props}) { return ( ); } diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index d71ad9c2629a..bc18808886e2 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -29,7 +29,10 @@ function getMonthFromExpirationDateString(expirationDateString: string) { * @param cardID * @returns boolean */ -function isExpensifyCard(cardID: number) { +function isExpensifyCard(cardID?: number) { + if (!cardID) { + return false; + } const card = allCards[cardID]; if (!card) { return false; @@ -49,7 +52,10 @@ function isCorporateCard(cardID: number) { * @param cardID * @returns string in format % - %. */ -function getCardDescription(cardID: number) { +function getCardDescription(cardID?: number) { + if (!cardID) { + return ''; + } const card = allCards[cardID]; if (!card) { return ''; diff --git a/src/libs/PaymentUtils.ts b/src/libs/PaymentUtils.ts index dd35d0df5cfb..60dac04a09ac 100644 --- a/src/libs/PaymentUtils.ts +++ b/src/libs/PaymentUtils.ts @@ -40,7 +40,7 @@ function getPaymentMethodDescription(accountType: AccountType, account: BankAcco /** * Get the PaymentMethods list */ -function formatPaymentMethods(bankAccountList: Record, fundList: Record, styles: ThemeStyles): PaymentMethod[] { +function formatPaymentMethods(bankAccountList: Record, fundList: Record | Fund[], styles: ThemeStyles): PaymentMethod[] { const combinedPaymentMethods: PaymentMethod[] = []; Object.values(bankAccountList).forEach((bankAccount) => { diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index f7b7ec89c670..1ce6bc38191f 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -50,7 +50,7 @@ function setPlaidEvent(eventName: string) { /** * Open the personal bank account setup flow, with an optional exitReportID to redirect to once the flow is finished. */ -function openPersonalBankAccountSetupView(exitReportID: string) { +function openPersonalBankAccountSetupView(exitReportID?: string) { clearPlaid().then(() => { if (exitReportID) { Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {exitReportID}); diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index a7ae54f46416..6614d3516253 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -71,10 +71,10 @@ function openWalletPage() { } function getMakeDefaultPaymentOnyxData( - bankAccountID: number, - fundID: number, - previousPaymentMethod: PaymentMethod, - currentPaymentMethod: PaymentMethod, + bankAccountID: number | null, + fundID: number | null, + previousPaymentMethod?: PaymentMethod, + currentPaymentMethod?: PaymentMethod, isOptimisticData = true, ): OnyxUpdate[] { const onyxData: OnyxUpdate[] = [ @@ -130,10 +130,10 @@ function getMakeDefaultPaymentOnyxData( * Sets the default bank account or debit card for an Expensify Wallet * */ -function makeDefaultPaymentMethod(bankAccountID: number, fundID: number, previousPaymentMethod: PaymentMethod, currentPaymentMethod: PaymentMethod) { +function makeDefaultPaymentMethod(bankAccountID: number | null, fundID: number | null, previousPaymentMethod?: PaymentMethod, currentPaymentMethod?: PaymentMethod) { type MakeDefaultPaymentMethodParams = { - bankAccountID: number; - fundID: number; + bankAccountID: number | null; + fundID: number | null; }; const parameters: MakeDefaultPaymentMethodParams = { diff --git a/src/pages/settings/Wallet/PaymentMethodList.js b/src/pages/settings/Wallet/PaymentMethodList.tsx similarity index 73% rename from src/pages/settings/Wallet/PaymentMethodList.js rename to src/pages/settings/Wallet/PaymentMethodList.tsx index 06bd8afa6140..9cceaa60c10c 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.js +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -1,15 +1,17 @@ import {FlashList} from '@shopify/flash-list'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useCallback, useMemo} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import React, {ReactElement, Ref, useCallback, useMemo} from 'react'; +import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; +import {TupleToUnion, ValueOf} from 'type-fest'; import _ from 'underscore'; import bankAccountPropTypes from '@components/bankAccountPropTypes'; import Button from '@components/Button'; import cardPropTypes from '@components/cardPropTypes'; import FormAlertWrapper from '@components/FormAlertWrapper'; import getBankIcon from '@components/Icon/BankIcons'; +import {BankName} from '@components/Icon/BankIconsUtils'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -28,112 +30,91 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import assignedCardPropTypes from './assignedCardPropTypes'; +import {AccountData, BankAccountList, Card, CardList, FundList, UserWallet} from '@src/types/onyx'; +import PaymentMethod from '@src/types/onyx/PaymentMethod'; -const propTypes = { - /** What to do when a menu item is pressed */ - onPress: PropTypes.func.isRequired, +const FILTER_TYPES = [CONST.PAYMENT_METHODS.DEBIT_CARD, CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, ''] as const; +const ACTION_PAYMENT_METHOD_TYPES = [...Object.values(CONST.PAYMENT_METHODS), ''] as const; +type PaymentMethodListOnyxProps = { /** List of bank accounts */ - bankAccountList: PropTypes.objectOf(bankAccountPropTypes), + bankAccountList: OnyxEntry; /** List of assigned cards */ - cardList: PropTypes.objectOf(assignedCardPropTypes), + cardList: OnyxEntry; /** List of user's cards */ - fundList: PropTypes.objectOf(cardPropTypes), - - /** Whether the add bank account button should be shown on the list */ - shouldShowAddBankAccount: PropTypes.bool, - - /** Whether the add Payment button be shown on the list */ - shouldShowAddPaymentMethodButton: PropTypes.bool, - - /** Whether the assigned cards should be shown on the list */ - shouldShowAssignedCards: PropTypes.bool, - - /** Whether the empty list message should be shown when the list is empty */ - shouldShowEmptyListMessage: PropTypes.bool, + fundList: OnyxEntry; /** Are we loading payment methods? */ - isLoadingPaymentMethods: PropTypes.bool, - - /** Type to filter the payment Method list */ - filterType: PropTypes.oneOf([CONST.PAYMENT_METHODS.DEBIT_CARD, CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, '']), + isLoadingPaymentMethods: OnyxEntry; /** User wallet props */ - userWallet: PropTypes.shape({ - /** The ID of the linked account */ - walletLinkedAccountID: PropTypes.number, - - /** The type of the linked account (debitCard or bankAccount) */ - walletLinkedAccountType: PropTypes.string, - }), + userWallet: OnyxEntry; +}; +type PaymentMethodListProps = PaymentMethodListOnyxProps & { /** Type of active/highlighted payment method */ - actionPaymentMethodType: PropTypes.oneOf([..._.values(CONST.PAYMENT_METHODS), '']), + actionPaymentMethodType?: TupleToUnion; /** ID of active/highlighted payment method */ - activePaymentMethodID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + activePaymentMethodID?: string | number; /** ID of selected payment method */ - selectedMethodID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + selectedMethodID?: string | number; /** Content for the FlatList header component */ - listHeaderComponent: PropTypes.func, + listHeaderComponent?: ReactElement; /** Callback for whenever FlatList component size changes */ - onListContentSizeChange: PropTypes.func, + onListContentSizeChange?: () => void; /** Should menu items be selectable with a checkbox */ - shouldShowSelectedState: PropTypes.bool, + shouldShowSelectedState?: boolean; /** React ref being forwarded to the PaymentMethodList Button */ - buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + buttonRef?: Ref; /** To enable/disable scrolling */ - shouldEnableScroll: PropTypes.bool, + shouldEnableScroll?: boolean; /** List container style */ - style: stylePropTypes, + style?: StyleProp; + + /** Type to filter the payment Method list */ + filterType?: TupleToUnion; + /** Whether the add bank account button should be shown on the list */ + shouldShowAddBankAccount?: boolean; + + /** Whether the add Payment button be shown on the list */ + shouldShowAddPaymentMethodButton?: boolean; + + /** Whether the assigned cards should be shown on the list */ + shouldShowAssignedCards?: boolean; + + /** Whether the empty list message should be shown when the list is empty */ + shouldShowEmptyListMessage?: boolean; + + /** What to do when a menu item is pressed */ + onPress: (event?: GestureResponderEvent | KeyboardEvent, accountType?: string, accountData?: AccountData, isDefault?: boolean, methodID?: number) => void | Promise; }; -const defaultProps = { - bankAccountList: {}, - cardList: {}, - fundList: null, - userWallet: { - walletLinkedAccountID: 0, - walletLinkedAccountType: '', - }, - isLoadingPaymentMethods: true, - shouldShowAddBankAccount: true, - shouldShowAddPaymentMethodButton: true, - shouldShowAssignedCards: false, - shouldShowEmptyListMessage: true, - filterType: '', - actionPaymentMethodType: '', - activePaymentMethodID: '', - selectedMethodID: '', - listHeaderComponent: null, - buttonRef: () => {}, - onListContentSizeChange: () => {}, - shouldEnableScroll: true, - style: {}, - shouldShowSelectedState: false, +type PaymentMethodItem = PaymentMethod & { + onPress?: (e: GestureResponderEvent | KeyboardEvent | undefined) => void; + canDismissError?: boolean; + disabled?: boolean; + shouldShowRightIcon?: boolean; + interactive?: boolean; + brickRoadIndicator?: ValueOf; }; -/** - * Dismisses the error on the payment method - * @param {Object} item - */ -function dismissError(item) { +function dismissError(item: PaymentMethod) { const isBankAccount = item.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT; const paymentList = isBankAccount ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.FUND_LIST; const paymentID = isBankAccount ? lodashGet(item, ['accountData', 'bankAccountID'], '') : lodashGet(item, ['accountData', 'fundID'], ''); if (!paymentID) { - Log.info('Unable to clear payment method error: ', item); + Log.info('Unable to clear payment method error: ', undefined, item); return; } @@ -150,12 +131,7 @@ function dismissError(item) { } } -/** - * @param {Array} filteredPaymentMethods - * @param {Boolean} isDefault - * @returns {Boolean} - */ -function shouldShowDefaultBadge(filteredPaymentMethods, isDefault = false) { +function shouldShowDefaultBadge(filteredPaymentMethods: PaymentMethod[], isDefault = false) { if (!isDefault) { return false; } @@ -167,45 +143,35 @@ function shouldShowDefaultBadge(filteredPaymentMethods, isDefault = false) { return defaultablePaymentMethodCount > 1; } -/** - * @param {String} actionPaymentMethodType - * @param {String|Number} activePaymentMethodID - * @param {String} paymentMethod - * @return {Boolean} - */ -function isPaymentMethodActive(actionPaymentMethodType, activePaymentMethodID, paymentMethod) { +function isPaymentMethodActive(actionPaymentMethodType: string, activePaymentMethodID: string | number, paymentMethod: PaymentMethod) { return paymentMethod.accountType === actionPaymentMethodType && paymentMethod.methodID === activePaymentMethodID; } -/** - * @param {Object} item - * @returns {String} - */ -function keyExtractor(item) { - return item.key; +function keyExtractor(item: PaymentMethod) { + return item.key || ''; } function PaymentMethodList({ - actionPaymentMethodType, - activePaymentMethodID, - bankAccountList, - buttonRef, - cardList, - fundList, - filterType, - isLoadingPaymentMethods, - onPress, - shouldShowSelectedState, - shouldShowAddPaymentMethodButton, - shouldShowAddBankAccount, - shouldShowEmptyListMessage, - shouldShowAssignedCards, - selectedMethodID, + actionPaymentMethodType = '', + activePaymentMethodID = '', + bankAccountList = {}, + buttonRef = () => {}, + cardList = {}, + fundList = {}, + filterType = '', listHeaderComponent, - onListContentSizeChange, - shouldEnableScroll, - style, -}) { + isLoadingPaymentMethods = true, + onPress, + shouldShowSelectedState = false, + shouldShowAddPaymentMethodButton = true, + shouldShowAddBankAccount = true, + shouldShowEmptyListMessage = true, + shouldShowAssignedCards = false, + selectedMethodID = '', + onListContentSizeChange = () => {}, + shouldEnableScroll = true, + style = {}, +}: PaymentMethodListProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -215,7 +181,7 @@ function PaymentMethodList({ if (shouldShowAssignedCards) { const assignedCards = _.chain(cardList) // Filter by physical, active cards associated with a domain - .filter((card) => !card.isVirtual && card.domainName && CONST.EXPENSIFY_CARD.ACTIVE_STATES.includes(card.state)) + .filter((card) => !card.isVirtual && card.domainName && CONST.EXPENSIFY_CARD.ACTIVE_STATES.includes(card.state ?? 0)) .sortBy((card) => !CardUtils.isExpensifyCard(card.cardID)) .value(); @@ -223,15 +189,15 @@ function PaymentMethodList({ return _.map(assignedCards, (card) => { const isExpensifyCard = CardUtils.isExpensifyCard(card.cardID); - const icon = getBankIcon({bankName: card.bank, isCard: true, styles}); + const icon = getBankIcon({bankName: card.bank as BankName, isCard: true, styles}); // In the case a user has been assigned multiple physical Expensify Cards under one domain, display the Card with PAN const expensifyCardDescription = numberPhysicalExpensifyCards > 1 ? CardUtils.getCardDescription(card.cardID) : translate('walletPage.expensifyCard'); return { - key: card.cardID, + key: card.cardID.toString(), title: isExpensifyCard ? expensifyCardDescription : card.cardName, description: card.domainName, - onPress: isExpensifyCard ? () => Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(card.domainName)) : () => {}, + onPress: isExpensifyCard ? () => Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(card.domainName ?? '')) : () => {}, shouldShowRightIcon: isExpensifyCard, interactive: isExpensifyCard, canDismissError: isExpensifyCard, @@ -245,8 +211,8 @@ function PaymentMethodList({ const paymentCardList = fundList || {}; // Hide any billing cards that are not P2P debit cards for now because you cannot make them your default method, or delete them - const filteredCardList = _.filter(paymentCardList, (card) => card.accountData.additionalData.isP2PDebitCard); - let combinedPaymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList, filteredCardList, styles); + const filteredCardList = _.filter(paymentCardList, (card) => !!card.accountData?.additionalData?.isP2PDebitCard); + let combinedPaymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList ?? {}, filteredCardList, styles); if (!_.isEmpty(filterType)) { combinedPaymentMethods = _.filter(combinedPaymentMethods, (paymentMethod) => paymentMethod.accountType === filterType); @@ -261,10 +227,9 @@ function PaymentMethodList({ combinedPaymentMethods = _.map(combinedPaymentMethods, (paymentMethod) => { const isMethodActive = isPaymentMethodActive(actionPaymentMethodType, activePaymentMethodID, paymentMethod); - return { ...paymentMethod, - onPress: (e) => onPress(e, paymentMethod.accountType, paymentMethod.accountData, paymentMethod.isDefault, paymentMethod.methodID), + onPress: (e: GestureResponderEvent) => onPress(e, paymentMethod.accountType, paymentMethod.accountData, paymentMethod.isDefault, paymentMethod.methodID), wrapperStyle: isMethodActive ? [StyleUtils.getButtonBackgroundColorStyle(CONST.BUTTON_STATES.PRESSED)] : null, disabled: paymentMethod.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, }; @@ -296,14 +261,9 @@ function PaymentMethodList({ /** * Create a menuItem for each passed paymentMethod - * - * @param {Object} params - * @param {Object} params.item - * - * @return {React.Component} */ const renderItem = useCallback( - ({item}) => ( + ({item}: {item: PaymentMethodItem}) => ( dismissError(item)} pendingAction={item.pendingAction} @@ -321,7 +281,7 @@ function PaymentMethodList({ iconHeight={item.iconHeight || item.iconSize} iconWidth={item.iconWidth || item.iconSize} iconStyles={item.iconStyles} - badgeText={shouldShowDefaultBadge(filteredPaymentMethods, item.isDefault) ? translate('paymentMethodList.defaultPaymentMethod') : null} + badgeText={shouldShowDefaultBadge(filteredPaymentMethods, item.isDefault) ? translate('paymentMethodList.defaultPaymentMethod') : undefined} wrapperStyle={styles.paymentMethod} shouldShowRightIcon={item.shouldShowRightIcon} shouldShowSelectedState={shouldShowSelectedState} @@ -340,7 +300,7 @@ function PaymentMethodList({ ({ bankAccountList: { key: ONYXKEYS.BANK_ACCOUNT_LIST, }, diff --git a/src/pages/settings/Wallet/WalletEmptyState.js b/src/pages/settings/Wallet/WalletEmptyState.tsx similarity index 87% rename from src/pages/settings/Wallet/WalletEmptyState.js rename to src/pages/settings/Wallet/WalletEmptyState.tsx index 7a3a9e9ce6b7..4ef9488368cb 100644 --- a/src/pages/settings/Wallet/WalletEmptyState.js +++ b/src/pages/settings/Wallet/WalletEmptyState.tsx @@ -11,9 +11,8 @@ import Navigation from '@libs/Navigation/Navigation'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -const propTypes = { - /** The function that is called when a menu item is pressed */ - onAddPaymentMethod: PropTypes.func.isRequired, +type WalletEmptyStateProps = { + onAddPaymentMethod: () => void; }; const WALLET_FEATURES = [ @@ -31,7 +30,7 @@ const WALLET_FEATURES = [ }, ]; -function WalletEmptyState({onAddPaymentMethod}) { +function WalletEmptyState({onAddPaymentMethod}: WalletEmptyStateProps) { const theme = useTheme(); const {translate} = useLocalize(); return ( @@ -58,7 +57,4 @@ function WalletEmptyState({onAddPaymentMethod}) { ); } -WalletEmptyState.displayName = 'WalletEmptyState'; -WalletEmptyState.propTypes = propTypes; - export default WalletEmptyState; diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.js b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx similarity index 75% rename from src/pages/settings/Wallet/WalletPage/CardDetails.js rename to src/pages/settings/Wallet/WalletPage/CardDetails.tsx index b51c34e89d17..09a7d937053b 100644 --- a/src/pages/settings/Wallet/WalletPage/CardDetails.js +++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayToggle'; @@ -14,50 +14,39 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import {PrivatePersonalDetails} from '@src/types/onyx'; -const propTypes = { +const defaultPrivatePersonalDetails = { + address: { + street: '', + street2: '', + city: '', + state: '', + zip: '', + country: '', + }, +}; + +type CardDetailsOnyxProps = { + /** User's private personal details */ + privatePersonalDetails: OnyxEntry; +}; + +type CardDetailsProps = CardDetailsOnyxProps & { /** Card number */ - pan: PropTypes.string, + pan?: string; /** Card expiration date */ - expiration: PropTypes.string, + expiration?: string; /** 3 digit code */ - cvv: PropTypes.string, - - /** User's private personal details */ - privatePersonalDetails: PropTypes.shape({ - /** User's home address */ - address: PropTypes.shape({ - street: PropTypes.string, - city: PropTypes.string, - state: PropTypes.string, - zip: PropTypes.string, - country: PropTypes.string, - }), - }), + cvv?: string; /** Domain name */ - domain: PropTypes.string.isRequired, -}; - -const defaultProps = { - pan: '', - expiration: '', - cvv: '', - privatePersonalDetails: { - address: { - street: '', - street2: '', - city: '', - state: '', - zip: '', - country: '', - }, - }, + domain: string; }; -function CardDetails({pan, expiration, cvv, privatePersonalDetails, domain}) { +function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetails = defaultPrivatePersonalDetails, domain}: CardDetailsProps) { const styles = useThemeStyles(); usePrivatePersonalDetails(); const {translate} = useLocalize(); @@ -79,6 +68,8 @@ function CardDetails({pan, expiration, cvv, privatePersonalDetails, domain}) { tooltipTextChecked={translate('reportActionContextMenu.copied')} icon={Expensicons.Copy} onPress={handleCopyToClipboard} + accessible={false} + text="" /> } @@ -96,7 +87,7 @@ function CardDetails({pan, expiration, cvv, privatePersonalDetails, domain}) { /> ({ privatePersonalDetails: { key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS, }, diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.js b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx similarity index 87% rename from src/pages/settings/Wallet/WalletPage/WalletPage.js rename to src/pages/settings/Wallet/WalletPage/WalletPage.tsx index bf547bc4bd10..0463008cf7cb 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.js +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -1,7 +1,9 @@ import lodashGet from 'lodash/get'; -import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; +import React, {Ref, useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; import {ActivityIndicator, Dimensions, ScrollView, View} from 'react-native'; +import {GestureResponderEvent} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import {TupleToUnion} from 'type-fest'; import _ from 'underscore'; import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu'; import Button from '@components/Button'; @@ -20,6 +22,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import WalletSection from '@components/WalletSection'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -35,27 +38,46 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {defaultProps, propTypes} from './walletPagePropTypes'; +import {AccountData, BankAccount} from '@src/types/onyx'; +import PaymentMethod from '@src/types/onyx/PaymentMethod'; +import IconAsset from '@src/types/utils/IconAsset'; +import {WalletPageOnyxProps, WalletPageProps} from './types'; + +const ACTION_PAYMENT_METHOD_TYPES = [...Object.values(CONST.PAYMENT_METHODS), ''] as const; + +type PaymentMethodState = { + isSelectedPaymentMethodDefault: boolean; + selectedPaymentMethod: AccountData; + formattedSelectedPaymentMethod: { + title: string; + icon?: IconAsset; + description?: string; + type?: string; + }; + methodID: string | number; + selectedPaymentMethodType: string; +}; -function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethods, network, shouldListenForResize, userWallet, walletTerms}) { +function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadingPaymentMethods = true, shouldListenForResize = false, userWallet, walletTerms = {}}: WalletPageProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); + const network = useNetwork(); const {isSmallScreenWidth, windowWidth} = useWindowDimensions(); const [shouldShowAddPaymentMenu, setShouldShowAddPaymentMenu] = useState(false); const [shouldShowDefaultDeleteMenu, setShouldShowDefaultDeleteMenu] = useState(false); const [shouldShowLoadingSpinner, setShouldShowLoadingSpinner] = useState(false); - const [paymentMethod, setPaymentMethod] = useState({ + const [paymentMethod, setPaymentMethod] = useState({ isSelectedPaymentMethodDefault: false, selectedPaymentMethod: {}, formattedSelectedPaymentMethod: { title: '', }, - methodID: null, - selectedPaymentMethodType: null, + methodID: '', + selectedPaymentMethodType: '', }); const addPaymentMethodAnchorRef = useRef(null); - const paymentMethodButtonRef = useRef(null); + const paymentMethodButtonRef = useRef(null); const [anchorPosition, setAnchorPosition] = useState({ anchorPositionHorizontal: 0, anchorPositionVertical: 0, @@ -66,7 +88,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod const hasBankAccount = !_.isEmpty(bankAccountList) || !_.isEmpty(fundList); const hasWallet = !_.isEmpty(userWallet); - const hasActivatedWallet = _.contains([CONST.WALLET.TIER_NAME.GOLD, CONST.WALLET.TIER_NAME.PLATINUM], userWallet.tierName); + const hasActivatedWallet = _.contains([CONST.WALLET.TIER_NAME.GOLD, CONST.WALLET.TIER_NAME.PLATINUM], userWallet?.tierName); const hasAssignedCard = !_.isEmpty(cardList); const shouldShowEmptyState = !hasBankAccount && !hasWallet && !hasAssignedCard; @@ -77,7 +99,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod // In order to prevent a loop, only update state of the spinner if there is a change const showLoadingSpinner = isLoadingPaymentMethods || false; if (showLoadingSpinner !== shouldShowLoadingSpinner) { - setShouldShowLoadingSpinner(isLoadingPaymentMethods && !network.isOffline); + setShouldShowLoadingSpinner(!!isLoadingPaymentMethods && !network.isOffline); } }, [isLoadingPaymentMethods, network.isOffline, shouldShowLoadingSpinner]); @@ -121,8 +143,8 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod formattedSelectedPaymentMethod: { title: '', }, - methodID: null, - selectedPaymentMethodType: null, + methodID: '', + selectedPaymentMethodType: '', }); }, [setPaymentMethod]); @@ -135,7 +157,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod * @param {Boolean} isDefault * @param {String|Number} methodID */ - const paymentMethodPressed = (nativeEvent, accountType, account, isDefault, methodID) => { + const paymentMethodPressed = (nativeEvent?: GestureResponderEvent | KeyboardEvent, accountType?: string, account?: AccountData, isDefault?: boolean, methodID?: string | number) => { if (shouldShowAddPaymentMenu) { setShouldShowAddPaymentMenu(false); return; @@ -145,32 +167,34 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod setShouldShowDefaultDeleteMenu(false); return; } - paymentMethodButtonRef.current = nativeEvent.currentTarget; + paymentMethodButtonRef.current = nativeEvent?.currentTarget as HTMLElement; // The delete/default menu if (accountType) { - let formattedSelectedPaymentMethod; + let formattedSelectedPaymentMethod = { + title: '', + }; if (accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { formattedSelectedPaymentMethod = { - title: account.addressName, - icon: account.icon, + title: account?.addressName ?? '', + icon: account?.icon, description: PaymentUtils.getPaymentMethodDescription(accountType, account), type: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, }; } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { formattedSelectedPaymentMethod = { - title: account.addressName, - icon: account.icon, + title: account?.addressName ?? '', + icon: account?.icon, description: PaymentUtils.getPaymentMethodDescription(accountType, account), type: CONST.PAYMENT_METHODS.DEBIT_CARD, }; } setPaymentMethod({ - isSelectedPaymentMethodDefault: isDefault, - selectedPaymentMethod: account, + isSelectedPaymentMethodDefault: !!isDefault, + selectedPaymentMethod: account || {}, selectedPaymentMethodType: accountType, formattedSelectedPaymentMethod, - methodID, + methodID: methodID ?? '', }); setShouldShowDefaultDeleteMenu(true); setMenuPosition(); @@ -189,10 +213,8 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod /** * Navigate to the appropriate payment type addition screen - * - * @param {String} paymentType */ - const addPaymentMethodTypePressed = (paymentType) => { + const addPaymentMethodTypePressed = (paymentType: string) => { hideAddPaymentMenu(); if (paymentType === CONST.PAYMENT_METHODS.DEBIT_CARD) { @@ -220,14 +242,14 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod const makeDefaultPaymentMethod = useCallback(() => { const paymentCardList = fundList || {}; // Find the previous default payment method so we can revert if the MakeDefaultPaymentMethod command errors - const paymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList, paymentCardList, styles); + const paymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList || {}, paymentCardList, styles); - const previousPaymentMethod = _.find(paymentMethods, (method) => method.isDefault); + const previousPaymentMethod = _.find(paymentMethods, (method) => !!method.isDefault); const currentPaymentMethod = _.find(paymentMethods, (method) => method.methodID === paymentMethod.methodID); if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { - PaymentMethods.makeDefaultPaymentMethod(paymentMethod.selectedPaymentMethod.bankAccountID, null, previousPaymentMethod, currentPaymentMethod); + PaymentMethods.makeDefaultPaymentMethod(paymentMethod.selectedPaymentMethod.bankAccountID || null, null, previousPaymentMethod, currentPaymentMethod); } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.makeDefaultPaymentMethod(null, paymentMethod.selectedPaymentMethod.fundID, previousPaymentMethod, currentPaymentMethod); + PaymentMethods.makeDefaultPaymentMethod(null, paymentMethod.selectedPaymentMethod.fundID || null, previousPaymentMethod, currentPaymentMethod); } }, [ paymentMethod.methodID, @@ -240,19 +262,19 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod ]); const deletePaymentMethod = useCallback(() => { - if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { - BankAccounts.deletePaymentBankAccount(paymentMethod.selectedPaymentMethod.bankAccountID); - } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.deletePaymentCard(paymentMethod.selectedPaymentMethod.fundID); + const bankAccountID = paymentMethod.selectedPaymentMethod.bankAccountID; + const fundID = paymentMethod.selectedPaymentMethod.fundID; + if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && bankAccountID) { + BankAccounts.deletePaymentBankAccount(bankAccountID); + } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD && fundID) { + PaymentMethods.deletePaymentCard(fundID); } }, [paymentMethod.selectedPaymentMethod.bankAccountID, paymentMethod.selectedPaymentMethod.fundID, paymentMethod.selectedPaymentMethodType]); /** * Navigate to the appropriate page after completing the KYC flow, depending on what initiated it - * - * @param {String} source */ - const navigateToWalletOrTransferBalancePage = (source) => { + const navigateToWalletOrTransferBalancePage = (source: string) => { Navigation.navigate(source === CONST.KYC_WALL_SOURCE.ENABLE_WALLET ? ROUTES.SETTINGS_WALLET : ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE); }; @@ -306,9 +328,9 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod // We should reset selected payment method state values and close corresponding modals if the selected payment method is deleted let shouldResetPaymentMethodData = false; - if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && _.isEmpty(bankAccountList[paymentMethod.methodID])) { + if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && _.isEmpty(bankAccountList?.[paymentMethod.methodID])) { shouldResetPaymentMethodData = true; - } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD && _.isEmpty(fundList[paymentMethod.methodID])) { + } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD && _.isEmpty(fundList?.[paymentMethod.methodID])) { shouldResetPaymentMethodData = true; } if (shouldResetPaymentMethodData) { @@ -344,7 +366,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod style={styles.flex1} contentContainerStyle={styles.flex1} onClose={PaymentMethods.clearWalletError} - errors={userWallet.errors} + errors={userWallet?.errors} errorRowStyles={[styles.ph6]} > {hasWallet && ( @@ -363,7 +385,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod ) : ( navigateToWalletOrTransferBalancePage(source)} - onSelectPaymentMethod={(selectedPaymentMethod) => { + onSuccessfulKYC={(_iouPaymentType: string, source: string) => navigateToWalletOrTransferBalancePage(source)} + onSelectPaymentMethod={(selectedPaymentMethod: string) => { if (hasActivatedWallet || selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { return; } @@ -388,7 +410,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod source={hasActivatedWallet ? CONST.KYC_WALL_SOURCE.TRANSFER_BALANCE : CONST.KYC_WALL_SOURCE.ENABLE_WALLET} shouldIncludeDebitCard={hasActivatedWallet} > - {(triggerKYCFlow, buttonRef) => { + {(triggerKYCFlow: (e: GestureResponderEvent | KeyboardEvent | undefined) => void, buttonRef: Ref) => { if (shouldShowLoadingSpinner) { return null; } @@ -463,7 +485,6 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod shouldEnableScroll={false} onPress={paymentMethodPressed} style={styles.mt5} - isAddPaymentMenuActive={shouldShowAddPaymentMenu} actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} @@ -480,7 +501,6 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod shouldShowAddPaymentMethodButton={false} shouldShowEmptyListMessage={false} onPress={paymentMethodPressed} - isAddPaymentMenuActive={shouldShowAddPaymentMenu} actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} @@ -502,7 +522,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod anchorRef={paymentMethodButtonRef} > {!showConfirmDeleteModal && ( - + {isPopoverBottomMount && ( addPaymentMethodTypePressed(method)} + onItemSelected={(method: string) => addPaymentMethodTypePressed(method)} anchorRef={addPaymentMethodAnchorRef} /> ); } -WalletPage.propTypes = propTypes; -WalletPage.defaultProps = defaultProps; WalletPage.displayName = 'WalletPage'; -export default compose( - withNetwork(), - withOnyx({ - cardList: { - key: ONYXKEYS.CARD_LIST, - }, - walletTransfer: { - key: ONYXKEYS.WALLET_TRANSFER, - }, - userWallet: { - key: ONYXKEYS.USER_WALLET, - }, - bankAccountList: { - key: ONYXKEYS.BANK_ACCOUNT_LIST, - }, - fundList: { - key: ONYXKEYS.FUND_LIST, - }, - walletTerms: { - key: ONYXKEYS.WALLET_TERMS, - }, - isLoadingPaymentMethods: { - key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, - }, - }), -)(WalletPage); +export default withOnyx({ + cardList: { + key: ONYXKEYS.CARD_LIST, + }, + walletTransfer: { + key: ONYXKEYS.WALLET_TRANSFER, + }, + userWallet: { + key: ONYXKEYS.USER_WALLET, + }, + bankAccountList: { + key: ONYXKEYS.BANK_ACCOUNT_LIST, + }, + fundList: { + key: ONYXKEYS.FUND_LIST, + }, + walletTerms: { + key: ONYXKEYS.WALLET_TERMS, + }, + isLoadingPaymentMethods: { + key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, + }, +})(WalletPage); diff --git a/src/pages/settings/Wallet/WalletPage/index.native.js b/src/pages/settings/Wallet/WalletPage/index.native.tsx similarity index 100% rename from src/pages/settings/Wallet/WalletPage/index.native.js rename to src/pages/settings/Wallet/WalletPage/index.native.tsx diff --git a/src/pages/settings/Wallet/WalletPage/index.js b/src/pages/settings/Wallet/WalletPage/index.tsx similarity index 100% rename from src/pages/settings/Wallet/WalletPage/index.js rename to src/pages/settings/Wallet/WalletPage/index.tsx diff --git a/src/pages/settings/Wallet/WalletPage/types.ts b/src/pages/settings/Wallet/WalletPage/types.ts new file mode 100644 index 000000000000..731587f86f39 --- /dev/null +++ b/src/pages/settings/Wallet/WalletPage/types.ts @@ -0,0 +1,31 @@ +import {OnyxEntry} from 'react-native-onyx'; +import {BankAccountList, Beta, Card, CardList, Fund, FundList, Network, UserWallet, WalletTerms, WalletTransfer} from '@src/types/onyx'; + +type WalletPageOnyxProps = { + /** Wallet balance transfer props */ + walletTransfer: OnyxEntry; + + /** The user's wallet account */ + userWallet: OnyxEntry; + + /** List of bank accounts */ + bankAccountList: OnyxEntry; + + /** List of user's cards */ + fundList: OnyxEntry; + + /** Information about the user accepting the terms for payments */ + walletTerms: OnyxEntry; + + cardList: OnyxEntry; + + /** Are we loading payment methods? */ + isLoadingPaymentMethods: OnyxEntry; +}; + +type WalletPageProps = WalletPageOnyxProps & { + /** Listen for window resize event on web and desktop. */ + shouldListenForResize?: boolean; +}; + +export type {WalletPageOnyxProps, WalletPageProps}; diff --git a/src/pages/settings/Wallet/WalletPage/walletPagePropTypes.js b/src/pages/settings/Wallet/WalletPage/walletPagePropTypes.js deleted file mode 100644 index 23bdfe99b086..000000000000 --- a/src/pages/settings/Wallet/WalletPage/walletPagePropTypes.js +++ /dev/null @@ -1,52 +0,0 @@ -import PropTypes from 'prop-types'; -import bankAccountPropTypes from '@components/bankAccountPropTypes'; -import cardPropTypes from '@components/cardPropTypes'; -import networkPropTypes from '@components/networkPropTypes'; -import userWalletPropTypes from '@pages/EnablePayments/userWalletPropTypes'; -import walletTermsPropTypes from '@pages/EnablePayments/walletTermsPropTypes'; -import walletTransferPropTypes from '@pages/settings/Wallet/walletTransferPropTypes'; - -const propTypes = { - /** Wallet balance transfer props */ - walletTransfer: walletTransferPropTypes, - - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), - - /** Are we loading payment methods? */ - isLoadingPaymentMethods: PropTypes.bool, - - /** Listen for window resize event on web and desktop. */ - shouldListenForResize: PropTypes.bool, - - /** The user's wallet account */ - userWallet: userWalletPropTypes, - - /** Information about the network */ - network: networkPropTypes.isRequired, - - /** List of bank accounts */ - bankAccountList: PropTypes.objectOf(bankAccountPropTypes), - - /** List of user's cards */ - fundList: PropTypes.objectOf(cardPropTypes), - - /** Information about the user accepting the terms for payments */ - walletTerms: walletTermsPropTypes, -}; - -const defaultProps = { - walletTransfer: { - shouldShowSuccess: false, - }, - betas: [], - isLoadingPaymentMethods: true, - shouldListenForResize: false, - userWallet: {}, - bankAccountList: {}, - cardList: {}, - fundList: null, - walletTerms: {}, -}; - -export {propTypes, defaultProps}; diff --git a/src/types/onyx/AccountData.ts b/src/types/onyx/AccountData.ts index 88511eec6864..124a006ff57c 100644 --- a/src/types/onyx/AccountData.ts +++ b/src/types/onyx/AccountData.ts @@ -51,6 +51,8 @@ type AccountData = { /** Any error message to show */ errors?: OnyxCommon.Errors; + + fundID?: number; }; export default AccountData; diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts index e3b025ff5a2f..68acd88aa120 100644 --- a/src/types/onyx/Card.ts +++ b/src/types/onyx/Card.ts @@ -33,5 +33,7 @@ type TCardDetails = { }; }; +type CardList = Record; + export default Card; -export type {TCardDetails}; +export type {TCardDetails, CardList}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 7bd9c321be5e..d881379f7e05 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -5,6 +5,7 @@ import type BankAccount from './BankAccount'; import type Beta from './Beta'; import type BlockedFromConcierge from './BlockedFromConcierge'; import type Card from './Card'; +import type {CardList} from './Card'; import type Credentials from './Credentials'; import type Currency from './Currency'; import type CustomStatusDraft from './CustomStatusDraft'; @@ -74,6 +75,7 @@ export type { Beta, BlockedFromConcierge, Card, + CardList, Credentials, Currency, CustomStatusDraft, From dba03e1ecc55504615c99ea610e83d0052a0e73b Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 11 Jan 2024 15:19:06 +0700 Subject: [PATCH 007/225] fix type error --- src/components/AddPaymentMethodMenu.js | 12 +- src/components/KYCWall/index.js | 5 +- src/components/Modal/index.android.tsx | 3 +- src/components/Modal/index.ios.tsx | 3 +- src/components/Modal/index.tsx | 3 +- src/components/Modal/types.ts | 74 ++++++------ src/components/Popover/index.tsx | 12 +- src/components/Popover/types.ts | 13 +-- src/components/PopoverProvider/types.ts | 2 +- src/components/PopoverWithoutOverlay/types.ts | 2 +- src/libs/actions/PaymentMethods.ts | 8 +- .../settings/Wallet/PaymentMethodList.tsx | 92 +++++++-------- .../settings/Wallet/WalletEmptyState.tsx | 1 - .../Wallet/WalletPage/CardDetails.tsx | 10 +- .../settings/Wallet/WalletPage/WalletPage.tsx | 107 +++++++++--------- src/pages/settings/Wallet/WalletPage/types.ts | 4 +- 16 files changed, 166 insertions(+), 185 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index a936b0efdfa1..033fb8765b6f 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -1,19 +1,17 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; -import compose from '@libs/compose'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import iouReportPropTypes from '@pages/iouReportPropTypes'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { withOnyx } from 'react-native-onyx'; +import _ from 'underscore'; import * as Expensicons from './Icon/Expensicons'; import PopoverMenu from './PopoverMenu'; import refPropTypes from './refPropTypes'; -import withWindowDimensions from './withWindowDimensions'; const propTypes = { /** Should the component be visible? */ diff --git a/src/components/KYCWall/index.js b/src/components/KYCWall/index.js index 87f1496a494a..e3a0d5e82cdc 100644 --- a/src/components/KYCWall/index.js +++ b/src/components/KYCWall/index.js @@ -8,8 +8,9 @@ function KYCWall({children, ...props}) { // eslint-disable-next-line react/jsx-props-no-spreading {...props} shouldListenForResize - children={children} - /> + > + {children} + ); } diff --git a/src/components/Modal/index.android.tsx b/src/components/Modal/index.android.tsx index 4d7ae128a114..86a1fd272185 100644 --- a/src/components/Modal/index.android.tsx +++ b/src/components/Modal/index.android.tsx @@ -1,6 +1,5 @@ import React from 'react'; import {AppState} from 'react-native'; -import withWindowDimensions from '@components/withWindowDimensions'; import ComposerFocusManager from '@libs/ComposerFocusManager'; import BaseModal from './BaseModal'; import type BaseModalProps from './types'; @@ -28,4 +27,4 @@ function Modal({useNativeDriver = true, ...rest}: BaseModalProps) { } Modal.displayName = 'Modal'; -export default withWindowDimensions(Modal); +export default Modal; diff --git a/src/components/Modal/index.ios.tsx b/src/components/Modal/index.ios.tsx index cbe58a071d7d..b26ba6cd0f89 100644 --- a/src/components/Modal/index.ios.tsx +++ b/src/components/Modal/index.ios.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import withWindowDimensions from '@components/withWindowDimensions'; import BaseModal from './BaseModal'; import type BaseModalProps from './types'; @@ -15,4 +14,4 @@ function Modal({children, ...rest}: BaseModalProps) { } Modal.displayName = 'Modal'; -export default withWindowDimensions(Modal); +export default Modal; diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 56f3c76a8879..71c0fe47ffca 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,5 +1,4 @@ import React, {useState} from 'react'; -import withWindowDimensions from '@components/withWindowDimensions'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import StatusBar from '@libs/StatusBar'; @@ -55,4 +54,4 @@ function Modal({fullscreen = true, onModalHide = () => {}, type, onModalShow = ( } Modal.displayName = 'Modal'; -export default withWindowDimensions(Modal); +export default Modal; diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 0fed37ffea8b..caba945cad30 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,7 +1,6 @@ import type {ViewStyle} from 'react-native'; import type {ModalProps} from 'react-native-modal'; import type {ValueOf} from 'type-fest'; -import type {WindowDimensionsProps} from '@components/withWindowDimensions/types'; import type CONST from '@src/CONST'; type PopoverAnchorPosition = { @@ -11,57 +10,56 @@ type PopoverAnchorPosition = { left?: number; }; -type BaseModalProps = WindowDimensionsProps & - Partial & { - /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ - fullscreen?: boolean; +type BaseModalProps = Partial & { + /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ + fullscreen?: boolean; - /** Should we close modal on outside click */ - shouldCloseOnOutsideClick?: boolean; + /** Should we close modal on outside click */ + shouldCloseOnOutsideClick?: boolean; - /** Should we announce the Modal visibility changes? */ - shouldSetModalVisibility?: boolean; + /** Should we announce the Modal visibility changes? */ + shouldSetModalVisibility?: boolean; - /** Callback method fired when the user requests to close the modal */ - onClose: (ref?: React.RefObject) => void; + /** Callback method fired when the user requests to close the modal */ + onClose: (ref?: React.RefObject) => void; - /** State that determines whether to display the modal or not */ - isVisible: boolean; + /** State that determines whether to display the modal or not */ + isVisible: boolean; - /** Callback method fired when the user requests to submit the modal content. */ - onSubmit?: () => void; + /** Callback method fired when the user requests to submit the modal content. */ + onSubmit?: () => void; - /** Callback method fired when the modal is hidden */ - onModalHide?: () => void; + /** Callback method fired when the modal is hidden */ + onModalHide?: () => void; - /** Callback method fired when the modal is shown */ - onModalShow?: () => void; + /** Callback method fired when the modal is shown */ + onModalShow?: () => void; - /** Style of modal to display */ - type?: ValueOf; + /** Style of modal to display */ + type?: ValueOf; - /** The anchor position of a popover modal. Has no effect on other modal types. */ - popoverAnchorPosition?: PopoverAnchorPosition; + /** The anchor position of a popover modal. Has no effect on other modal types. */ + popoverAnchorPosition?: PopoverAnchorPosition; - outerStyle?: ViewStyle; + outerStyle?: ViewStyle; - /** Whether the modal should go under the system statusbar */ - statusBarTranslucent?: boolean; + /** Whether the modal should go under the system statusbar */ + statusBarTranslucent?: boolean; - /** Whether the modal should avoid the keyboard */ - avoidKeyboard?: boolean; + /** Whether the modal should avoid the keyboard */ + avoidKeyboard?: boolean; - /** Modal container styles */ - innerContainerStyle?: ViewStyle; + /** Modal container styles */ + innerContainerStyle?: ViewStyle; - /** - * Whether the modal should hide its content while animating. On iOS, set to true - * if `useNativeDriver` is also true, to avoid flashes in the UI. - * - * See: https://github.com/react-native-modal/react-native-modal/pull/116 - * */ - hideModalContentWhileAnimating?: boolean; - }; + /** + * Whether the modal should hide its content while animating. On iOS, set to true + * if `useNativeDriver` is also true, to avoid flashes in the UI. + * + * See: https://github.com/react-native-modal/react-native-modal/pull/116 + * */ + hideModalContentWhileAnimating?: boolean; +}; export default BaseModalProps; export type {PopoverAnchorPosition}; diff --git a/src/components/Popover/index.tsx b/src/components/Popover/index.tsx index 762e79fab63c..e1cd18ba4767 100644 --- a/src/components/Popover/index.tsx +++ b/src/components/Popover/index.tsx @@ -3,32 +3,32 @@ import {createPortal} from 'react-dom'; import Modal from '@components/Modal'; import {PopoverContext} from '@components/PopoverProvider'; import PopoverWithoutOverlay from '@components/PopoverWithoutOverlay'; -import withWindowDimensions from '@components/withWindowDimensions'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; -import type {PopoverWithWindowDimensionsProps} from './types'; +import type {PopoverProps} from './types'; /* * This is a convenience wrapper around the Modal component for a responsive Popover. * On small screen widths, it uses BottomDocked modal type, and a Popover type on wide screen widths. */ -function Popover(props: PopoverWithWindowDimensionsProps) { +function Popover(props: PopoverProps) { const { isVisible, onClose, - isSmallScreenWidth, fullscreen, animationInTiming = CONST.ANIMATED_TRANSITION, onLayout, animationOutTiming, disableAnimation = true, - withoutOverlay, + withoutOverlay = false, anchorPosition = {}, anchorRef = () => {}, animationIn = 'fadeIn', animationOut = 'fadeOut', } = props; + const {isSmallScreenWidth} = useWindowDimensions(); const withoutOverlayRef = useRef(null); const {close, popover} = React.useContext(PopoverContext); @@ -106,4 +106,4 @@ function Popover(props: PopoverWithWindowDimensionsProps) { Popover.displayName = 'Popover'; -export default withWindowDimensions(Popover); +export default Popover; diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index 3d1f95822e6a..551756ed706c 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -1,7 +1,6 @@ import type {ValueOf} from 'type-fest'; import type {PopoverAnchorPosition} from '@components/Modal/types'; import type BaseModalProps from '@components/Modal/types'; -import type {WindowDimensionsProps} from '@components/withWindowDimensions/types'; import type CONST from '@src/CONST'; type AnchorAlignment = { @@ -22,16 +21,16 @@ type PopoverProps = BaseModalProps & { anchorPosition?: PopoverAnchorPosition; /** The anchor alignment of the popover */ - anchorAlignment: AnchorAlignment; + anchorAlignment?: AnchorAlignment; /** The anchor ref of the popover */ - anchorRef: React.RefObject; + anchorRef?: React.RefObject; /** Whether disable the animations */ - disableAnimation: boolean; + disableAnimation?: boolean; /** Whether we don't want to show overlay */ - withoutOverlay: boolean; + withoutOverlay?: boolean; /** The dimensions of the popover */ popoverDimensions?: PopoverDimensions; @@ -46,6 +45,4 @@ type PopoverProps = BaseModalProps & { children: React.ReactNode; }; -type PopoverWithWindowDimensionsProps = PopoverProps & WindowDimensionsProps; - -export type {PopoverProps, PopoverWithWindowDimensionsProps, AnchorAlignment}; +export type {PopoverProps, AnchorAlignment}; diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index ffd0087cd5ff..acc01fd74427 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -12,7 +12,7 @@ type PopoverContextValue = { type AnchorRef = { ref: React.RefObject; close: (anchorRef?: React.RefObject) => void; - anchorRef: React.RefObject; + anchorRef?: React.RefObject; onOpenCallback?: () => void; onCloseCallback?: () => void; }; diff --git a/src/components/PopoverWithoutOverlay/types.ts b/src/components/PopoverWithoutOverlay/types.ts index ff4f73fd4114..fd42bf7d8e42 100644 --- a/src/components/PopoverWithoutOverlay/types.ts +++ b/src/components/PopoverWithoutOverlay/types.ts @@ -13,7 +13,7 @@ type PopoverWithoutOverlayProps = ChildrenProps & }; /** The anchor ref of the popover */ - anchorRef: React.RefObject; + anchorRef?: React.RefObject; /** A react-native-animatable animation timing for the modal display animation */ animationInTiming?: number; diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index 6614d3516253..c152a968f030 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -83,7 +83,7 @@ function getMakeDefaultPaymentOnyxData( onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.USER_WALLET, value: { - walletLinkedAccountID: bankAccountID || fundID, + walletLinkedAccountID: bankAccountID ?? fundID, walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, // Only clear the error if this is optimistic data. If this is failure data, we do not want to clear the error that came from the server. errors: null, @@ -93,7 +93,7 @@ function getMakeDefaultPaymentOnyxData( onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.USER_WALLET, value: { - walletLinkedAccountID: bankAccountID || fundID, + walletLinkedAccountID: bankAccountID ?? fundID, walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, }, }, @@ -320,7 +320,7 @@ type PaymentListKey = typeof ONYXKEYS.BANK_ACCOUNT_LIST | typeof ONYXKEYS.FUND_L * @param paymentListKey The onyx key for the provided payment method * @param paymentMethodID */ -function clearDeletePaymentMethodError(paymentListKey: PaymentListKey, paymentMethodID: string) { +function clearDeletePaymentMethodError(paymentListKey: PaymentListKey, paymentMethodID: number) { Onyx.merge(paymentListKey, { [paymentMethodID]: { pendingAction: null, @@ -334,7 +334,7 @@ function clearDeletePaymentMethodError(paymentListKey: PaymentListKey, paymentMe * @param paymentListKey The onyx key for the provided payment method * @param paymentMethodID */ -function clearAddPaymentMethodError(paymentListKey: PaymentListKey, paymentMethodID: string) { +function clearAddPaymentMethodError(paymentListKey: PaymentListKey, paymentMethodID: number) { Onyx.merge(paymentListKey, { [paymentMethodID]: null, }); diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 9cceaa60c10c..e7c6ba37ed9f 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -1,17 +1,7 @@ -import {FlashList} from '@shopify/flash-list'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {ReactElement, Ref, useCallback, useMemo} from 'react'; -import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'; -import {OnyxEntry, withOnyx} from 'react-native-onyx'; -import {TupleToUnion, ValueOf} from 'type-fest'; -import _ from 'underscore'; -import bankAccountPropTypes from '@components/bankAccountPropTypes'; import Button from '@components/Button'; -import cardPropTypes from '@components/cardPropTypes'; import FormAlertWrapper from '@components/FormAlertWrapper'; import getBankIcon from '@components/Icon/BankIcons'; -import {BankName} from '@components/Icon/BankIconsUtils'; +import type { BankName } from '@components/Icon/BankIconsUtils'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -24,17 +14,26 @@ import * as CardUtils from '@libs/CardUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as PaymentUtils from '@libs/PaymentUtils'; -import stylePropTypes from '@styles/stylePropTypes'; -import variables from '@styles/variables'; -import * as PaymentMethods from '@userActions/PaymentMethods'; +import { FlashList } from '@shopify/flash-list'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {AccountData, BankAccountList, Card, CardList, FundList, UserWallet} from '@src/types/onyx'; -import PaymentMethod from '@src/types/onyx/PaymentMethod'; +import type { AccountData, BankAccountList, CardList, FundList } from '@src/types/onyx'; +import type PaymentMethod from '@src/types/onyx/PaymentMethod'; +import type IconAsset from '@src/types/utils/IconAsset'; +import variables from '@styles/variables'; +import * as PaymentMethods from '@userActions/PaymentMethods'; +import type { ReactElement, Ref } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; +import { View } from 'react-native'; +import type { OnyxEntry } from 'react-native-onyx'; +import { withOnyx } from 'react-native-onyx'; +import type { TupleToUnion, ValueOf } from 'type-fest'; +import _ from 'lodash'; +import type { RenderSuggestionMenuItemProps } from '@components/AutoCompleteSuggestions/types'; const FILTER_TYPES = [CONST.PAYMENT_METHODS.DEBIT_CARD, CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, ''] as const; -const ACTION_PAYMENT_METHOD_TYPES = [...Object.values(CONST.PAYMENT_METHODS), ''] as const; type PaymentMethodListOnyxProps = { /** List of bank accounts */ @@ -48,14 +47,11 @@ type PaymentMethodListOnyxProps = { /** Are we loading payment methods? */ isLoadingPaymentMethods: OnyxEntry; - - /** User wallet props */ - userWallet: OnyxEntry; }; type PaymentMethodListProps = PaymentMethodListOnyxProps & { /** Type of active/highlighted payment method */ - actionPaymentMethodType?: TupleToUnion; + actionPaymentMethodType?: string; /** ID of active/highlighted payment method */ activePaymentMethodID?: string | number; @@ -96,7 +92,14 @@ type PaymentMethodListProps = PaymentMethodListOnyxProps & { shouldShowEmptyListMessage?: boolean; /** What to do when a menu item is pressed */ - onPress: (event?: GestureResponderEvent | KeyboardEvent, accountType?: string, accountData?: AccountData, isDefault?: boolean, methodID?: number) => void | Promise; + onPress: ( + event?: GestureResponderEvent | KeyboardEvent, + accountType?: string, + accountData?: AccountData, + icon?: IconAsset, + isDefault?: boolean, + methodID?: number, + ) => void; }; type PaymentMethodItem = PaymentMethod & { @@ -111,7 +114,7 @@ type PaymentMethodItem = PaymentMethod & { function dismissError(item: PaymentMethod) { const isBankAccount = item.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT; const paymentList = isBankAccount ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.FUND_LIST; - const paymentID = isBankAccount ? lodashGet(item, ['accountData', 'bankAccountID'], '') : lodashGet(item, ['accountData', 'fundID'], ''); + const paymentID = isBankAccount ? item.accountData?.bankAccountID ?? '' : item.accountData?.fundID ?? ''; if (!paymentID) { Log.info('Unable to clear payment method error: ', undefined, item); @@ -136,9 +139,8 @@ function shouldShowDefaultBadge(filteredPaymentMethods: PaymentMethod[], isDefau return false; } - const defaultablePaymentMethodCount = _.filter( - filteredPaymentMethods, - (method) => method.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT || method.accountType === CONST.PAYMENT_METHODS.DEBIT_CARD, + const defaultablePaymentMethodCount = filteredPaymentMethods.filter( + (method) => method.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ?? method.accountType === CONST.PAYMENT_METHODS.DEBIT_CARD, ).length; return defaultablePaymentMethodCount > 1; } @@ -148,7 +150,7 @@ function isPaymentMethodActive(actionPaymentMethodType: string, activePaymentMet } function keyExtractor(item: PaymentMethod) { - return item.key || ''; + return item.key ?? ''; } function PaymentMethodList({ @@ -181,13 +183,13 @@ function PaymentMethodList({ if (shouldShowAssignedCards) { const assignedCards = _.chain(cardList) // Filter by physical, active cards associated with a domain - .filter((card) => !card.isVirtual && card.domainName && CONST.EXPENSIFY_CARD.ACTIVE_STATES.includes(card.state ?? 0)) + .filter((card) => !card.isVirtual && !!card.domainName && CONST.EXPENSIFY_CARD.ACTIVE_STATES.includes(card.state ?? 0)) .sortBy((card) => !CardUtils.isExpensifyCard(card.cardID)) .value(); - const numberPhysicalExpensifyCards = _.filter(assignedCards, (card) => CardUtils.isExpensifyCard(card.cardID)).length; + const numberPhysicalExpensifyCards = assignedCards.filter((card) => CardUtils.isExpensifyCard(card.cardID)).length; - return _.map(assignedCards, (card) => { + return assignedCards.map((card) => { const isExpensifyCard = CardUtils.isExpensifyCard(card.cardID); const icon = getBankIcon({bankName: card.bank as BankName, isCard: true, styles}); @@ -202,34 +204,33 @@ function PaymentMethodList({ interactive: isExpensifyCard, canDismissError: isExpensifyCard, errors: card.errors, - brickRoadIndicator: card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL ? 'error' : null, + brickRoadIndicator: card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN ?? card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL ? 'error' : null, ...icon, }; }); } - const paymentCardList = fundList || {}; + const paymentCardList = fundList ?? {}; // Hide any billing cards that are not P2P debit cards for now because you cannot make them your default method, or delete them - const filteredCardList = _.filter(paymentCardList, (card) => !!card.accountData?.additionalData?.isP2PDebitCard); + const filteredCardList = Object.values(paymentCardList).filter((card) => !!card.accountData?.additionalData?.isP2PDebitCard); let combinedPaymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList ?? {}, filteredCardList, styles); if (!_.isEmpty(filterType)) { - combinedPaymentMethods = _.filter(combinedPaymentMethods, (paymentMethod) => paymentMethod.accountType === filterType); + combinedPaymentMethods = combinedPaymentMethods.filter((paymentMethod) => paymentMethod.accountType === filterType); } if (!isOffline) { - combinedPaymentMethods = _.filter( - combinedPaymentMethods, - (paymentMethod) => paymentMethod.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !_.isEmpty(paymentMethod.errors), + combinedPaymentMethods = combinedPaymentMethods.filter( + (paymentMethod) => paymentMethod.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ?? !_.isEmpty(paymentMethod.errors), ); } - combinedPaymentMethods = _.map(combinedPaymentMethods, (paymentMethod) => { + combinedPaymentMethods = combinedPaymentMethods.map((paymentMethod) => { const isMethodActive = isPaymentMethodActive(actionPaymentMethodType, activePaymentMethodID, paymentMethod); return { ...paymentMethod, - onPress: (e: GestureResponderEvent) => onPress(e, paymentMethod.accountType, paymentMethod.accountData, paymentMethod.isDefault, paymentMethod.methodID), + onPress: (e: GestureResponderEvent) => onPress(e, paymentMethod.accountType, paymentMethod.accountData, paymentMethod.icon, paymentMethod.isDefault, paymentMethod.methodID), wrapperStyle: isMethodActive ? [StyleUtils.getButtonBackgroundColorStyle(CONST.BUTTON_STATES.PRESSED)] : null, disabled: paymentMethod.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, }; @@ -240,8 +241,6 @@ function PaymentMethodList({ /** * Render placeholder when there are no payments methods - * - * @return {React.Component} */ const renderListEmptyComponent = () => {translate('paymentMethodList.addFirstPaymentMethod')}; @@ -263,7 +262,7 @@ function PaymentMethodList({ * Create a menuItem for each passed paymentMethod */ const renderItem = useCallback( - ({item}: {item: PaymentMethodItem}) => ( + ({item}: RenderSuggestionMenuItemProps) => ( dismissError(item)} pendingAction={item.pendingAction} @@ -278,8 +277,8 @@ function PaymentMethodList({ icon={item.icon} disabled={item.disabled} displayInDefaultIconColor - iconHeight={item.iconHeight || item.iconSize} - iconWidth={item.iconWidth || item.iconSize} + iconHeight={item.iconHeight ?? item.iconSize} + iconWidth={item.iconWidth ?? item.iconSize} iconStyles={item.iconStyles} badgeText={shouldShowDefaultBadge(filteredPaymentMethods, item.isDefault) ? translate('paymentMethodList.defaultPaymentMethod') : undefined} wrapperStyle={styles.paymentMethod} @@ -317,7 +316,7 @@ function PaymentMethodList({ text={translate('paymentMethodList.addPaymentMethod')} icon={Expensicons.CreditCard} onPress={onPress} - isDisabled={isLoadingPaymentMethods || isFormOffline} + isDisabled={isLoadingPaymentMethods ?? isFormOffline} style={[styles.mh4, styles.buttonCTA]} iconStyles={[styles.buttonCTAIcon]} key="addPaymentMethodButton" @@ -348,7 +347,4 @@ export default withOnyx({ isLoadingPaymentMethods: { key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, }, - userWallet: { - key: ONYXKEYS.USER_WALLET, - }, })(PaymentMethodList); diff --git a/src/pages/settings/Wallet/WalletEmptyState.tsx b/src/pages/settings/Wallet/WalletEmptyState.tsx index 4ef9488368cb..4060a3695b92 100644 --- a/src/pages/settings/Wallet/WalletEmptyState.tsx +++ b/src/pages/settings/Wallet/WalletEmptyState.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import Button from '@components/Button'; import FeatureList from '@components/FeatureList'; diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx index 09a7d937053b..a21172da2832 100644 --- a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx +++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx @@ -1,7 +1,7 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; -import {OnyxEntry, withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import { withOnyx} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayToggle'; @@ -14,7 +14,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {PrivatePersonalDetails} from '@src/types/onyx'; +import type {PrivatePersonalDetails} from '@src/types/onyx'; const defaultPrivatePersonalDetails = { address: { @@ -46,7 +46,7 @@ type CardDetailsProps = CardDetailsOnyxProps & { domain: string; }; -function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetails = defaultPrivatePersonalDetails, domain}: CardDetailsProps) { +function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetails, domain}: CardDetailsProps) { const styles = useThemeStyles(); usePrivatePersonalDetails(); const {translate} = useLocalize(); @@ -87,7 +87,7 @@ function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetail /> { // In order to prevent a loop, only update state of the spinner if there is a change - const showLoadingSpinner = isLoadingPaymentMethods || false; + const showLoadingSpinner = isLoadingPaymentMethods ?? false; if (showLoadingSpinner !== shouldShowLoadingSpinner) { setShouldShowLoadingSpinner(!!isLoadingPaymentMethods && !network.isOffline); } @@ -107,8 +103,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi /** * Set position of the payment menu - * - * @param {Object} position */ const setMenuPosition = useCallback(() => { if (!paymentMethodButtonRef.current) { @@ -150,14 +144,15 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi /** * Display the delete/default menu, or the add payment method menu - * - * @param {Object} nativeEvent - * @param {String} accountType - * @param {String} account - * @param {Boolean} isDefault - * @param {String|Number} methodID */ - const paymentMethodPressed = (nativeEvent?: GestureResponderEvent | KeyboardEvent, accountType?: string, account?: AccountData, isDefault?: boolean, methodID?: string | number) => { + const paymentMethodPressed = ( + nativeEvent?: GestureResponderEvent | KeyboardEvent, + accountType?: string, + account?: AccountData, + icon?: IconAsset, + isDefault?: boolean, + methodID?: string | number, + ) => { if (shouldShowAddPaymentMenu) { setShouldShowAddPaymentMenu(false); return; @@ -171,27 +166,27 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi // The delete/default menu if (accountType) { - let formattedSelectedPaymentMethod = { + let formattedSelectedPaymentMethod: FormattedSelectedPaymentMethod = { title: '', }; if (accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { formattedSelectedPaymentMethod = { title: account?.addressName ?? '', - icon: account?.icon, + icon, description: PaymentUtils.getPaymentMethodDescription(accountType, account), type: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, }; } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { formattedSelectedPaymentMethod = { title: account?.addressName ?? '', - icon: account?.icon, + icon, description: PaymentUtils.getPaymentMethodDescription(accountType, account), type: CONST.PAYMENT_METHODS.DEBIT_CARD, }; } setPaymentMethod({ isSelectedPaymentMethodDefault: !!isDefault, - selectedPaymentMethod: account || {}, + selectedPaymentMethod: account ?? {}, selectedPaymentMethodType: accountType, formattedSelectedPaymentMethod, methodID: methodID ?? '', @@ -222,7 +217,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi return; } - if (paymentType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT || paymentType === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { + if (paymentType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ?? paymentType === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { BankAccounts.openPersonalBankAccountSetupView(); return; } @@ -232,7 +227,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi /** * Hide the default / delete modal - * @param {boolean} shouldClearSelectedData - Clear selected payment method data if true */ const hideDefaultDeleteMenu = useCallback(() => { setShouldShowDefaultDeleteMenu(false); @@ -240,16 +234,16 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi }, [setShouldShowDefaultDeleteMenu, setShowConfirmDeleteModal]); const makeDefaultPaymentMethod = useCallback(() => { - const paymentCardList = fundList || {}; + const paymentCardList = fundList ?? {}; // Find the previous default payment method so we can revert if the MakeDefaultPaymentMethod command errors - const paymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList || {}, paymentCardList, styles); + const paymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList ?? {}, paymentCardList, styles); - const previousPaymentMethod = _.find(paymentMethods, (method) => !!method.isDefault); - const currentPaymentMethod = _.find(paymentMethods, (method) => method.methodID === paymentMethod.methodID); + const previousPaymentMethod = paymentMethods.find((method) => !!method.isDefault); + const currentPaymentMethod = paymentMethods.find((method) => method.methodID === paymentMethod.methodID); if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { - PaymentMethods.makeDefaultPaymentMethod(paymentMethod.selectedPaymentMethod.bankAccountID || null, null, previousPaymentMethod, currentPaymentMethod); + PaymentMethods.makeDefaultPaymentMethod(paymentMethod.selectedPaymentMethod.bankAccountID ?? null, null, previousPaymentMethod, currentPaymentMethod); } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.makeDefaultPaymentMethod(null, paymentMethod.selectedPaymentMethod.fundID || null, previousPaymentMethod, currentPaymentMethod); + PaymentMethods.makeDefaultPaymentMethod(null, paymentMethod.selectedPaymentMethod.fundID ?? null, previousPaymentMethod, currentPaymentMethod); } }, [ paymentMethod.methodID, @@ -346,7 +340,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi !(paymentMethod.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && paymentMethod.selectedPaymentMethod.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS); // Determines whether or not the modal popup is mounted from the bottom of the screen instead of the side mount on Web or Desktop screens - const isPopoverBottomMount = anchorPosition.anchorPositionTop === 0 || isSmallScreenWidth; + const isPopoverBottomMount = anchorPosition.anchorPositionTop === 0 ?? isSmallScreenWidth; const alertTextStyle = [styles.inlineSystemMessage, styles.flexShrink1]; const alertViewStyle = [styles.flexRow, styles.alignItemsCenter, styles.w100, styles.ph5]; @@ -395,9 +389,10 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi )} navigateToWalletOrTransferBalancePage(source)} onSelectPaymentMethod={(selectedPaymentMethod: string) => { - if (hasActivatedWallet || selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { + if (hasActivatedWallet ?? selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { return; } // To allow upgrading to a gold wallet, continue with the KYC flow after adding a bank account @@ -488,7 +483,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} - onListContentSizeChange={shouldShowAddPaymentMenu || shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} + onListContentSizeChange={shouldShowAddPaymentMenu ?? shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} /> ) : null} @@ -504,7 +499,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} - onListContentSizeChange={shouldShowAddPaymentMenu || shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} + onListContentSizeChange={shouldShowAddPaymentMenu ?? shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} shouldEnableScroll={false} style={styles.mt5} /> @@ -525,7 +520,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi {isPopoverBottomMount && ( Date: Thu, 11 Jan 2024 15:24:39 +0700 Subject: [PATCH 008/225] resolve conflict --- src/pages/settings/Wallet/PaymentMethodList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index f318b77f8a0a..71c995363666 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -296,7 +296,7 @@ function PaymentMethodList({ return ( <> - + Date: Thu, 11 Jan 2024 15:29:21 +0700 Subject: [PATCH 009/225] lint --- src/components/AddPaymentMethodMenu.js | 10 ++--- .../settings/Wallet/PaymentMethodList.tsx | 39 ++++++++----------- .../Wallet/WalletPage/CardDetails.tsx | 2 +- .../settings/Wallet/WalletPage/WalletPage.tsx | 28 ++++++------- 4 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index 033fb8765b6f..803b7f2cdabe 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -1,14 +1,14 @@ +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import iouReportPropTypes from '@pages/iouReportPropTypes'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { withOnyx } from 'react-native-onyx'; -import _ from 'underscore'; import * as Expensicons from './Icon/Expensicons'; import PopoverMenu from './PopoverMenu'; import refPropTypes from './refPropTypes'; diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 71c995363666..c8dcee62988e 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -1,7 +1,17 @@ +import {FlashList} from '@shopify/flash-list'; +import _ from 'lodash'; +import type {ReactElement, Ref} from 'react'; +import React, {useCallback, useMemo} from 'react'; +import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import type {TupleToUnion, ValueOf} from 'type-fest'; +import type {RenderSuggestionMenuItemProps} from '@components/AutoCompleteSuggestions/types'; import Button from '@components/Button'; import FormAlertWrapper from '@components/FormAlertWrapper'; import getBankIcon from '@components/Icon/BankIcons'; -import type { BankName } from '@components/Icon/BankIconsUtils'; +import type {BankName} from '@components/Icon/BankIconsUtils'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -14,24 +24,14 @@ import * as CardUtils from '@libs/CardUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as PaymentUtils from '@libs/PaymentUtils'; -import { FlashList } from '@shopify/flash-list'; +import variables from '@styles/variables'; +import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type { AccountData, BankAccountList, CardList, FundList } from '@src/types/onyx'; +import type {AccountData, BankAccountList, CardList, FundList} from '@src/types/onyx'; import type PaymentMethod from '@src/types/onyx/PaymentMethod'; import type IconAsset from '@src/types/utils/IconAsset'; -import variables from '@styles/variables'; -import * as PaymentMethods from '@userActions/PaymentMethods'; -import type { ReactElement, Ref } from 'react'; -import React, { useCallback, useMemo } from 'react'; -import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; -import { View } from 'react-native'; -import type { OnyxEntry } from 'react-native-onyx'; -import { withOnyx } from 'react-native-onyx'; -import type { TupleToUnion, ValueOf } from 'type-fest'; -import _ from 'lodash'; -import type { RenderSuggestionMenuItemProps } from '@components/AutoCompleteSuggestions/types'; const FILTER_TYPES = [CONST.PAYMENT_METHODS.DEBIT_CARD, CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, ''] as const; @@ -92,14 +92,7 @@ type PaymentMethodListProps = PaymentMethodListOnyxProps & { shouldShowEmptyListMessage?: boolean; /** What to do when a menu item is pressed */ - onPress: ( - event?: GestureResponderEvent | KeyboardEvent, - accountType?: string, - accountData?: AccountData, - icon?: IconAsset, - isDefault?: boolean, - methodID?: number, - ) => void; + onPress: (event?: GestureResponderEvent | KeyboardEvent, accountType?: string, accountData?: AccountData, icon?: IconAsset, isDefault?: boolean, methodID?: number) => void; }; type PaymentMethodItem = PaymentMethod & { @@ -296,7 +289,7 @@ function PaymentMethodList({ return ( <> - + { // In order to prevent a loop, only update state of the spinner if there is a change From ec74f831b88083175fa147d3ebb69cea73d4f842 Mon Sep 17 00:00:00 2001 From: Vadym Date: Mon, 15 Jan 2024 02:10:23 +0000 Subject: [PATCH 010/225] chore: migrates reactionPropTypes, BaseReactionList and HeaderReactionList to TS --- ...seReactionList.js => BaseReactionList.tsx} | 53 +++++++++---------- .../report/ReactionList/HeaderReactionList.js | 48 ----------------- .../ReactionList/HeaderReactionList.tsx | 48 +++++++++++++++++ .../report/ReactionList/reactionPropTypes.js | 17 ------ .../report/ReactionList/reactionPropTypes.ts | 13 +++++ 5 files changed, 86 insertions(+), 93 deletions(-) rename src/pages/home/report/ReactionList/{BaseReactionList.js => BaseReactionList.tsx} (69%) delete mode 100644 src/pages/home/report/ReactionList/HeaderReactionList.js create mode 100644 src/pages/home/report/ReactionList/HeaderReactionList.tsx delete mode 100644 src/pages/home/report/ReactionList/reactionPropTypes.js create mode 100644 src/pages/home/report/ReactionList/reactionPropTypes.ts diff --git a/src/pages/home/report/ReactionList/BaseReactionList.js b/src/pages/home/report/ReactionList/BaseReactionList.tsx similarity index 69% rename from src/pages/home/report/ReactionList/BaseReactionList.js rename to src/pages/home/report/ReactionList/BaseReactionList.tsx index 2d881d080c31..82541b28c9c8 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.js +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -1,36 +1,34 @@ /* eslint-disable rulesdir/onyx-props-must-have-default */ import Str from 'expensify-common/lib/str'; -import PropTypes from 'prop-types'; import React from 'react'; -import {FlatList} from 'react-native'; +import {FlatList, type FlatListProps} from 'react-native'; import OptionRow from '@components/OptionRow'; -import participantPropTypes from '@components/participantPropTypes'; -import withWindowDimensions from '@components/withWindowDimensions'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import * as UserUtils from '@libs/UserUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import type {PersonalDetails} from '@src/types/onyx'; import HeaderReactionList from './HeaderReactionList'; -import reactionPropTypes from './reactionPropTypes'; +import type {ReactionListProps} from './reactionPropTypes'; -const propTypes = { +type BaseReactionListProps = ReactionListProps & { /** * Array of personal detail objects */ - users: PropTypes.arrayOf(participantPropTypes).isRequired, + users: PersonalDetails[]; /** * Returns true if the current account has reacted to the report action (with the given skin tone). */ - hasUserReacted: PropTypes.bool, + hasUserReacted: boolean; - ...reactionPropTypes, -}; - -const defaultProps = { - hasUserReacted: false, + /** + * Returns true if the reaction list is visible + */ + isVisible: boolean; }; /** @@ -39,7 +37,7 @@ const defaultProps = { * @param {Number} index * @return {String} */ -const keyExtractor = (item, index) => `${item.login}+${index}`; +const keyExtractor: FlatListProps['keyExtractor'] = (item, index) => `${item.login}+${index}`; /** * This function will be used with FlatList getItemLayout property for optimization purpose that allows skipping @@ -51,14 +49,15 @@ const keyExtractor = (item, index) => `${item.login}+${index}`; * @param {Number} index row index * @returns {Object} */ -const getItemLayout = (_, index) => ({ +const getItemLayout = (_: any, index: number): {length: number; offset: number; index: number} => ({ index, length: variables.listItemHeightNormal, offset: variables.listItemHeightNormal * index, }); -function BaseReactionList(props) { - const styles = useThemeStyles(); +function BaseReactionList(props: BaseReactionListProps) { + const {isSmallScreenWidth} = useWindowDimensions(); + const {hoveredComponentBG, reactionListContainer, reactionListContainerFixedWidth, pv2} = useThemeStyles(); if (!props.isVisible) { return null; } @@ -72,25 +71,25 @@ function BaseReactionList(props) { * @param {Object} params.item * @return {React.Component} */ - const renderItem = ({item}) => ( + const renderItem: FlatListProps['renderItem'] = ({item}) => ( { - props.onClose(); + props.onClose && props.onClose(); Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); }} option={{ - text: Str.removeSMSDomain(item.displayName), + reportID: String(item.accountID), + text: Str.removeSMSDomain(item.displayName || ''), alternateText: Str.removeSMSDomain(item.login || ''), participantsList: [item], icons: [ { id: item.accountID, source: UserUtils.getAvatar(item.avatar, item.accountID), - name: item.login, + name: item.login || '', type: CONST.ICON_TYPE_AVATAR, }, ], @@ -113,15 +112,13 @@ function BaseReactionList(props) { renderItem={renderItem} keyExtractor={keyExtractor} getItemLayout={getItemLayout} - contentContainerStyle={styles.pv2} - style={[styles.reactionListContainer, !props.isSmallScreenWidth && styles.reactionListContainerFixedWidth]} + contentContainerStyle={pv2} + style={[reactionListContainer, !isSmallScreenWidth && reactionListContainerFixedWidth]} /> ); } -BaseReactionList.propTypes = propTypes; -BaseReactionList.defaultProps = defaultProps; BaseReactionList.displayName = 'BaseReactionList'; -export default withWindowDimensions(BaseReactionList); +export default BaseReactionList; diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.js b/src/pages/home/report/ReactionList/HeaderReactionList.js deleted file mode 100644 index 04b124f969a9..000000000000 --- a/src/pages/home/report/ReactionList/HeaderReactionList.js +++ /dev/null @@ -1,48 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import * as EmojiUtils from '@libs/EmojiUtils'; -import reactionPropTypes from './reactionPropTypes'; - -const propTypes = { - ...reactionPropTypes, - ...withLocalizePropTypes, - ...windowDimensionsPropTypes, - - /** - * Returns true if the current account has reacted to the report action (with the given skin tone). - */ - hasUserReacted: PropTypes.bool, -}; - -const defaultProps = { - hasUserReacted: false, -}; - -function HeaderReactionList(props) { - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - return ( - - - - {props.emojiCodes.join('')} - {props.emojiCount} - - {`:${EmojiUtils.getLocalizedEmojiName(props.emojiName, props.preferredLocale)}:`} - - - ); -} - -HeaderReactionList.propTypes = propTypes; -HeaderReactionList.defaultProps = defaultProps; -HeaderReactionList.displayName = 'HeaderReactionList'; - -export default compose(withWindowDimensions, withLocalize)(HeaderReactionList); diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.tsx b/src/pages/home/report/ReactionList/HeaderReactionList.tsx new file mode 100644 index 000000000000..551059b697e6 --- /dev/null +++ b/src/pages/home/report/ReactionList/HeaderReactionList.tsx @@ -0,0 +1,48 @@ +import {View} from 'react-native'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as EmojiUtils from '@libs/EmojiUtils'; +import type {ReactionListProps} from './reactionPropTypes'; + +type HeaderReactionListProps = ReactionListProps & { + /** + * Returns true if the current account has reacted to the report action (with the given skin tone). + */ + hasUserReacted: boolean; +}; + +function HeaderReactionList(props: HeaderReactionListProps) { + const { + flexRow, + justifyContentBetween, + alignItemsCenter, + emojiReactionListHeader, + pt4, + emojiReactionListHeaderBubble, + miniQuickEmojiReactionText, + reactionCounterText, + reactionListHeaderText, + } = useThemeStyles(); + const {getEmojiReactionBubbleStyle, getEmojiReactionBubbleTextStyle, getEmojiReactionCounterTextStyle} = useStyleUtils(); + const {preferredLocale} = useLocalize(); + const {isSmallScreenWidth} = useWindowDimensions(); + + return ( + + + + {props.emojiCodes.join('')} + {props.emojiCount} + + {`:${EmojiUtils.getLocalizedEmojiName(props.emojiName, preferredLocale)}:`} + + + ); +} + +HeaderReactionList.displayName = 'HeaderReactionList'; + +export default HeaderReactionList; diff --git a/src/pages/home/report/ReactionList/reactionPropTypes.js b/src/pages/home/report/ReactionList/reactionPropTypes.js deleted file mode 100644 index 3421af230399..000000000000 --- a/src/pages/home/report/ReactionList/reactionPropTypes.js +++ /dev/null @@ -1,17 +0,0 @@ -import PropTypes from 'prop-types'; - -const propTypes = { - /** Hide the ReactionList modal popover */ - onClose: PropTypes.func, - - /** The emoji codes */ - emojiCodes: PropTypes.arrayOf(PropTypes.string).isRequired, - - /** The name of the emoji */ - emojiName: PropTypes.string.isRequired, - - /** Count of the emoji */ - emojiCount: PropTypes.number.isRequired, -}; - -export default propTypes; diff --git a/src/pages/home/report/ReactionList/reactionPropTypes.ts b/src/pages/home/report/ReactionList/reactionPropTypes.ts new file mode 100644 index 000000000000..d88316754600 --- /dev/null +++ b/src/pages/home/report/ReactionList/reactionPropTypes.ts @@ -0,0 +1,13 @@ +export type ReactionListProps = { + /** Hide the ReactionList modal popover */ + onClose?: () => void; + + /** The emoji codes */ + emojiCodes: string[]; + + /** The name of the emoji */ + emojiName: string; + + /** Count of the emoji */ + emojiCount: number; +}; From cb70663a543eaee30f6f0a0302e740b8f5a70d5f Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 15 Jan 2024 16:51:24 +0700 Subject: [PATCH 011/225] change ?? to || --- src/libs/actions/PaymentMethods.ts | 6 +++-- .../settings/Wallet/PaymentMethodList.tsx | 18 ++++++++++----- .../settings/Wallet/WalletEmptyState.tsx | 3 +++ .../Wallet/WalletPage/CardDetails.tsx | 3 ++- .../settings/Wallet/WalletPage/WalletPage.tsx | 23 +++++++++++-------- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index c152a968f030..24899087c46d 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -83,7 +83,8 @@ function getMakeDefaultPaymentOnyxData( onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.USER_WALLET, value: { - walletLinkedAccountID: bankAccountID ?? fundID, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + walletLinkedAccountID: bankAccountID || fundID, walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, // Only clear the error if this is optimistic data. If this is failure data, we do not want to clear the error that came from the server. errors: null, @@ -93,7 +94,8 @@ function getMakeDefaultPaymentOnyxData( onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.USER_WALLET, value: { - walletLinkedAccountID: bankAccountID ?? fundID, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + walletLinkedAccountID: bankAccountID || fundID, walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, }, }, diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index c8dcee62988e..db04bfb9f183 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -133,7 +133,8 @@ function shouldShowDefaultBadge(filteredPaymentMethods: PaymentMethod[], isDefau } const defaultablePaymentMethodCount = filteredPaymentMethods.filter( - (method) => method.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ?? method.accountType === CONST.PAYMENT_METHODS.DEBIT_CARD, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + (method) => method.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT || method.accountType === CONST.PAYMENT_METHODS.DEBIT_CARD, ).length; return defaultablePaymentMethodCount > 1; } @@ -197,7 +198,8 @@ function PaymentMethodList({ interactive: isExpensifyCard, canDismissError: isExpensifyCard, errors: card.errors, - brickRoadIndicator: card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN ?? card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL ? 'error' : null, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + brickRoadIndicator: card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL ? 'error' : null, ...icon, }; }); @@ -215,7 +217,8 @@ function PaymentMethodList({ if (!isOffline) { combinedPaymentMethods = combinedPaymentMethods.filter( - (paymentMethod) => paymentMethod.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ?? !_.isEmpty(paymentMethod.errors), + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + (paymentMethod) => paymentMethod.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !_.isEmpty(paymentMethod.errors), ); } @@ -270,8 +273,10 @@ function PaymentMethodList({ icon={item.icon} disabled={item.disabled} displayInDefaultIconColor - iconHeight={item.iconHeight ?? item.iconSize} - iconWidth={item.iconWidth ?? item.iconSize} + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + iconHeight={item.iconHeight || item.iconSize} + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + iconWidth={item.iconWidth || item.iconSize} iconStyles={item.iconStyles} badgeText={shouldShowDefaultBadge(filteredPaymentMethods, item.isDefault) ? translate('paymentMethodList.defaultPaymentMethod') : undefined} wrapperStyle={styles.paymentMethod} @@ -309,7 +314,8 @@ function PaymentMethodList({ text={translate('paymentMethodList.addPaymentMethod')} icon={Expensicons.CreditCard} onPress={onPress} - isDisabled={isLoadingPaymentMethods ?? isFormOffline} + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + isDisabled={isLoadingPaymentMethods || isFormOffline} style={[styles.mh4, styles.buttonCTA]} iconStyles={[styles.buttonCTAIcon]} key="addPaymentMethodButton" diff --git a/src/pages/settings/Wallet/WalletEmptyState.tsx b/src/pages/settings/Wallet/WalletEmptyState.tsx index 4060a3695b92..bfec7fa6314b 100644 --- a/src/pages/settings/Wallet/WalletEmptyState.tsx +++ b/src/pages/settings/Wallet/WalletEmptyState.tsx @@ -11,6 +11,7 @@ import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; type WalletEmptyStateProps = { + /** The function that is called when a menu item is pressed */ onAddPaymentMethod: () => void; }; @@ -56,4 +57,6 @@ function WalletEmptyState({onAddPaymentMethod}: WalletEmptyStateProps) { ); } +WalletEmptyState.displayName = 'WalletEmptyState'; + export default WalletEmptyState; diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx index 2732e817ae47..ce2dbc6b2cb5 100644 --- a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx +++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx @@ -87,7 +87,8 @@ function CardDetails({pan = '', expiration = '', cvv = '', privatePersonalDetail /> navigateToWalletOrTransferBalancePage(source)} onSelectPaymentMethod={(selectedPaymentMethod: string) => { - if (hasActivatedWallet ?? selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (hasActivatedWallet || selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { return; } // To allow upgrading to a gold wallet, continue with the KYC flow after adding a bank account @@ -483,7 +486,8 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} - onListContentSizeChange={shouldShowAddPaymentMenu ?? shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + onListContentSizeChange={shouldShowAddPaymentMenu || shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} /> ) : null} @@ -499,7 +503,8 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} - onListContentSizeChange={shouldShowAddPaymentMenu ?? shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + onListContentSizeChange={shouldShowAddPaymentMenu || shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} shouldEnableScroll={false} style={styles.mt5} /> @@ -520,7 +525,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi {isPopoverBottomMount && ( Date: Tue, 16 Jan 2024 03:11:39 +0100 Subject: [PATCH 012/225] chore: migrates BasePopoverReactionList and ReactionList to TS --- ...ionList.js => BasePopoverReactionList.tsx} | 104 +++++++++++++----- .../ReactionList/PopoverReactionList/index.js | 67 ----------- .../PopoverReactionList/index.tsx | 60 ++++++++++ 3 files changed, 136 insertions(+), 95 deletions(-) rename src/pages/home/report/ReactionList/PopoverReactionList/{BasePopoverReactionList.js => BasePopoverReactionList.tsx} (65%) delete mode 100644 src/pages/home/report/ReactionList/PopoverReactionList/index.js create mode 100644 src/pages/home/report/ReactionList/PopoverReactionList/index.tsx diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx similarity index 65% rename from src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js rename to src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx index a06ed18de957..8f532253fc43 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx @@ -1,36 +1,63 @@ import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React from 'react'; import {Dimensions} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import _ from 'underscore'; import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent'; -import EmojiReactionsPropTypes from '@components/Reactions/EmojiReactionsPropTypes'; -import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import withCurrentUserPersonalDetails, {type WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import withLocalize, {type WithLocalizeProps} from '@components/withLocalize'; import compose from '@libs/compose'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import BaseReactionList from '@pages/home/report/ReactionList/BaseReactionList'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {ReportActionReactions} from '@src/types/onyx'; +import {ReportActionReaction} from '@src/types/onyx/ReportActionReactions'; -const propTypes = { - reportActionID: PropTypes.string, - emojiName: PropTypes.string, - emojiReactions: EmojiReactionsPropTypes, +type BasePopoverReactionListOnyxProps = { + /** The reactions for the report action */ + emojiReactions: OnyxEntry; +}; + +type BasePopoverReactionListProps = { + /** The ID of the report action */ + reportActionID: string; + + /** The emoji name */ + emojiName: string; - ...withLocalizePropTypes, + /** The ref of the action */ + ref: React.Ref; }; -const defaultProps = { - reportActionID: '', - emojiName: '', - emojiReactions: {}, +type BasePopoverReactionListWithLocalizeProps = WithLocalizeProps & WithCurrentUserPersonalDetailsProps; + +type BasePopoverReactionListPropsWithLocalWithOnyx = BasePopoverReactionListWithLocalizeProps & BasePopoverReactionListOnyxProps & BasePopoverReactionListProps; +type BasePopoverReactionListState = { + /** Whether the popover is visible */ + isPopoverVisible: boolean; + + /** The horizontal and vertical position (relative to the screen) where the popover will display. */ + popoverAnchorPosition: { + horizontal: number; + vertical: number; + }; + + /** The horizontal and vertical position (relative to the screen) where the cursor is. */ + cursorRelativePosition: { + horizontal: number; + vertical: number; + }; }; -class BasePopoverReactionList extends React.Component { - constructor(props) { +class BasePopoverReactionList extends React.Component { + reactionListAnchor: React.RefObject; + dimensionsEventListener: { + remove: () => void; + } | null; + constructor(props: BasePopoverReactionListPropsWithLocalWithOnyx) { super(props); this.state = { @@ -60,8 +87,9 @@ class BasePopoverReactionList extends React.Component { this.dimensionsEventListener = Dimensions.addEventListener('change', this.measureReactionListPosition); } - shouldComponentUpdate(nextProps, nextState) { + shouldComponentUpdate(nextProps: BasePopoverReactionListPropsWithLocalWithOnyx, nextState: BasePopoverReactionListState) { if (!this.state.isPopoverVisible && !nextState.isPopoverVisible) { + // If the popover is not visible, we don't need to update the component return false; } @@ -81,11 +109,13 @@ class BasePopoverReactionList extends React.Component { componentDidUpdate() { if (!this.state.isPopoverVisible) { + // If the popover is not visible, we don't need to update the component return; } // Hide the list when all reactions are removed - const isEmptyList = !_.some(lodashGet(this.props.emojiReactions, [this.props.emojiName, 'users'])); + const emojiReactions = lodashGet(this.props.emojiReactions, [this.props.emojiName, 'users']); + const isEmptyList = !emojiReactions || !_.some(emojiReactions); if (!isEmptyList) { return; } @@ -94,6 +124,7 @@ class BasePopoverReactionList extends React.Component { } componentWillUnmount() { + // Remove the event listener if (!this.dimensionsEventListener) { return; } @@ -106,11 +137,13 @@ class BasePopoverReactionList extends React.Component { * * @returns {Promise} */ - getReactionListMeasuredLocation() { + getReactionListMeasuredLocation(): Promise<{x: number; y: number}> { return new Promise((resolve) => { - if (this.reactionListAnchor.current) { - this.reactionListAnchor.current.measureInWindow((x, y) => resolve({x, y})); + const reactionListAnchor = this.reactionListAnchor.current as HTMLElement & {measureInWindow: (callback: (x: number, y: number) => void) => void}; + if (reactionListAnchor) { + reactionListAnchor.measureInWindow((x, y) => resolve({x, y})); } else { + // If the anchor is not available, we return 0, 0 resolve({x: 0, y: 0}); } }); @@ -123,8 +156,9 @@ class BasePopoverReactionList extends React.Component { * @param {String} emojiName * @returns {Object} */ - getReactionInformation(selectedReaction, emojiName) { + getReactionInformation(selectedReaction: ReportActionReaction | null | undefined, emojiName: string) { if (!selectedReaction) { + // If there is no reaction, we return default values return { emojiName: '', reactionCount: 0, @@ -152,9 +186,18 @@ class BasePopoverReactionList extends React.Component { * @param {Object} [event] - A press event. * @param {Element} reactionListAnchor - reactionListAnchor */ - showReactionList(event, reactionListAnchor) { + showReactionList( + event: { + nativeEvent: { + pageX: number; + pageY: number; + }; + }, + reactionListAnchor: HTMLElement, + ) { + // We get the cursor coordinates and the reactionListAnchor coordinates to calculate the popover position const nativeEvent = event.nativeEvent || {}; - this.reactionListAnchor.current = reactionListAnchor; + this.reactionListAnchor = {current: reactionListAnchor}; this.getReactionListMeasuredLocation().then(({x, y}) => { this.setState({ cursorRelativePosition: { @@ -175,6 +218,7 @@ class BasePopoverReactionList extends React.Component { */ measureReactionListPosition() { if (!this.state.isPopoverVisible) { + // If the popover is not visible, we don't need to update the component return; } this.getReactionListMeasuredLocation().then(({x, y}) => { @@ -200,7 +244,10 @@ class BasePopoverReactionList extends React.Component { } render() { + // Get the selected reaction const selectedReaction = this.state.isPopoverVisible ? lodashGet(this.props.emojiReactions, [this.props.emojiName]) : null; + + // Get the reaction information const {emojiName, emojiCodes, reactionCount, hasUserReacted, users} = this.getReactionInformation(selectedReaction, this.props.emojiName); return ( @@ -215,9 +262,12 @@ class BasePopoverReactionList extends React.Component { fullscreen withoutOverlay anchorRef={this.reactionListAnchor} + anchorAlignment={{ + horizontal: 'left', + vertical: 'top', + }} > ({ emojiReactions: { key: ({reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, }, diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.js b/src/pages/home/report/ReactionList/PopoverReactionList/index.js deleted file mode 100644 index 9f1e7b3113fc..000000000000 --- a/src/pages/home/report/ReactionList/PopoverReactionList/index.js +++ /dev/null @@ -1,67 +0,0 @@ -import PropTypes from 'prop-types'; -import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; -import BasePopoverReactionList from './BasePopoverReactionList'; - -const propTypes = { - innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), -}; - -const defaultProps = { - innerRef: () => {}, -}; - -function PopoverReactionList(props) { - const innerReactionListRef = useRef(); - const [reactionListReportActionID, setReactionListReportActionID] = useState(''); - const [reactionListEmojiName, setReactionListEmojiName] = useState(''); - - /** - * Show the ReactionList modal popover. - * - * @param {Object} [event] - A press event. - * @param {Element} reactionListAnchor - reactionListAnchor - * @param {String} emojiName - Name of emoji - * @param {String} reportActionID - ID of the report action - */ - const showReactionList = (event, reactionListAnchor, emojiName, reportActionID) => { - setReactionListReportActionID(reportActionID); - setReactionListEmojiName(emojiName); - innerReactionListRef.current.showReactionList(event, reactionListAnchor); - }; - - const hideReactionList = () => { - innerReactionListRef.current.hideReactionList(); - }; - - /** - * Whether PopoverReactionList is active for the Report Action. - * - * @param {Number|String} actionID - * @return {Boolean} - */ - const isActiveReportAction = (actionID) => Boolean(actionID) && reactionListReportActionID === actionID; - - useImperativeHandle(props.innerRef, () => ({showReactionList, hideReactionList, isActiveReportAction})); - - return ( - - ); -} - -PopoverReactionList.propTypes = propTypes; -PopoverReactionList.defaultProps = defaultProps; -PopoverReactionList.displayName = 'PopoverReactionList'; - -export default React.memo( - forwardRef((props, ref) => ( - - )), -); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx new file mode 100644 index 000000000000..e4a7aa42791b --- /dev/null +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx @@ -0,0 +1,60 @@ +import React, {forwardRef, Ref, useImperativeHandle, useRef, useState} from 'react'; +import BasePopoverReactionList from './BasePopoverReactionList'; + +type PopoverReactionListProps = { + innerRef: Ref; +}; + +type InnerReactionListRefType = { + showReactionList: ( + event: { + nativeEvent: { + pageX: number; + pageY: number; + }; + }, + reactionListAnchor: HTMLElement, + ) => void; + hideReactionList: () => void; + isActiveReportAction: (actionID: number | string) => boolean; +}; + +const PopoverReactionList = (props: PopoverReactionListProps) => { + const innerReactionListRef = useRef(null); + const [reactionListReportActionID, setReactionListReportActionID] = useState(''); + const [reactionListEmojiName, setReactionListEmojiName] = useState(''); + + const showReactionList = (event: React.MouseEvent, reactionListAnchor: HTMLElement, emojiName: string, reportActionID: string) => { + setReactionListReportActionID(reportActionID); + setReactionListEmojiName(emojiName); + innerReactionListRef.current?.showReactionList(event, reactionListAnchor); + }; + + const hideReactionList = () => { + innerReactionListRef.current?.hideReactionList(); + }; + + const isActiveReportAction = (actionID: number | string) => Boolean(actionID) && reactionListReportActionID === actionID; + + useImperativeHandle(props.innerRef, () => ({showReactionList, hideReactionList, isActiveReportAction})); + + return ( + + ); +}; + +PopoverReactionList.displayName = 'PopoverReactionList'; + +export default React.memo( + forwardRef((props, ref) => ( + + )), +); From 28dfadc15e19eb23a9c361e336c698c4da4bf586 Mon Sep 17 00:00:00 2001 From: vadymbokatov <138146362+vadymbokatov@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:07:10 +0100 Subject: [PATCH 013/225] Update src/pages/home/report/ReactionList/HeaderReactionList.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Błażej Kustra <46095609+blazejkustra@users.noreply.github.com> --- src/pages/home/report/ReactionList/HeaderReactionList.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.tsx b/src/pages/home/report/ReactionList/HeaderReactionList.tsx index 551059b697e6..869dd95baf1e 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList.tsx +++ b/src/pages/home/report/ReactionList/HeaderReactionList.tsx @@ -8,9 +8,7 @@ import * as EmojiUtils from '@libs/EmojiUtils'; import type {ReactionListProps} from './reactionPropTypes'; type HeaderReactionListProps = ReactionListProps & { - /** - * Returns true if the current account has reacted to the report action (with the given skin tone). - */ + /** Returns true if the current account has reacted to the report action (with the given skin tone). */ hasUserReacted: boolean; }; From 952c5232776b082eecb746119bf0e5b3e399eac3 Mon Sep 17 00:00:00 2001 From: Vadym Date: Tue, 16 Jan 2024 15:14:47 +0100 Subject: [PATCH 014/225] chore: renames the types file and fixes its linting errors --- src/pages/home/report/ReactionList/BaseReactionList.tsx | 2 +- src/pages/home/report/ReactionList/HeaderReactionList.tsx | 2 +- .../report/ReactionList/{reactionPropTypes.ts => types.ts} | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) rename src/pages/home/report/ReactionList/{reactionPropTypes.ts => types.ts} (79%) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index 82541b28c9c8..5d41d2f6656f 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -12,7 +12,7 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {PersonalDetails} from '@src/types/onyx'; import HeaderReactionList from './HeaderReactionList'; -import type {ReactionListProps} from './reactionPropTypes'; +import type {ReactionListProps} from './types'; type BaseReactionListProps = ReactionListProps & { /** diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.tsx b/src/pages/home/report/ReactionList/HeaderReactionList.tsx index 869dd95baf1e..e715f6010fc3 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList.tsx +++ b/src/pages/home/report/ReactionList/HeaderReactionList.tsx @@ -5,7 +5,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as EmojiUtils from '@libs/EmojiUtils'; -import type {ReactionListProps} from './reactionPropTypes'; +import type {ReactionListProps} from './types'; type HeaderReactionListProps = ReactionListProps & { /** Returns true if the current account has reacted to the report action (with the given skin tone). */ diff --git a/src/pages/home/report/ReactionList/reactionPropTypes.ts b/src/pages/home/report/ReactionList/types.ts similarity index 79% rename from src/pages/home/report/ReactionList/reactionPropTypes.ts rename to src/pages/home/report/ReactionList/types.ts index d88316754600..ec03b141080d 100644 --- a/src/pages/home/report/ReactionList/reactionPropTypes.ts +++ b/src/pages/home/report/ReactionList/types.ts @@ -1,4 +1,4 @@ -export type ReactionListProps = { +type ReactionListProps = { /** Hide the ReactionList modal popover */ onClose?: () => void; @@ -11,3 +11,5 @@ export type ReactionListProps = { /** Count of the emoji */ emojiCount: number; }; + +export type {ReactionListProps}; From efe9d72aaa7f9079b0657c66619ebdfbd857b194 Mon Sep 17 00:00:00 2001 From: Vadym Date: Tue, 16 Jan 2024 16:52:08 +0100 Subject: [PATCH 015/225] chore: removes any typed and fixes linting errors on the index file --- .../BasePopoverReactionList.tsx | 2 ++ .../PopoverReactionList/index.tsx | 31 ++++++++----------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx index 8f532253fc43..f2c226d7856a 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx @@ -54,9 +54,11 @@ type BasePopoverReactionListState = { class BasePopoverReactionList extends React.Component { reactionListAnchor: React.RefObject; + dimensionsEventListener: { remove: () => void; } | null; + constructor(props: BasePopoverReactionListPropsWithLocalWithOnyx) { super(props); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx index e4a7aa42791b..8ac89cef6656 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx @@ -1,33 +1,28 @@ -import React, {forwardRef, Ref, useImperativeHandle, useRef, useState} from 'react'; +import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; +import type {ForwardedRef} from 'react'; import BasePopoverReactionList from './BasePopoverReactionList'; type PopoverReactionListProps = { - innerRef: Ref; + ref: ForwardedRef; }; +type ShowReactionList = (event: React.MouseEvent, reactionListAnchor: HTMLElement, emojiName: string, reportActionID: string) => void; + type InnerReactionListRefType = { - showReactionList: ( - event: { - nativeEvent: { - pageX: number; - pageY: number; - }; - }, - reactionListAnchor: HTMLElement, - ) => void; + showReactionList: ShowReactionList; hideReactionList: () => void; isActiveReportAction: (actionID: number | string) => boolean; }; -const PopoverReactionList = (props: PopoverReactionListProps) => { +function PopoverReactionList(props: PopoverReactionListProps) { const innerReactionListRef = useRef(null); const [reactionListReportActionID, setReactionListReportActionID] = useState(''); const [reactionListEmojiName, setReactionListEmojiName] = useState(''); - const showReactionList = (event: React.MouseEvent, reactionListAnchor: HTMLElement, emojiName: string, reportActionID: string) => { + const showReactionList: ShowReactionList = (event, reactionListAnchor, emojiName, reportActionID) => { setReactionListReportActionID(reportActionID); setReactionListEmojiName(emojiName); - innerReactionListRef.current?.showReactionList(event, reactionListAnchor); + innerReactionListRef.current?.showReactionList(event, reactionListAnchor, emojiName, reportActionID); }; const hideReactionList = () => { @@ -36,7 +31,7 @@ const PopoverReactionList = (props: PopoverReactionListProps) => { const isActiveReportAction = (actionID: number | string) => Boolean(actionID) && reactionListReportActionID === actionID; - useImperativeHandle(props.innerRef, () => ({showReactionList, hideReactionList, isActiveReportAction})); + useImperativeHandle(props.ref, () => ({showReactionList, hideReactionList, isActiveReportAction})); return ( { emojiName={reactionListEmojiName} /> ); -}; +} PopoverReactionList.displayName = 'PopoverReactionList'; export default React.memo( - forwardRef((props, ref) => ( + forwardRef((props, ref) => ( )), ); From a303a5e6f55cef79a27cc54eec24d650b4e20bdb Mon Sep 17 00:00:00 2001 From: Vadym Date: Tue, 16 Jan 2024 17:22:07 +0100 Subject: [PATCH 016/225] fix: some linting errors and any types --- .../report/ReactionList/BaseReactionList.tsx | 36 ++++++++++--------- .../BasePopoverReactionList.tsx | 10 +++--- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index 5d41d2f6656f..80cbc834f344 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -1,7 +1,8 @@ /* eslint-disable rulesdir/onyx-props-must-have-default */ import Str from 'expensify-common/lib/str'; import React from 'react'; -import {FlatList, type FlatListProps} from 'react-native'; +import {FlatList} from 'react-native'; +import type {FlatListProps} from 'react-native'; import OptionRow from '@components/OptionRow'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -33,9 +34,9 @@ type BaseReactionListProps = ReactionListProps & { /** * Create a unique key for each action in the FlatList. - * @param {Object} item - * @param {Number} index - * @return {String} + * @param item object + * @param index number + * @return string */ const keyExtractor: FlatListProps['keyExtractor'] = (item, index) => `${item.login}+${index}`; @@ -45,11 +46,11 @@ const keyExtractor: FlatListProps['keyExtractor'] = (item, inde * Generate and return an object with properties length(height of each individual row), * offset(distance of the current row from the top of the FlatList), index(current row index) * - * @param {*} _ FlatList item - * @param {Number} index row index - * @returns {Object} + * @param data FlatList item + * @param index number - row index + * @returns object */ -const getItemLayout = (_: any, index: number): {length: number; offset: number; index: number} => ({ +const getItemLayout = (data: ArrayLike | null | undefined, index: number): {length: number; offset: number; index: number} => ({ index, length: variables.listItemHeightNormal, offset: variables.listItemHeightNormal * index, @@ -67,9 +68,9 @@ function BaseReactionList(props: BaseReactionListProps) { * Items with the code "SPACER" return nothing and are used to fill rows up to 8 * so that the sticky headers function properly * - * @param {Object} params - * @param {Object} params.item - * @return {React.Component} + * @param params object + * @param params.item object + * @return React.Component */ const renderItem: FlatListProps['renderItem'] = ({item}) => ( { - props.onClose && props.onClose(); + if (props.onClose) { + props.onClose(); + } + Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); }} option={{ reportID: String(item.accountID), - text: Str.removeSMSDomain(item.displayName || ''), - alternateText: Str.removeSMSDomain(item.login || ''), + text: Str.removeSMSDomain(item.displayName ?? ''), + alternateText: Str.removeSMSDomain(item.login ?? ''), participantsList: [item], icons: [ { id: item.accountID, source: UserUtils.getAvatar(item.avatar, item.accountID), - name: item.login || '', + name: item.login ?? '', type: CONST.ICON_TYPE_AVATAR, }, ], - keyForList: item.login || String(item.accountID), + keyForList: item.login ?? String(item.accountID), }} /> ); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx index f2c226d7856a..4bee2e8d1d3c 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx @@ -5,8 +5,10 @@ import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import _ from 'underscore'; import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent'; -import withCurrentUserPersonalDetails, {type WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {type WithLocalizeProps} from '@components/withLocalize'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import withLocalize from '@components/withLocalize'; +import type {WithLocalizeProps} from '@components/withLocalize'; import compose from '@libs/compose'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; @@ -14,7 +16,7 @@ import BaseReactionList from '@pages/home/report/ReactionList/BaseReactionList'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ReportActionReactions} from '@src/types/onyx'; -import {ReportActionReaction} from '@src/types/onyx/ReportActionReactions'; +import type {ReportActionReaction} from '@src/types/onyx/ReportActionReactions'; type BasePopoverReactionListOnyxProps = { /** The reactions for the report action */ @@ -29,7 +31,7 @@ type BasePopoverReactionListProps = { emojiName: string; /** The ref of the action */ - ref: React.Ref; + ref: React.Ref; }; type BasePopoverReactionListWithLocalizeProps = WithLocalizeProps & WithCurrentUserPersonalDetailsProps; From bc0a36a17f0080492ff63db5ec49b8a94de6467a Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 17 Jan 2024 17:01:05 +0100 Subject: [PATCH 017/225] ValuePicker migration --- ...electorModal.js => ValueSelectorModal.tsx} | 53 ++------ src/components/ValuePicker/index.js | 119 ------------------ src/components/ValuePicker/index.tsx | 64 ++++++++++ src/components/ValuePicker/types.ts | 58 +++++++++ 4 files changed, 135 insertions(+), 159 deletions(-) rename src/components/ValuePicker/{ValueSelectorModal.js => ValueSelectorModal.tsx} (51%) delete mode 100644 src/components/ValuePicker/index.js create mode 100644 src/components/ValuePicker/index.tsx create mode 100644 src/components/ValuePicker/types.ts diff --git a/src/components/ValuePicker/ValueSelectorModal.js b/src/components/ValuePicker/ValueSelectorModal.tsx similarity index 51% rename from src/components/ValuePicker/ValueSelectorModal.js rename to src/components/ValuePicker/ValueSelectorModal.tsx index e45ba873d8a3..61588b9f8e37 100644 --- a/src/components/ValuePicker/ValueSelectorModal.js +++ b/src/components/ValuePicker/ValueSelectorModal.tsx @@ -1,5 +1,3 @@ -import _ from 'lodash'; -import PropTypes from 'prop-types'; import React, {useEffect, useState} from 'react'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Modal from '@components/Modal'; @@ -7,45 +5,22 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; +import type {ValuePickerItem, ValueSelectorModalProps} from './types'; -const propTypes = { - /** Whether the modal is visible */ - isVisible: PropTypes.bool.isRequired, - - /** Items to pick from */ - items: PropTypes.arrayOf(PropTypes.shape({value: PropTypes.string, label: PropTypes.string})), - - /** The selected item */ - selectedItem: PropTypes.shape({value: PropTypes.string, label: PropTypes.string}), - - /** Label for values */ - label: PropTypes.string, - - /** Function to call when the user selects a item */ - onItemSelected: PropTypes.func, - - /** Function to call when the user closes the modal */ - onClose: PropTypes.func, - - /** Whether to show the toolip text */ - shouldShowTooltips: PropTypes.bool, -}; - -const defaultProps = { - items: [], - selectedItem: {}, - label: '', - onClose: () => {}, - onItemSelected: () => {}, - shouldShowTooltips: true, -}; - -function ValueSelectorModal({items, selectedItem, label, isVisible, onClose, onItemSelected, shouldShowTooltips}) { +function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, onClose, onItemSelected, shouldShowTooltips}: ValueSelectorModalProps) { const styles = useThemeStyles(); - const [sectionsData, setSectionsData] = useState([]); + const [sectionsData, setSectionsData] = useState([]); useEffect(() => { - const itemsData = _.map(items, (item) => ({value: item.value, alternateText: item.description, keyForList: item.value, text: item.label, isSelected: item === selectedItem})); + const itemsData = items.map((item, index) => ({ + value: item?.value, + alternateText: item?.description, + keyForList: item.value ?? '', + text: item?.label ?? '', + isSelected: item === selectedItem, + sectionIndex: 0, + index, + })); setSectionsData(itemsData); }, [items, selectedItem]); @@ -71,7 +46,7 @@ function ValueSelectorModal({items, selectedItem, label, isVisible, onClose, onI @@ -80,8 +55,6 @@ function ValueSelectorModal({items, selectedItem, label, isVisible, onClose, onI ); } -ValueSelectorModal.propTypes = propTypes; -ValueSelectorModal.defaultProps = defaultProps; ValueSelectorModal.displayName = 'ValueSelectorModal'; export default ValueSelectorModal; diff --git a/src/components/ValuePicker/index.js b/src/components/ValuePicker/index.js deleted file mode 100644 index d90529114af4..000000000000 --- a/src/components/ValuePicker/index.js +++ /dev/null @@ -1,119 +0,0 @@ -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React, {useState} from 'react'; -import {View} from 'react-native'; -import FormHelpMessage from '@components/FormHelpMessage'; -import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import refPropTypes from '@components/refPropTypes'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useThemeStyles from '@hooks/useThemeStyles'; -import variables from '@styles/variables'; -import ValueSelectorModal from './ValueSelectorModal'; - -const propTypes = { - /** Form Error description */ - errorText: PropTypes.string, - - /** Item to display */ - value: PropTypes.string, - - /** A placeholder value to display */ - placeholder: PropTypes.string, - - /** Items to pick from */ - items: PropTypes.arrayOf(PropTypes.shape({value: PropTypes.string, label: PropTypes.string})), - - /** Label of picker */ - label: PropTypes.string, - - /** Callback to call when the input changes */ - onInputChange: PropTypes.func, - - /** Text to display under the main menu item */ - furtherDetails: PropTypes.string, - - /** A ref to forward to MenuItemWithTopDescription */ - forwardedRef: refPropTypes, - - /** Whether to show the toolip text */ - shouldShowTooltips: PropTypes.bool, -}; - -const defaultProps = { - value: undefined, - label: undefined, - placeholder: '', - items: {}, - forwardedRef: undefined, - errorText: '', - furtherDetails: undefined, - onInputChange: () => {}, - shouldShowTooltips: true, -}; - -function ValuePicker({value, label, items, placeholder, errorText, onInputChange, furtherDetails, shouldShowTooltips, forwardedRef}) { - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - const [isPickerVisible, setIsPickerVisible] = useState(false); - - const showPickerModal = () => { - setIsPickerVisible(true); - }; - - const hidePickerModal = () => { - setIsPickerVisible(false); - }; - - const updateInput = (item) => { - if (item.value !== value) { - onInputChange(item.value); - } - hidePickerModal(); - }; - - const descStyle = !value || value.length === 0 ? StyleUtils.getFontSizeStyle(variables.fontSizeLabel) : null; - const selectedItem = _.find(items, {value}); - const selectedLabel = selectedItem ? selectedItem.label : ''; - - return ( - - - - - - - - ); -} - -ValuePicker.propTypes = propTypes; -ValuePicker.defaultProps = defaultProps; -ValuePicker.displayName = 'ValuePicker'; - -const ValuePickerWithRef = React.forwardRef((props, ref) => ( - -)); - -ValuePickerWithRef.displayName = 'ValuePickerWithRef'; - -export default ValuePickerWithRef; diff --git a/src/components/ValuePicker/index.tsx b/src/components/ValuePicker/index.tsx new file mode 100644 index 000000000000..1137a3a00d81 --- /dev/null +++ b/src/components/ValuePicker/index.tsx @@ -0,0 +1,64 @@ +import React, {forwardRef, useState} from 'react'; +import type {ForwardedRef} from 'react'; +import {View} from 'react-native'; +import FormHelpMessage from '@components/FormHelpMessage'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; +import type {ValuePickerItem, ValuePickerProps} from './types'; +import ValueSelectorModal from './ValueSelectorModal'; + +function ValuePicker({value, label, items, placeholder = '', errorText = '', onInputChange, furtherDetails, shouldShowTooltips = true}: ValuePickerProps, forwardedRef: ForwardedRef) { + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const [isPickerVisible, setIsPickerVisible] = useState(false); + + const showPickerModal = () => { + setIsPickerVisible(true); + }; + + const hidePickerModal = () => { + setIsPickerVisible(false); + }; + + const updateInput = (item: ValuePickerItem) => { + if (item?.value && item.value !== value) { + onInputChange?.(item.value); + } + hidePickerModal(); + }; + + const descStyle = value?.length === 0 ? StyleUtils.getFontSizeStyle(variables.fontSizeLabel) : null; + const selectedItem = items?.find((item) => item.value === value); + + return ( + + + + + + + + ); +} + +ValuePicker.displayName = 'ValuePicker'; + +export default forwardRef(ValuePicker); diff --git a/src/components/ValuePicker/types.ts b/src/components/ValuePicker/types.ts new file mode 100644 index 000000000000..70bdfbe1f302 --- /dev/null +++ b/src/components/ValuePicker/types.ts @@ -0,0 +1,58 @@ +import type {RadioItem} from '@components/SelectionList/types'; + +type ValuePickerItem = RadioItem & { + value?: string; + label?: string; + description?: string; +}; + +type ValueSelectorModalProps = { + /** Whether the modal is visible */ + isVisible: boolean; + + /** Items to pick from */ + items?: ValuePickerItem[]; + + /** The selected item */ + selectedItem?: ValuePickerItem; + + /** Label for values */ + label?: string; + + /** Function to call when the user selects a item */ + onItemSelected: (item: ValuePickerItem) => void; + + /** Function to call when the user closes the modal */ + onClose: () => void; + + /** Whether to show the toolip text */ + shouldShowTooltips?: boolean; +}; + +type ValuePickerProps = { + /** Item to display */ + value?: string; + + /** Label of picker */ + label?: string; + + /** Items to pick from */ + items?: ValuePickerItem[]; + + /** A placeholder value to display */ + placeholder?: string; + + /** Form Error description */ + errorText?: string; + + /** Callback to call when the input changes */ + onInputChange?: (value: string) => void; + + /** Text to display under the main menu item */ + furtherDetails?: string; + + /** Whether to show the toolip text */ + shouldShowTooltips?: boolean; +}; + +export type {ValuePickerItem, ValueSelectorModalProps, ValuePickerProps}; From 8045ca6f214867395c2d0af3a99fcfcfba4b241e Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 17 Jan 2024 19:17:49 +0100 Subject: [PATCH 018/225] Add optional mark to props --- src/components/ValuePicker/ValueSelectorModal.tsx | 4 ++-- src/components/ValuePicker/types.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ValuePicker/ValueSelectorModal.tsx b/src/components/ValuePicker/ValueSelectorModal.tsx index 61588b9f8e37..a7b5f494aa67 100644 --- a/src/components/ValuePicker/ValueSelectorModal.tsx +++ b/src/components/ValuePicker/ValueSelectorModal.tsx @@ -28,7 +28,7 @@ function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, on onClose?.()} onModalHide={onClose} hideModalContentWhileAnimating useNativeDriver @@ -45,7 +45,7 @@ function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, on /> onItemSelected?.(item)} initiallyFocusedOptionKey={selectedItem?.value} shouldStopPropagation shouldShowTooltips={shouldShowTooltips} diff --git a/src/components/ValuePicker/types.ts b/src/components/ValuePicker/types.ts index 70bdfbe1f302..285299bc0101 100644 --- a/src/components/ValuePicker/types.ts +++ b/src/components/ValuePicker/types.ts @@ -20,10 +20,10 @@ type ValueSelectorModalProps = { label?: string; /** Function to call when the user selects a item */ - onItemSelected: (item: ValuePickerItem) => void; + onItemSelected?: (item: ValuePickerItem) => void; /** Function to call when the user closes the modal */ - onClose: () => void; + onClose?: () => void; /** Whether to show the toolip text */ shouldShowTooltips?: boolean; From 00e958e057588cdb6bff53dc254e324546d21800 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 18 Jan 2024 18:33:45 +0700 Subject: [PATCH 019/225] fix: Pin & Delete request appear on Receipt image when quickly tap on receipt and 3-Dot menu --- src/components/Modal/BaseModal.tsx | 9 +++--- src/components/Modal/types.ts | 3 ++ src/components/Popover/index.tsx | 2 ++ .../PopoverWithoutOverlay/index.tsx | 2 +- src/components/ThreeDotsMenu/index.js | 30 ++++++++++++++++--- src/libs/actions/Modal.ts | 4 +-- src/types/onyx/Modal.ts | 2 ++ 7 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 6e5b4eddae9e..4bec71b5a2dd 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -38,6 +38,7 @@ function BaseModal( onLayout, avoidKeyboard = false, children, + isPopover = false, }: BaseModalProps, ref: React.ForwardedRef, ) { @@ -57,7 +58,7 @@ function BaseModal( */ const hideModal = useCallback( (callHideCallback = true) => { - Modal.willAlertModalBecomeVisible(false); + Modal.willAlertModalBecomeVisible(false, isPopover); if (shouldSetModalVisibility) { Modal.setModalVisibility(false); } @@ -69,14 +70,14 @@ function BaseModal( ComposerFocusManager.setReadyToFocus(); } }, - [shouldSetModalVisibility, onModalHide, fullscreen], + [shouldSetModalVisibility, onModalHide, fullscreen, isPopover], ); useEffect(() => { isVisibleRef.current = isVisible; let removeOnCloseListener: () => void; if (isVisible) { - Modal.willAlertModalBecomeVisible(true); + Modal.willAlertModalBecomeVisible(true, isPopover); // To handle closing any modal already visible when this modal is mounted, i.e. PopoverReportActionContextMenu removeOnCloseListener = Modal.setCloseModal(onClose); } @@ -87,7 +88,7 @@ function BaseModal( } removeOnCloseListener(); }; - }, [isVisible, wasVisible, onClose]); + }, [isVisible, wasVisible, onClose, isPopover]); useEffect( () => () => { diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 0773f0741233..d6fdd4cabea8 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -59,6 +59,9 @@ type BaseModalProps = Partial & { * See: https://github.com/react-native-modal/react-native-modal/pull/116 * */ hideModalContentWhileAnimating?: boolean; + + /** Whether the modal is popover or not */ + isPopover?: boolean; }; export default BaseModalProps; diff --git a/src/components/Popover/index.tsx b/src/components/Popover/index.tsx index 762e79fab63c..246286abdcbc 100644 --- a/src/components/Popover/index.tsx +++ b/src/components/Popover/index.tsx @@ -69,6 +69,7 @@ function Popover(props: PopoverWithWindowDimensionsProps) { onLayout={onLayout} animationIn={animationIn} animationOut={animationOut} + isPopover />, document.body, ); @@ -100,6 +101,7 @@ function Popover(props: PopoverWithWindowDimensionsProps) { onLayout={onLayout} animationIn={animationIn} animationOut={animationOut} + isPopover /> ); } diff --git a/src/components/PopoverWithoutOverlay/index.tsx b/src/components/PopoverWithoutOverlay/index.tsx index 58d022ef9d65..437fb4946f86 100644 --- a/src/components/PopoverWithoutOverlay/index.tsx +++ b/src/components/PopoverWithoutOverlay/index.tsx @@ -59,7 +59,7 @@ function PopoverWithoutOverlay( close(anchorRef); Modal.onModalDidClose(); } - Modal.willAlertModalBecomeVisible(isVisible); + Modal.willAlertModalBecomeVisible(isVisible, true); return () => { if (!removeOnClose) { diff --git a/src/components/ThreeDotsMenu/index.js b/src/components/ThreeDotsMenu/index.js index 150487b2aa57..cc90067a2e70 100644 --- a/src/components/ThreeDotsMenu/index.js +++ b/src/components/ThreeDotsMenu/index.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; -import React, {useRef, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -13,6 +14,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Browser from '@libs/Browser'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ThreeDotsMenuItemPropTypes from './ThreeDotsMenuItemPropTypes'; const propTypes = { @@ -57,6 +59,13 @@ const propTypes = { /** Should we announce the Modal visibility changes? */ shouldSetModalVisibility: PropTypes.bool, + + /** Details about any modals being used */ + modal: PropTypes.shape({ + isVisible: PropTypes.bool, + willAlertModalBecomeVisible: PropTypes.bool, + isPopover: PropTypes.bool, + }), }; const defaultProps = { @@ -72,14 +81,16 @@ const defaultProps = { }, shouldOverlay: false, shouldSetModalVisibility: true, + modal: {}, }; -function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment, shouldOverlay, shouldSetModalVisibility, disabled}) { +function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment, shouldOverlay, shouldSetModalVisibility, disabled, modal}) { const theme = useTheme(); const styles = useThemeStyles(); const [isPopupMenuVisible, setPopupMenuVisible] = useState(false); const buttonRef = useRef(null); const {translate} = useLocalize(); + const isBehindModal = modal.willAlertModalBecomeVisible && !modal.isPopover && !shouldOverlay; const showPopoverMenu = () => { setPopupMenuVisible(true); @@ -89,6 +100,13 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, me setPopupMenuVisible(false); }; + useEffect(() => { + if (!isBehindModal || !isPopupMenuVisible) { + return; + } + hidePopoverMenu(); + }, [isBehindModal, isPopupMenuVisible]); + return ( <> @@ -126,7 +144,7 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, me Date: Fri, 19 Jan 2024 16:02:16 +0700 Subject: [PATCH 020/225] clean code --- src/pages/settings/Wallet/PaymentMethodList.tsx | 1 + src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index db04bfb9f183..48f6e1b671f4 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -79,6 +79,7 @@ type PaymentMethodListProps = PaymentMethodListOnyxProps & { /** Type to filter the payment Method list */ filterType?: TupleToUnion; + /** Whether the add bank account button should be shown on the list */ shouldShowAddBankAccount?: boolean; diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index 0a24e7da9d81..230ad920171d 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -13,6 +13,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import KYCWall from '@components/KYCWall'; +import {Source, TransferMethod} from '@components/KYCWall/types'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Popover from '@components/Popover'; @@ -38,7 +39,6 @@ import ROUTES from '@src/ROUTES'; import type {AccountData} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; import type {WalletPageOnyxProps, WalletPageProps} from './types'; -import { Source, TransferMethod } from '@components/KYCWall/types'; type FormattedSelectedPaymentMethod = { title: string; @@ -408,7 +408,10 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi source={hasActivatedWallet ? CONST.KYC_WALL_SOURCE.TRANSFER_BALANCE : CONST.KYC_WALL_SOURCE.ENABLE_WALLET} shouldIncludeDebitCard={hasActivatedWallet} > - {(triggerKYCFlow: (event: SyntheticEvent, iouPaymentType: TransferMethod) => void, buttonRef: ForwardedRef) => { + {( + triggerKYCFlow: (event: SyntheticEvent, iouPaymentType: TransferMethod) => void, + buttonRef: ForwardedRef, + ) => { if (shouldShowLoadingSpinner) { return null; } From 5f6ee9c528d133f78b5e50fdc2b2c13d9862d045 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 22 Jan 2024 16:26:12 +0700 Subject: [PATCH 021/225] change BaseKYCWall type --- src/components/KYCWall/BaseKYCWall.tsx | 5 +++-- src/components/KYCWall/types.ts | 6 +++--- src/components/Popover/types.ts | 2 +- src/pages/settings/Wallet/PaymentMethodList.tsx | 2 +- src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 10 +++++----- src/types/onyx/WalletTerms.ts | 5 ++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index 04c8397bc33b..15f9b295325e 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -2,6 +2,7 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; import type {SyntheticEvent} from 'react'; import {Dimensions} from 'react-native'; import type {EmitterSubscription, NativeTouchEvent} from 'react-native'; +import type {GestureResponderEvent} from 'react-native-modal'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu'; @@ -146,7 +147,7 @@ function KYCWall({ * */ const continueAction = useCallback( - (event?: SyntheticEvent, iouPaymentType?: TransferMethod) => { + (event?: GestureResponderEvent | KeyboardEvent | SyntheticEvent, iouPaymentType?: TransferMethod) => { const currentSource = walletTerms?.source ?? source; /** @@ -161,7 +162,7 @@ function KYCWall({ } // Use event target as fallback if anchorRef is null for safety - const targetElement = anchorRef.current ?? (event?.nativeEvent.target as HTMLDivElement); + const targetElement = anchorRef.current ?? ((event as SyntheticEvent)?.nativeEvent.target as HTMLDivElement); transferBalanceButtonRef.current = targetElement; diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index 8a654cfc25d8..68374834e254 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -1,5 +1,5 @@ -import type {ForwardedRef, SyntheticEvent} from 'react'; -import type {NativeTouchEvent} from 'react-native'; +import type {ForwardedRef} from 'react'; +import type {GestureResponderEvent} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; @@ -67,7 +67,7 @@ type KYCWallProps = { onSuccessfulKYC: (currentSource?: Source, iouPaymentType?: TransferMethod) => void; /** Children to build the KYC */ - children: (continueAction: (event: SyntheticEvent, method: TransferMethod) => void, anchorRef: ForwardedRef) => void; + children: (continueAction: (event?: GestureResponderEvent | KeyboardEvent, method?: TransferMethod) => void, anchorRef: ForwardedRef) => void; }; export type {AnchorPosition, KYCWallProps, PaymentMethod, TransferMethod, DomRect, Source}; diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index bdc5ae493771..7a8b8d6a7f1f 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -34,7 +34,7 @@ type PopoverProps = BaseModalProps & disableAnimation?: boolean; /** Whether we don't want to show overlay */ - withoutOverlay: boolean; + withoutOverlay?: boolean; /** The dimensions of the popover */ popoverDimensions?: PopoverDimensions; diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 48f6e1b671f4..c1fb1bbd8253 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -128,7 +128,7 @@ function dismissError(item: PaymentMethod) { } } -function shouldShowDefaultBadge(filteredPaymentMethods: PaymentMethod[], isDefault = false) { +function shouldShowDefaultBadge(filteredPaymentMethods: PaymentMethod[], isDefault = false): boolean { if (!isDefault) { return false; } diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index 230ad920171d..d1823beddef4 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -1,7 +1,7 @@ import _ from 'lodash'; -import type {ForwardedRef, Ref, RefObject, SyntheticEvent} from 'react'; +import type {ForwardedRef, RefObject} from 'react'; import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; -import type {GestureResponderEvent, NativeTouchEvent} from 'react-native'; +import type {GestureResponderEvent} from 'react-native'; import {ActivityIndicator, Dimensions, ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu'; @@ -13,7 +13,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import KYCWall from '@components/KYCWall'; -import {Source, TransferMethod} from '@components/KYCWall/types'; +import type {Source, TransferMethod} from '@components/KYCWall/types'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Popover from '@components/Popover'; @@ -409,7 +409,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi shouldIncludeDebitCard={hasActivatedWallet} > {( - triggerKYCFlow: (event: SyntheticEvent, iouPaymentType: TransferMethod) => void, + triggerKYCFlow: (event?: GestureResponderEvent | KeyboardEvent, iouPaymentType?: TransferMethod) => void, buttonRef: ForwardedRef, ) => { if (shouldShowLoadingSpinner) { @@ -522,7 +522,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi top: anchorPosition.anchorPositionTop, right: anchorPosition.anchorPositionRight, }} - anchorRef={paymentMethodButtonRef} + anchorRef={paymentMethodButtonRef as RefObject} > {!showConfirmDeleteModal && ( diff --git a/src/types/onyx/WalletTerms.ts b/src/types/onyx/WalletTerms.ts index f0563310859a..c2653cae0f97 100644 --- a/src/types/onyx/WalletTerms.ts +++ b/src/types/onyx/WalletTerms.ts @@ -1,5 +1,4 @@ -import type {ValueOf} from 'type-fest'; -import type CONST from '@src/CONST'; +import type {Source} from '@components/KYCWall/types'; import type * as OnyxCommon from './OnyxCommon'; type WalletTerms = { @@ -10,7 +9,7 @@ type WalletTerms = { chatReportID?: string; /** The source that triggered the KYC wall */ - source?: ValueOf; + source?: Source; /** Loading state to provide feedback when we are waiting for a request to finish */ isLoading?: boolean; From 0129e58af57e4a81de7fb8eaed1e53543be24a93 Mon Sep 17 00:00:00 2001 From: Vadym Date: Tue, 23 Jan 2024 16:11:22 +0100 Subject: [PATCH 022/225] chore: converts BasePopoverReactionList to functional component --- src/hooks/useBasePopoverReactionList/index.ts | 134 +++++++ src/hooks/useBasePopoverReactionList/types.ts | 65 ++++ src/pages/home/ReportScreenContext.ts | 4 +- .../BasePopoverReactionList.tsx | 334 +++--------------- .../PopoverReactionList/index.tsx | 18 +- 5 files changed, 256 insertions(+), 299 deletions(-) create mode 100644 src/hooks/useBasePopoverReactionList/index.ts create mode 100644 src/hooks/useBasePopoverReactionList/types.ts diff --git a/src/hooks/useBasePopoverReactionList/index.ts b/src/hooks/useBasePopoverReactionList/index.ts new file mode 100644 index 000000000000..cccb8a99b526 --- /dev/null +++ b/src/hooks/useBasePopoverReactionList/index.ts @@ -0,0 +1,134 @@ +import {useEffect, useMemo, useRef, useState} from 'react'; +import type {SyntheticEvent} from 'react'; +import {Dimensions} from 'react-native'; +import * as EmojiUtils from '@libs/EmojiUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; +import type {BasePopoverReactionListHookProps, ReactionListAnchor, ShowReactionList} from './types'; + +export default function useBasePopoverReactionList({emojiName, emojiReactions, accountID, reportActionID, preferredLocale}: BasePopoverReactionListHookProps) { + const [isPopoverVisible, setIsPopoverVisible] = useState(false); + const [cursorRelativePosition, setCursorRelativePosition] = useState({horizontal: 0, vertical: 0}); + const [popoverAnchorPosition, setPopoverAnchorPosition] = useState({horizontal: 0, vertical: 0}); + const reactionListRef = useRef(null); + + // Get the selected reaction + const selectedReaction = useMemo(() => (isPopoverVisible ? emojiReactions?.emojiName : null), [isPopoverVisible, emojiReactions]); + + // custom methods + function getReactionInformation() { + if (!selectedReaction) { + // If there is no reaction, we return default values + return { + emojiName: '', + reactionCount: 0, + emojiCodes: [], + hasUserReacted: false, + users: [], + }; + } + + const {emojiCodes, reactionCount, hasUserReacted, userAccountIDs} = EmojiUtils.getEmojiReactionDetails(emojiName, selectedReaction, accountID); + + const users = PersonalDetailsUtils.getPersonalDetailsByIDs(userAccountIDs, accountID, true); + return { + emojiName, + emojiCodes, + reactionCount, + hasUserReacted, + users, + }; + } + + /** + * Get the BasePopoverReactionList anchor position + * We calculate the achor coordinates from measureInWindow async method + * + * @returns promise + */ + function getReactionListMeasuredLocation(): Promise<{x: number; y: number}> { + return new Promise((resolve) => { + const reactionListAnchor = reactionListRef.current; + if (reactionListAnchor && 'measureInWindow' in reactionListAnchor) { + reactionListAnchor.measureInWindow((x, y) => resolve({x, y})); + } else { + // If the anchor is not available or does not have the measureInWindow method, we return 0, 0 + resolve({x: 0, y: 0}); + } + }); + } + + /** + * Show the ReactionList modal popover. + * + * @param event - Object - A press event. + * @param reactionListAnchor - Element - reactionListAnchor + */ + const showReactionList: ShowReactionList = (event, reactionListAnchor) => { + // We get the cursor coordinates and the reactionListAnchor coordinates to calculate the popover position + const nativeEvent = (event as SyntheticEvent)?.nativeEvent || {}; + reactionListRef.current = reactionListAnchor; + getReactionListMeasuredLocation().then(({x, y}) => { + setCursorRelativePosition({horizontal: nativeEvent.pageX - x, vertical: nativeEvent.pageY - y}); + setPopoverAnchorPosition({ + horizontal: nativeEvent.pageX, + vertical: nativeEvent.pageY, + }); + setIsPopoverVisible(true); + }); + }; + + /** + * Hide the ReactionList modal popover. + */ + function hideReactionList() { + setIsPopoverVisible(false); + } + + useEffect(() => { + const dimensionsEventListener = Dimensions.addEventListener('change', () => { + if (!isPopoverVisible) { + // If the popover is not visible, we don't need to update the component + return; + } + getReactionListMeasuredLocation().then(({x, y}) => { + if (!x || !y) { + return; + } + setPopoverAnchorPosition({ + horizontal: cursorRelativePosition.horizontal + x, + vertical: cursorRelativePosition.vertical + y, + }); + }); + }); + + return () => { + dimensionsEventListener.remove(); + }; + }, [ + isPopoverVisible, + reportActionID, + preferredLocale, + cursorRelativePosition.horizontal, + cursorRelativePosition.vertical, + popoverAnchorPosition.horizontal, + popoverAnchorPosition.vertical, + ]); + + useEffect(() => { + if (!isPopoverVisible) { + // If the popover is not visible, we don't need to update the component + return; + } + + // Hide the list when all reactions are removed + const emojiReactionsList = emojiReactions?.emojiName.users; + const isEmptyList = Array.isArray(emojiReactionsList) && !emojiReactionsList.some((emojiReaction) => emojiReaction); + if (!isEmptyList) { + return; + } + + hideReactionList(); + }); + + return {isPopoverVisible, cursorRelativePosition, popoverAnchorPosition, getReactionInformation, hideReactionList, reactionListRef, showReactionList}; +} diff --git a/src/hooks/useBasePopoverReactionList/types.ts b/src/hooks/useBasePopoverReactionList/types.ts new file mode 100644 index 000000000000..e4ba9263b41b --- /dev/null +++ b/src/hooks/useBasePopoverReactionList/types.ts @@ -0,0 +1,65 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {LocaleContextProps} from '@components/LocaleContextProvider'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import type {ReactionListAnchor, ReactionListEvent} from '@pages/home/ReportScreenContext'; +import type {ReportActionReactions} from '@src/types/onyx'; + +type BasePopoverReactionListOnyxProps = { + /** The reactions for the report action */ + emojiReactions: OnyxEntry; +}; + +type BasePopoverReactionListProps = { + /** The ID of the report action */ + reportActionID: string; + + /** The emoji name */ + emojiName: string; +}; + +type BasePopoverReactionListHookProps = BasePopoverReactionListProps & { + /** The reactions for the report action */ + emojiReactions: OnyxEntry; + + /** The current user's account ID */ + accountID: WithCurrentUserPersonalDetailsProps['currentUserPersonalDetails']['accountID']; + + preferredLocale: LocaleContextProps['preferredLocale']; +}; + +type BasePopoverReactionListPropsWithLocalWithOnyx = WithCurrentUserPersonalDetailsProps & BasePopoverReactionListOnyxProps & BasePopoverReactionListProps; +type BasePopoverReactionListState = { + /** Whether the popover is visible */ + isPopoverVisible: boolean; + + /** The horizontal and vertical position (relative to the screen) where the popover will display. */ + popoverAnchorPosition: { + horizontal: number; + vertical: number; + }; + + /** The horizontal and vertical position (relative to the screen) where the cursor is. */ + cursorRelativePosition: { + horizontal: number; + vertical: number; + }; +}; + +type ShowReactionList = (event: ReactionListEvent | undefined, reactionListAnchor: ReactionListAnchor) => void; + +type InnerReactionListRefType = { + showReactionList: ShowReactionList; + hideReactionList: () => void; + isActiveReportAction: (actionID: number | string) => boolean; +}; + +export type { + BasePopoverReactionListProps, + BasePopoverReactionListHookProps, + BasePopoverReactionListPropsWithLocalWithOnyx, + BasePopoverReactionListState, + BasePopoverReactionListOnyxProps, + ShowReactionList, + ReactionListAnchor, + InnerReactionListRefType, +}; diff --git a/src/pages/home/ReportScreenContext.ts b/src/pages/home/ReportScreenContext.ts index 3b4e574e01a1..e9440ab932d6 100644 --- a/src/pages/home/ReportScreenContext.ts +++ b/src/pages/home/ReportScreenContext.ts @@ -1,10 +1,10 @@ -import type {RefObject} from 'react'; +import type {RefObject, SyntheticEvent} from 'react'; import {createContext} from 'react'; import type {FlatList, GestureResponderEvent, View} from 'react-native'; type ReactionListAnchor = View | HTMLDivElement | null; -type ReactionListEvent = GestureResponderEvent | MouseEvent; +type ReactionListEvent = GestureResponderEvent | MouseEvent | SyntheticEvent; type ReactionListRef = { showReactionList: (event: ReactionListEvent | undefined, reactionListAnchor: ReactionListAnchor, emojiName: string, reportActionID: string) => void; diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx index 4bee2e8d1d3c..3f5fdeb6428f 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx @@ -1,297 +1,61 @@ -import lodashGet from 'lodash/get'; import React from 'react'; -import {Dimensions} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; -import _ from 'underscore'; import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; -import withLocalize from '@components/withLocalize'; -import type {WithLocalizeProps} from '@components/withLocalize'; -import compose from '@libs/compose'; -import * as EmojiUtils from '@libs/EmojiUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; +import useBasePopoverReactionList from '@hooks/useBasePopoverReactionList'; +import type {BasePopoverReactionListOnyxProps, BasePopoverReactionListPropsWithLocalWithOnyx} from '@hooks/useBasePopoverReactionList/types'; +import useLocalize from '@hooks/useLocalize'; import BaseReactionList from '@pages/home/report/ReactionList/BaseReactionList'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportActionReactions} from '@src/types/onyx'; -import type {ReportActionReaction} from '@src/types/onyx/ReportActionReactions'; -type BasePopoverReactionListOnyxProps = { - /** The reactions for the report action */ - emojiReactions: OnyxEntry; -}; - -type BasePopoverReactionListProps = { - /** The ID of the report action */ - reportActionID: string; - - /** The emoji name */ - emojiName: string; - - /** The ref of the action */ - ref: React.Ref; -}; - -type BasePopoverReactionListWithLocalizeProps = WithLocalizeProps & WithCurrentUserPersonalDetailsProps; - -type BasePopoverReactionListPropsWithLocalWithOnyx = BasePopoverReactionListWithLocalizeProps & BasePopoverReactionListOnyxProps & BasePopoverReactionListProps; -type BasePopoverReactionListState = { - /** Whether the popover is visible */ - isPopoverVisible: boolean; - - /** The horizontal and vertical position (relative to the screen) where the popover will display. */ - popoverAnchorPosition: { - horizontal: number; - vertical: number; - }; - - /** The horizontal and vertical position (relative to the screen) where the cursor is. */ - cursorRelativePosition: { - horizontal: number; - vertical: number; - }; -}; - -class BasePopoverReactionList extends React.Component { - reactionListAnchor: React.RefObject; - - dimensionsEventListener: { - remove: () => void; - } | null; - - constructor(props: BasePopoverReactionListPropsWithLocalWithOnyx) { - super(props); - - this.state = { - isPopoverVisible: false, - cursorRelativePosition: { - horizontal: 0, - vertical: 0, - }, - - // The horizontal and vertical position (relative to the screen) where the popover will display. - popoverAnchorPosition: { - horizontal: 0, - vertical: 0, - }, - }; - - this.reactionListAnchor = React.createRef(); - this.showReactionList = this.showReactionList.bind(this); - this.hideReactionList = this.hideReactionList.bind(this); - this.measureReactionListPosition = this.measureReactionListPosition.bind(this); - this.getReactionListMeasuredLocation = this.getReactionListMeasuredLocation.bind(this); - this.getReactionInformation = this.getReactionInformation.bind(this); - this.dimensionsEventListener = null; - } - - componentDidMount() { - this.dimensionsEventListener = Dimensions.addEventListener('change', this.measureReactionListPosition); - } - - shouldComponentUpdate(nextProps: BasePopoverReactionListPropsWithLocalWithOnyx, nextState: BasePopoverReactionListState) { - if (!this.state.isPopoverVisible && !nextState.isPopoverVisible) { - // If the popover is not visible, we don't need to update the component - return false; - } - - const previousLocale = lodashGet(this.props, 'preferredLocale', CONST.LOCALES.DEFAULT); - const nextLocale = lodashGet(nextProps, 'preferredLocale', CONST.LOCALES.DEFAULT); - const prevReaction = lodashGet(this.props.emojiReactions, this.props.emojiName); - const nextReaction = lodashGet(nextProps.emojiReactions, nextProps.emojiName); - - return ( - this.props.reportActionID !== nextProps.reportActionID || - this.props.emojiName !== nextProps.emojiName || - !_.isEqual(prevReaction, nextReaction) || - !_.isEqual(this.state, nextState) || - previousLocale !== nextLocale - ); - } - - componentDidUpdate() { - if (!this.state.isPopoverVisible) { - // If the popover is not visible, we don't need to update the component - return; - } - - // Hide the list when all reactions are removed - const emojiReactions = lodashGet(this.props.emojiReactions, [this.props.emojiName, 'users']); - const isEmptyList = !emojiReactions || !_.some(emojiReactions); - if (!isEmptyList) { - return; - } - - this.hideReactionList(); - } - - componentWillUnmount() { - // Remove the event listener - if (!this.dimensionsEventListener) { - return; - } - this.dimensionsEventListener.remove(); - } - - /** - * Get the BasePopoverReactionList anchor position - * We calculate the achor coordinates from measureInWindow async method - * - * @returns {Promise} - */ - getReactionListMeasuredLocation(): Promise<{x: number; y: number}> { - return new Promise((resolve) => { - const reactionListAnchor = this.reactionListAnchor.current as HTMLElement & {measureInWindow: (callback: (x: number, y: number) => void) => void}; - if (reactionListAnchor) { - reactionListAnchor.measureInWindow((x, y) => resolve({x, y})); - } else { - // If the anchor is not available, we return 0, 0 - resolve({x: 0, y: 0}); - } - }); - } - - /** - * Get the reaction information. - * - * @param {Object} selectedReaction - * @param {String} emojiName - * @returns {Object} - */ - getReactionInformation(selectedReaction: ReportActionReaction | null | undefined, emojiName: string) { - if (!selectedReaction) { - // If there is no reaction, we return default values - return { - emojiName: '', - reactionCount: 0, - emojiCodes: [], - hasUserReacted: false, - users: [], - }; - } - - const {emojiCodes, reactionCount, hasUserReacted, userAccountIDs} = EmojiUtils.getEmojiReactionDetails(emojiName, selectedReaction, this.props.currentUserPersonalDetails.accountID); - - const users = PersonalDetailsUtils.getPersonalDetailsByIDs(userAccountIDs, this.props.currentUserPersonalDetails.accountID, true); - return { - emojiName, - emojiCodes, - reactionCount, - hasUserReacted, - users, - }; - } - - /** - * Show the ReactionList modal popover. - * - * @param {Object} [event] - A press event. - * @param {Element} reactionListAnchor - reactionListAnchor - */ - showReactionList( - event: { - nativeEvent: { - pageX: number; - pageY: number; - }; - }, - reactionListAnchor: HTMLElement, - ) { - // We get the cursor coordinates and the reactionListAnchor coordinates to calculate the popover position - const nativeEvent = event.nativeEvent || {}; - this.reactionListAnchor = {current: reactionListAnchor}; - this.getReactionListMeasuredLocation().then(({x, y}) => { - this.setState({ - cursorRelativePosition: { - horizontal: nativeEvent.pageX - x, - vertical: nativeEvent.pageY - y, - }, - popoverAnchorPosition: { - horizontal: nativeEvent.pageX, - vertical: nativeEvent.pageY, - }, - isPopoverVisible: true, - }); - }); - } - - /** - * This gets called on Dimensions change to find the anchor coordinates for the action BasePopoverReactionList. - */ - measureReactionListPosition() { - if (!this.state.isPopoverVisible) { - // If the popover is not visible, we don't need to update the component - return; - } - this.getReactionListMeasuredLocation().then(({x, y}) => { - if (!x || !y) { - return; - } - this.setState((prev) => ({ - popoverAnchorPosition: { - horizontal: prev.cursorRelativePosition.horizontal + x, - vertical: prev.cursorRelativePosition.vertical + y, - }, - })); - }); - } - - /** - * Hide the ReactionList modal popover. - */ - hideReactionList() { - this.setState({ - isPopoverVisible: false, - }); - } - - render() { - // Get the selected reaction - const selectedReaction = this.state.isPopoverVisible ? lodashGet(this.props.emojiReactions, [this.props.emojiName]) : null; - - // Get the reaction information - const {emojiName, emojiCodes, reactionCount, hasUserReacted, users} = this.getReactionInformation(selectedReaction, this.props.emojiName); - - return ( - - - - ); - } +function BasePopoverReactionList(props: BasePopoverReactionListPropsWithLocalWithOnyx) { + // hooks + const {emojiReactions, emojiName, reportActionID, currentUserPersonalDetails} = props; + const {preferredLocale} = useLocalize(); + const {isPopoverVisible, hideReactionList, popoverAnchorPosition, reactionListRef, getReactionInformation} = useBasePopoverReactionList({ + emojiName, + emojiReactions, + accountID: currentUserPersonalDetails.accountID, + reportActionID, + preferredLocale, + }); + // Get the reaction information + const {emojiCodes, reactionCount, hasUserReacted, users} = getReactionInformation(); + + return ( + + + + ); } -export default compose( - // @ts-ignore TODO: Fix this when the type is fixed - withLocalize, - withCurrentUserPersonalDetails, - withOnyx({ +export default withCurrentUserPersonalDetails( + withOnyx({ emojiReactions: { key: ({reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, }, - }), -)(BasePopoverReactionList); + })(BasePopoverReactionList), +); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx index 8ac89cef6656..610146559786 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx @@ -1,17 +1,11 @@ import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; +import type {InnerReactionListRefType} from '@hooks/useBasePopoverReactionList/types'; +import type {ReactionListRef} from '@pages/home/ReportScreenContext'; import BasePopoverReactionList from './BasePopoverReactionList'; type PopoverReactionListProps = { - ref: ForwardedRef; -}; - -type ShowReactionList = (event: React.MouseEvent, reactionListAnchor: HTMLElement, emojiName: string, reportActionID: string) => void; - -type InnerReactionListRefType = { - showReactionList: ShowReactionList; - hideReactionList: () => void; - isActiveReportAction: (actionID: number | string) => boolean; + ref: ForwardedRef; }; function PopoverReactionList(props: PopoverReactionListProps) { @@ -19,10 +13,10 @@ function PopoverReactionList(props: PopoverReactionListProps) { const [reactionListReportActionID, setReactionListReportActionID] = useState(''); const [reactionListEmojiName, setReactionListEmojiName] = useState(''); - const showReactionList: ShowReactionList = (event, reactionListAnchor, emojiName, reportActionID) => { + const showReactionList: ReactionListRef['showReactionList'] = (event, reactionListAnchor, emojiName, reportActionID) => { setReactionListReportActionID(reportActionID); setReactionListEmojiName(emojiName); - innerReactionListRef.current?.showReactionList(event, reactionListAnchor, emojiName, reportActionID); + innerReactionListRef.current?.showReactionList(event, reactionListAnchor); }; const hideReactionList = () => { @@ -45,7 +39,7 @@ function PopoverReactionList(props: PopoverReactionListProps) { PopoverReactionList.displayName = 'PopoverReactionList'; export default React.memo( - forwardRef((props, ref) => ( + forwardRef((props, ref) => ( Date: Wed, 24 Jan 2024 09:40:09 +0100 Subject: [PATCH 023/225] chore: fixes the linting error regarding the named export --- src/pages/home/report/ReactionList/BaseReactionList.tsx | 2 +- src/pages/home/report/ReactionList/HeaderReactionList.tsx | 2 +- src/pages/home/report/ReactionList/types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index 80cbc834f344..f1002bddcddc 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -13,7 +13,7 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {PersonalDetails} from '@src/types/onyx'; import HeaderReactionList from './HeaderReactionList'; -import type {ReactionListProps} from './types'; +import type ReactionListProps from './types'; type BaseReactionListProps = ReactionListProps & { /** diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.tsx b/src/pages/home/report/ReactionList/HeaderReactionList.tsx index e715f6010fc3..bf6c7c1192a5 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList.tsx +++ b/src/pages/home/report/ReactionList/HeaderReactionList.tsx @@ -5,7 +5,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as EmojiUtils from '@libs/EmojiUtils'; -import type {ReactionListProps} from './types'; +import type ReactionListProps from './types'; type HeaderReactionListProps = ReactionListProps & { /** Returns true if the current account has reacted to the report action (with the given skin tone). */ diff --git a/src/pages/home/report/ReactionList/types.ts b/src/pages/home/report/ReactionList/types.ts index ec03b141080d..eb5dfe1d998e 100644 --- a/src/pages/home/report/ReactionList/types.ts +++ b/src/pages/home/report/ReactionList/types.ts @@ -12,4 +12,4 @@ type ReactionListProps = { emojiCount: number; }; -export type {ReactionListProps}; +export default ReactionListProps; From d700d8db166245a5057f8d935dafa8606badbc35 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 24 Jan 2024 17:13:57 +0700 Subject: [PATCH 024/225] resolve conflict --- src/components/KYCWall/types.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index 2dc12b32ead4..dc038e998694 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -67,11 +67,7 @@ type KYCWallProps = { onSuccessfulKYC: (iouPaymentType?: TransferMethod, currentSource?: Source) => void; /** Children to build the KYC */ -<<<<<<< HEAD - children: (continueAction: (event?: GestureResponderEvent | KeyboardEvent, method?: TransferMethod) => void, anchorRef: ForwardedRef) => void; -======= children: (continueAction: (event: GestureResponderEvent | KeyboardEvent | undefined, method: TransferMethod) => void, anchorRef: ForwardedRef) => void; ->>>>>>> main }; export type {AnchorPosition, KYCWallProps, PaymentMethod, TransferMethod, DomRect, Source}; From 7deab6a55225f700aede985b0edb3532add49664 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 24 Jan 2024 17:41:13 +0700 Subject: [PATCH 025/225] clean code --- src/CONST.ts | 7 ++++--- src/components/KYCWall/types.ts | 2 +- src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 264c10b79921..7e5efb2a07a8 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -37,6 +37,9 @@ const keyInputRightArrow = KeyCommand?.constants?.keyInputRightArrow ?? 'keyInpu // describes if a shortcut key can cause navigation const KEYBOARD_SHORTCUT_NAVIGATION_TYPE = 'NAVIGATION_SHORTCUT'; +// Explicit type annotation is required +const cardActiveStates: number[] = [2, 3, 4, 7] + const CONST = { ANDROID_PACKAGE_NAME, ANIMATED_TRANSITION: 300, @@ -1377,9 +1380,7 @@ const CONST = { CLOSED: 6, STATE_SUSPENDED: 7, }, - get ACTIVE_STATES() { - return [2, 3, 4, 7]; - }, + ACTIVE_STATES: cardActiveStates }, AVATAR_ROW_SIZE: { DEFAULT: 4, diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index dc038e998694..5c4b78edd2b1 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -67,7 +67,7 @@ type KYCWallProps = { onSuccessfulKYC: (iouPaymentType?: TransferMethod, currentSource?: Source) => void; /** Children to build the KYC */ - children: (continueAction: (event: GestureResponderEvent | KeyboardEvent | undefined, method: TransferMethod) => void, anchorRef: ForwardedRef) => void; + children: (continueAction: (event?: GestureResponderEvent | KeyboardEvent | undefined, method?: TransferMethod) => void, anchorRef: ForwardedRef) => void; }; export type {AnchorPosition, KYCWallProps, PaymentMethod, TransferMethod, DomRect, Source}; diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index d1823beddef4..09f847ddf490 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -393,7 +393,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi navigateToWalletOrTransferBalancePage(source)} + onSuccessfulKYC={(_iouPaymentType?: TransferMethod, source?: Source) => navigateToWalletOrTransferBalancePage(source)} onSelectPaymentMethod={(selectedPaymentMethod: string) => { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing if (hasActivatedWallet || selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { From d33d249b09a30ec98242241872de8ac718d24903 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 24 Jan 2024 17:42:34 +0700 Subject: [PATCH 026/225] type error --- src/CONST.ts | 2 +- src/components/KYCWall/BaseKYCWall.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 7e5efb2a07a8..f8ce9689a494 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -38,7 +38,7 @@ const keyInputRightArrow = KeyCommand?.constants?.keyInputRightArrow ?? 'keyInpu const KEYBOARD_SHORTCUT_NAVIGATION_TYPE = 'NAVIGATION_SHORTCUT'; // Explicit type annotation is required -const cardActiveStates: number[] = [2, 3, 4, 7] +const cardActiveStates: number[] = [2, 3, 4, 7]; const CONST = { ANDROID_PACKAGE_NAME, diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index 89cceadc0fb0..b2d979a33fcb 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -243,7 +243,6 @@ function KYCWall({ return ( <> setShouldShowAddPaymentMenu(false)} From ffe26d60a5dc6916dcec7c9f913d87f9c4c988e5 Mon Sep 17 00:00:00 2001 From: vadymbokatov <138146362+vadymbokatov@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:44:48 +0100 Subject: [PATCH 027/225] Update src/pages/home/report/ReactionList/PopoverReactionList/index.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Błażej Kustra <46095609+blazejkustra@users.noreply.github.com> --- .../report/ReactionList/PopoverReactionList/index.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx index 610146559786..4368f5a609ab 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx @@ -38,12 +38,4 @@ function PopoverReactionList(props: PopoverReactionListProps) { PopoverReactionList.displayName = 'PopoverReactionList'; -export default React.memo( - forwardRef((props, ref) => ( - - )), -); +export default React.memo(forwardRef(PopoverReactionList)); From a1a4f17f6c44190a5f63b131e0b76056d87a93e4 Mon Sep 17 00:00:00 2001 From: vadymbokatov <138146362+vadymbokatov@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:45:38 +0100 Subject: [PATCH 028/225] Update src/pages/home/report/ReactionList/BaseReactionList.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Błażej Kustra <46095609+blazejkustra@users.noreply.github.com> --- src/pages/home/report/ReactionList/BaseReactionList.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index f1002bddcddc..bec68ba268ff 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -78,9 +78,7 @@ function BaseReactionList(props: BaseReactionListProps) { style={{maxWidth: variables.mobileResponsiveWidthBreakpoint}} hoverStyle={hoveredComponentBG} onSelectRow={() => { - if (props.onClose) { - props.onClose(); - } + props.onClose?.(); Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); }} From 1059ccfb9be071330b004b744a2e46abb3d7e77d Mon Sep 17 00:00:00 2001 From: vadymbokatov <138146362+vadymbokatov@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:46:05 +0100 Subject: [PATCH 029/225] Update src/pages/home/report/ReactionList/BaseReactionList.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Błażej Kustra <46095609+blazejkustra@users.noreply.github.com> --- src/pages/home/report/ReactionList/BaseReactionList.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index bec68ba268ff..c905630f547c 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -68,9 +68,6 @@ function BaseReactionList(props: BaseReactionListProps) { * Items with the code "SPACER" return nothing and are used to fill rows up to 8 * so that the sticky headers function properly * - * @param params object - * @param params.item object - * @return React.Component */ const renderItem: FlatListProps['renderItem'] = ({item}) => ( Date: Wed, 24 Jan 2024 14:46:53 +0100 Subject: [PATCH 030/225] Update src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Błażej Kustra <46095609+blazejkustra@users.noreply.github.com> --- .../ReactionList/PopoverReactionList/BasePopoverReactionList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx index 3f5fdeb6428f..5ef74a62ec67 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx @@ -9,7 +9,6 @@ import BaseReactionList from '@pages/home/report/ReactionList/BaseReactionList'; import ONYXKEYS from '@src/ONYXKEYS'; function BasePopoverReactionList(props: BasePopoverReactionListPropsWithLocalWithOnyx) { - // hooks const {emojiReactions, emojiName, reportActionID, currentUserPersonalDetails} = props; const {preferredLocale} = useLocalize(); const {isPopoverVisible, hideReactionList, popoverAnchorPosition, reactionListRef, getReactionInformation} = useBasePopoverReactionList({ From ffca03ed05404e85a1cc271cace70016c03d2c57 Mon Sep 17 00:00:00 2001 From: Vadym Date: Wed, 24 Jan 2024 16:22:38 +0100 Subject: [PATCH 031/225] fix: ref object not getting passed as the second arg --- .../report/ReactionList/PopoverReactionList/index.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx index 4368f5a609ab..75b8e080b301 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx @@ -4,11 +4,7 @@ import type {InnerReactionListRefType} from '@hooks/useBasePopoverReactionList/t import type {ReactionListRef} from '@pages/home/ReportScreenContext'; import BasePopoverReactionList from './BasePopoverReactionList'; -type PopoverReactionListProps = { - ref: ForwardedRef; -}; - -function PopoverReactionList(props: PopoverReactionListProps) { +function PopoverReactionList(props: unknown, ref: ForwardedRef) { const innerReactionListRef = useRef(null); const [reactionListReportActionID, setReactionListReportActionID] = useState(''); const [reactionListEmojiName, setReactionListEmojiName] = useState(''); @@ -25,7 +21,7 @@ function PopoverReactionList(props: PopoverReactionListProps) { const isActiveReportAction = (actionID: number | string) => Boolean(actionID) && reactionListReportActionID === actionID; - useImperativeHandle(props.ref, () => ({showReactionList, hideReactionList, isActiveReportAction})); + useImperativeHandle(ref, () => ({showReactionList, hideReactionList, isActiveReportAction})); return ( Date: Wed, 24 Jan 2024 16:25:41 +0100 Subject: [PATCH 032/225] fix: types naming convention --- src/hooks/useBasePopoverReactionList/types.ts | 4 ++-- .../home/report/ReactionList/PopoverReactionList/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hooks/useBasePopoverReactionList/types.ts b/src/hooks/useBasePopoverReactionList/types.ts index e4ba9263b41b..49620ccd9370 100644 --- a/src/hooks/useBasePopoverReactionList/types.ts +++ b/src/hooks/useBasePopoverReactionList/types.ts @@ -47,7 +47,7 @@ type BasePopoverReactionListState = { type ShowReactionList = (event: ReactionListEvent | undefined, reactionListAnchor: ReactionListAnchor) => void; -type InnerReactionListRefType = { +type InnerReactionListRef = { showReactionList: ShowReactionList; hideReactionList: () => void; isActiveReportAction: (actionID: number | string) => boolean; @@ -61,5 +61,5 @@ export type { BasePopoverReactionListOnyxProps, ShowReactionList, ReactionListAnchor, - InnerReactionListRefType, + InnerReactionListRef, }; diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx index 75b8e080b301..e7899c814568 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx @@ -1,11 +1,11 @@ import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; -import type {InnerReactionListRefType} from '@hooks/useBasePopoverReactionList/types'; +import type {InnerReactionListRef} from '@hooks/useBasePopoverReactionList/types'; import type {ReactionListRef} from '@pages/home/ReportScreenContext'; import BasePopoverReactionList from './BasePopoverReactionList'; function PopoverReactionList(props: unknown, ref: ForwardedRef) { - const innerReactionListRef = useRef(null); + const innerReactionListRef = useRef(null); const [reactionListReportActionID, setReactionListReportActionID] = useState(''); const [reactionListEmojiName, setReactionListEmojiName] = useState(''); From cebd4dcc436ec9a1f9f618f29c4f0a84265e6eb5 Mon Sep 17 00:00:00 2001 From: Vadym Date: Wed, 24 Jan 2024 16:37:36 +0100 Subject: [PATCH 033/225] chore: removes unneeded JSDocs --- .../report/ReactionList/BaseReactionList.tsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index c905630f547c..80ffd11e59ba 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -32,24 +32,8 @@ type BaseReactionListProps = ReactionListProps & { isVisible: boolean; }; -/** - * Create a unique key for each action in the FlatList. - * @param item object - * @param index number - * @return string - */ const keyExtractor: FlatListProps['keyExtractor'] = (item, index) => `${item.login}+${index}`; -/** - * This function will be used with FlatList getItemLayout property for optimization purpose that allows skipping - * the measurement of dynamic content if we know the size (height or width) of items ahead of time. - * Generate and return an object with properties length(height of each individual row), - * offset(distance of the current row from the top of the FlatList), index(current row index) - * - * @param data FlatList item - * @param index number - row index - * @returns object - */ const getItemLayout = (data: ArrayLike | null | undefined, index: number): {length: number; offset: number; index: number} => ({ index, length: variables.listItemHeightNormal, From 3f8233674cb94e7a6a92ebdb4c517ec4b29aac95 Mon Sep 17 00:00:00 2001 From: Vadym Date: Wed, 24 Jan 2024 16:51:22 +0100 Subject: [PATCH 034/225] chore: makes isVisible prop optional --- src/pages/home/report/ReactionList/BaseReactionList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index 80ffd11e59ba..a49c4d832f30 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -29,7 +29,7 @@ type BaseReactionListProps = ReactionListProps & { /** * Returns true if the reaction list is visible */ - isVisible: boolean; + isVisible?: boolean; }; const keyExtractor: FlatListProps['keyExtractor'] = (item, index) => `${item.login}+${index}`; From ac5f0889d73ca657b25c097ead4cb5159fad02f9 Mon Sep 17 00:00:00 2001 From: Vadym Date: Wed, 24 Jan 2024 20:09:46 +0100 Subject: [PATCH 035/225] fix: menu ref not getting forwarded and menu not showing up --- src/hooks/useBasePopoverReactionList/index.ts | 12 ++++++------ .../home/report/ReactionList/HeaderReactionList.tsx | 1 + .../PopoverReactionList/BasePopoverReactionList.tsx | 12 ++++++++---- .../ReactionList/PopoverReactionList/index.tsx | 4 ++++ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/hooks/useBasePopoverReactionList/index.ts b/src/hooks/useBasePopoverReactionList/index.ts index cccb8a99b526..64c5f6d8efc0 100644 --- a/src/hooks/useBasePopoverReactionList/index.ts +++ b/src/hooks/useBasePopoverReactionList/index.ts @@ -1,4 +1,4 @@ -import {useEffect, useMemo, useRef, useState} from 'react'; +import {useEffect, useRef, useState} from 'react'; import type {SyntheticEvent} from 'react'; import {Dimensions} from 'react-native'; import * as EmojiUtils from '@libs/EmojiUtils'; @@ -11,11 +11,10 @@ export default function useBasePopoverReactionList({emojiName, emojiReactions, a const [popoverAnchorPosition, setPopoverAnchorPosition] = useState({horizontal: 0, vertical: 0}); const reactionListRef = useRef(null); - // Get the selected reaction - const selectedReaction = useMemo(() => (isPopoverVisible ? emojiReactions?.emojiName : null), [isPopoverVisible, emojiReactions]); - // custom methods function getReactionInformation() { + const selectedReaction = emojiReactions?.[emojiName]; + if (!selectedReaction) { // If there is no reaction, we return default values return { @@ -121,8 +120,9 @@ export default function useBasePopoverReactionList({emojiName, emojiReactions, a } // Hide the list when all reactions are removed - const emojiReactionsList = emojiReactions?.emojiName.users; - const isEmptyList = Array.isArray(emojiReactionsList) && !emojiReactionsList.some((emojiReaction) => emojiReaction); + const users = emojiReactions?.[emojiName].users; + const isEmptyList = users && Object.keys(users).length === 0; + if (!isEmptyList) { return; } diff --git a/src/pages/home/report/ReactionList/HeaderReactionList.tsx b/src/pages/home/report/ReactionList/HeaderReactionList.tsx index bf6c7c1192a5..5742566e115c 100644 --- a/src/pages/home/report/ReactionList/HeaderReactionList.tsx +++ b/src/pages/home/report/ReactionList/HeaderReactionList.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import {View} from 'react-native'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx index 5ef74a62ec67..6a464c46cce8 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, {forwardRef, useImperativeHandle} from 'react'; +import type {ForwardedRef} from 'react'; import {withOnyx} from 'react-native-onyx'; import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; @@ -6,12 +7,13 @@ import useBasePopoverReactionList from '@hooks/useBasePopoverReactionList'; import type {BasePopoverReactionListOnyxProps, BasePopoverReactionListPropsWithLocalWithOnyx} from '@hooks/useBasePopoverReactionList/types'; import useLocalize from '@hooks/useLocalize'; import BaseReactionList from '@pages/home/report/ReactionList/BaseReactionList'; +import type {ReactionListRef} from '@pages/home/ReportScreenContext'; import ONYXKEYS from '@src/ONYXKEYS'; -function BasePopoverReactionList(props: BasePopoverReactionListPropsWithLocalWithOnyx) { +function BasePopoverReactionList(props: BasePopoverReactionListPropsWithLocalWithOnyx, ref: ForwardedRef>) { const {emojiReactions, emojiName, reportActionID, currentUserPersonalDetails} = props; const {preferredLocale} = useLocalize(); - const {isPopoverVisible, hideReactionList, popoverAnchorPosition, reactionListRef, getReactionInformation} = useBasePopoverReactionList({ + const {isPopoverVisible, hideReactionList, showReactionList, popoverAnchorPosition, reactionListRef, getReactionInformation} = useBasePopoverReactionList({ emojiName, emojiReactions, accountID: currentUserPersonalDetails.accountID, @@ -21,6 +23,8 @@ function BasePopoverReactionList(props: BasePopoverReactionListPropsWithLocalWit // Get the reaction information const {emojiCodes, reactionCount, hasUserReacted, users} = getReactionInformation(); + useImperativeHandle(ref, () => ({hideReactionList, showReactionList})); + return ( `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, }, - })(BasePopoverReactionList), + })(forwardRef(BasePopoverReactionList)), ); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx index e7899c814568..2cdcac65feae 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.tsx @@ -23,6 +23,10 @@ function PopoverReactionList(props: unknown, ref: ForwardedRef) useImperativeHandle(ref, () => ({showReactionList, hideReactionList, isActiveReportAction})); + if (reactionListReportActionID === '' || reactionListEmojiName === '') { + return null; + } + return ( Date: Thu, 25 Jan 2024 15:37:52 +0700 Subject: [PATCH 036/225] lint fix --- src/CONST.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index d3f712c77e1f..d2422088d1fb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1387,7 +1387,7 @@ const CONST = { CLOSED: 6, STATE_SUSPENDED: 7, }, - ACTIVE_STATES: cardActiveStates + ACTIVE_STATES: cardActiveStates, }, AVATAR_ROW_SIZE: { DEFAULT: 4, From 7e80328234b81dbea037a970ebee6f53558c76e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Thu, 25 Jan 2024 18:31:47 +0100 Subject: [PATCH 037/225] da refactor --- src/components/CategoryPicker/index.js | 50 ++++++++++++-------------- src/libs/OptionsListUtils.js | 2 ++ 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index a957e31a9de4..ca64c1cc444a 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -1,22 +1,18 @@ import lodashGet from 'lodash/get'; -import React, {useMemo, useState} from 'react'; +import React, {useMemo} from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import OptionsSelector from '@components/OptionsSelector'; +import SelectionList from '@components/SelectionList'; +import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {defaultProps, propTypes} from './categoryPickerPropTypes'; function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedCategories, onSubmit}) { - const styles = useThemeStyles(); const {translate} = useLocalize(); - const [searchValue, setSearchValue] = useState(''); - - const policyCategoriesCount = OptionsListUtils.getEnabledCategoriesCount(_.values(policyCategories)); - const isCategoriesCountBelowThreshold = policyCategoriesCount < CONST.CATEGORY_LIST_THRESHOLD; + const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const selectedOptions = useMemo(() => { if (!selectedCategory) { @@ -28,17 +24,18 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC name: selectedCategory, enabled: true, accountID: null, + isSelected: true, }, ]; }, [selectedCategory]); - const sections = useMemo(() => { + const [sections, headerMessage, policyCategoriesCount, shouldShowTextInput] = useMemo(() => { const validPolicyRecentlyUsedCategories = _.filter(policyRecentlyUsedCategories, (p) => !_.isEmpty(p)); const {categoryOptions} = OptionsListUtils.getFilteredOptions( {}, {}, [], - searchValue, + debouncedSearchValue, selectedOptions, [], false, @@ -49,31 +46,28 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC false, ); - return categoryOptions; - }, [policyCategories, policyRecentlyUsedCategories, searchValue, selectedOptions]); + const header = OptionsListUtils.getHeaderMessageForNonUserList(lodashGet(categoryOptions, '[0].data.length', 0) > 0, debouncedSearchValue); + const policiesCount = OptionsListUtils.getEnabledCategoriesCount(_.values(policyCategories)); + const isCategoriesCountBelowThreshold = policyCategoriesCount < CONST.CATEGORY_LIST_THRESHOLD; + const showInput = !isCategoriesCountBelowThreshold; + + return [categoryOptions, header, policiesCount, showInput]; + }, [policyCategories, policyRecentlyUsedCategories, debouncedSearchValue, selectedOptions]); - const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList(lodashGet(sections, '[0].data.length', 0) > 0, searchValue); - const shouldShowTextInput = !isCategoriesCountBelowThreshold; - const selectedOptionKey = lodashGet(_.filter(lodashGet(sections, '[0].data', []), (category) => category.searchText === selectedCategory)[0], 'keyForList'); + const selectedOptionKey = useMemo( + () => lodashGet(_.filter(lodashGet(sections, '[0].data', []), (category) => category.searchText === selectedCategory)[0], 'keyForList'), + [sections, selectedCategory], + ); return ( - ); } diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index d44df3c6c39c..fbaeeb8f5bc4 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -805,6 +805,7 @@ function getCategoryOptionTree(options, isOneLine = false) { searchText: option.name, tooltipText: option.name, isDisabled: !option.enabled, + isSelected: !!option.isSelected, }); return; @@ -825,6 +826,7 @@ function getCategoryOptionTree(options, isOneLine = false) { searchText, tooltipText: optionName, isDisabled: isChild ? !option.enabled : true, + isSelected: !!option.isSelected, }); }); }); From b64181c38ee322c3a0912d7e5d375bd6eb648c69 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Fri, 26 Jan 2024 13:09:00 +0100 Subject: [PATCH 038/225] review fixes --- src/components/SelectionList/BaseListItem.tsx | 4 +- .../SelectionList/BaseSelectionList.tsx | 4 +- .../SelectionList/RadioListItem.tsx | 4 +- src/components/SelectionList/UserListItem.tsx | 4 +- .../SelectionList/index.android.tsx | 4 +- src/components/SelectionList/index.ios.tsx | 4 +- src/components/SelectionList/index.tsx | 4 +- src/components/SelectionList/types.ts | 50 ++++--------------- .../ValuePicker/ValueSelectorModal.tsx | 8 ++- src/components/ValuePicker/index.tsx | 2 +- src/components/ValuePicker/types.ts | 6 +-- 11 files changed, 31 insertions(+), 63 deletions(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index 71845931ba52..3ee59fb5a579 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -11,10 +11,10 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import RadioListItem from './RadioListItem'; -import type {BaseListItemProps, RadioItem, User} from './types'; +import type {BaseListItemProps, ListItem} from './types'; import UserListItem from './UserListItem'; -function BaseListItem({ +function BaseListItem({ item, isFocused = false, isDisabled = false, diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index d97c47c84ee7..2143f76d351f 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -22,9 +22,9 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import BaseListItem from './BaseListItem'; -import type {BaseSelectionListProps, ButtonOrCheckBoxRoles, FlattenedSectionsReturn, RadioItem, Section, SectionListDataType, User} from './types'; +import type {BaseSelectionListProps, ButtonOrCheckBoxRoles, FlattenedSectionsReturn, ListItem, Section, SectionListDataType} from './types'; -function BaseSelectionList( +function BaseSelectionList( { sections, canSelectMultiple = false, diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index 769eaa80df4b..e666fa6865da 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -3,9 +3,9 @@ import {View} from 'react-native'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {RadioListItemProps} from './types'; +import type {ListItemProps} from './types'; -function RadioListItem({item, showTooltip, textStyles, alternateTextStyles}: RadioListItemProps) { +function RadioListItem({item, showTooltip, textStyles, alternateTextStyles}: ListItemProps) { const styles = useThemeStyles(); return ( diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 3c973ad0bbea..016d4c74d65e 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -4,9 +4,9 @@ import SubscriptAvatar from '@components/SubscriptAvatar'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {UserListItemProps} from './types'; +import type {ListItemProps} from './types'; -function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style}: UserListItemProps) { +function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style}: ListItemProps) { const styles = useThemeStyles(); return ( <> diff --git a/src/components/SelectionList/index.android.tsx b/src/components/SelectionList/index.android.tsx index 8487c6e2cc67..46f2af8356f6 100644 --- a/src/components/SelectionList/index.android.tsx +++ b/src/components/SelectionList/index.android.tsx @@ -3,9 +3,9 @@ import type {ForwardedRef} from 'react'; import {Keyboard} from 'react-native'; import type {TextInput} from 'react-native'; import BaseSelectionList from './BaseSelectionList'; -import type {BaseSelectionListProps, RadioItem, User} from './types'; +import type {BaseSelectionListProps, ListItem} from './types'; -function SelectionList(props: BaseSelectionListProps, ref: ForwardedRef) { +function SelectionList(props: BaseSelectionListProps, ref: ForwardedRef) { return ( (props: BaseSelectionListProps, ref: ForwardedRef) { +function SelectionList(props: BaseSelectionListProps, ref: ForwardedRef) { return ( // eslint-disable-next-line react/jsx-props-no-spreading diff --git a/src/components/SelectionList/index.tsx b/src/components/SelectionList/index.tsx index 93754926cacb..2446e1b4f5c1 100644 --- a/src/components/SelectionList/index.tsx +++ b/src/components/SelectionList/index.tsx @@ -4,9 +4,9 @@ import {Keyboard} from 'react-native'; import type {TextInput} from 'react-native'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import BaseSelectionList from './BaseSelectionList'; -import type {BaseSelectionListProps, RadioItem, User} from './types'; +import type {BaseSelectionListProps, ListItem} from './types'; -function SelectionList(props: BaseSelectionListProps, ref: ForwardedRef) { +function SelectionList(props: BaseSelectionListProps, ref: ForwardedRef) { const [isScreenTouched, setIsScreenTouched] = useState(false); const touchStart = () => setIsScreenTouched(true); diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index a82ddef6febb..c939279dc684 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -32,7 +32,7 @@ type CommonListItemProps = { rightHandSideComponent?: ((item: TItem) => ReactElement) | ReactElement | null; }; -type User = { +type ListItem = { /** Text to display */ text: string; @@ -75,49 +75,21 @@ type User = { index?: number; }; -type UserListItemProps = CommonListItemProps & { +type ListItemProps = CommonListItemProps & { /** The section list item */ - item: User; + item: ListItem; /** Additional styles to apply to text */ style?: StyleProp; }; -type RadioItem = { - /** Text to display */ - text: string; - - /** Alternate text to display */ - alternateText?: string; - - /** Key used internally by React */ - keyForList: string; - - /** Whether this option is selected */ - isSelected?: boolean; - - /** Whether this option is disabled for selection */ - isDisabled?: boolean; - - /** Represents the index of the section it came from */ - sectionIndex?: number; - - /** Represents the index of the option within the section it came from */ - index?: number; -}; - -type RadioListItemProps = CommonListItemProps & { - /** The section list item */ - item: RadioItem; -}; - -type BaseListItemProps = CommonListItemProps & { +type BaseListItemProps = CommonListItemProps & { item: TItem; shouldPreventDefaultFocusOnSelectRow?: boolean; keyForList?: string; }; -type Section = { +type Section = { /** Title of the section */ title?: string; @@ -131,7 +103,7 @@ type Section = { isDisabled?: boolean; }; -type BaseSelectionListProps = Partial & { +type BaseSelectionListProps = Partial & { /** Sections for the section list */ sections: Array>>; @@ -237,7 +209,7 @@ type ItemLayout = { offset: number; }; -type FlattenedSectionsReturn = { +type FlattenedSectionsReturn = { allOptions: TItem[]; selectedOptions: TItem[]; disabledOptionsIndexes: number[]; @@ -247,17 +219,15 @@ type FlattenedSectionsReturn = { type ButtonOrCheckBoxRoles = 'button' | 'checkbox'; -type SectionListDataType = SectionListData>; +type SectionListDataType = SectionListData>; export type { BaseSelectionListProps, CommonListItemProps, - UserListItemProps, Section, - RadioListItemProps, BaseListItemProps, - User, - RadioItem, + ListItem, + ListItemProps, FlattenedSectionsReturn, ItemLayout, ButtonOrCheckBoxRoles, diff --git a/src/components/ValuePicker/ValueSelectorModal.tsx b/src/components/ValuePicker/ValueSelectorModal.tsx index a7b5f494aa67..98e276dcc9ec 100644 --- a/src/components/ValuePicker/ValueSelectorModal.tsx +++ b/src/components/ValuePicker/ValueSelectorModal.tsx @@ -7,19 +7,17 @@ import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import type {ValuePickerItem, ValueSelectorModalProps} from './types'; -function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, onClose, onItemSelected, shouldShowTooltips}: ValueSelectorModalProps) { +function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, onClose, onItemSelected, shouldShowTooltips = true}: ValueSelectorModalProps) { const styles = useThemeStyles(); const [sectionsData, setSectionsData] = useState([]); useEffect(() => { - const itemsData = items.map((item, index) => ({ + const itemsData = items.map((item) => ({ value: item?.value, alternateText: item?.description, keyForList: item.value ?? '', text: item?.label ?? '', isSelected: item === selectedItem, - sectionIndex: 0, - index, })); setSectionsData(itemsData); }, [items, selectedItem]); @@ -34,7 +32,7 @@ function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, on useNativeDriver > item.value === value); return ( diff --git a/src/components/ValuePicker/types.ts b/src/components/ValuePicker/types.ts index 285299bc0101..2f29f820b818 100644 --- a/src/components/ValuePicker/types.ts +++ b/src/components/ValuePicker/types.ts @@ -1,9 +1,9 @@ -import type {RadioItem} from '@components/SelectionList/types'; - -type ValuePickerItem = RadioItem & { +type ValuePickerItem = { value?: string; label?: string; description?: string; + text: string; + keyForList: string; }; type ValueSelectorModalProps = { From 9633be5b555d71e14f7026b65066a64880ba784e Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Fri, 26 Jan 2024 13:51:53 +0100 Subject: [PATCH 039/225] Change onInputChange function type --- src/components/ValuePicker/index.tsx | 2 +- src/components/ValuePicker/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ValuePicker/index.tsx b/src/components/ValuePicker/index.tsx index 8d42bb49c4fc..795395615b45 100644 --- a/src/components/ValuePicker/index.tsx +++ b/src/components/ValuePicker/index.tsx @@ -23,7 +23,7 @@ function ValuePicker({value, label, items, placeholder = '', errorText = '', onI }; const updateInput = (item: ValuePickerItem) => { - if (item?.value && item.value !== value) { + if (item.value !== value) { onInputChange?.(item.value); } hidePickerModal(); diff --git a/src/components/ValuePicker/types.ts b/src/components/ValuePicker/types.ts index 2f29f820b818..1ff2e113f606 100644 --- a/src/components/ValuePicker/types.ts +++ b/src/components/ValuePicker/types.ts @@ -46,7 +46,7 @@ type ValuePickerProps = { errorText?: string; /** Callback to call when the input changes */ - onInputChange?: (value: string) => void; + onInputChange?: (value: string | undefined) => void; /** Text to display under the main menu item */ furtherDetails?: string; From 5af50d4f9d9b6f5c513872891a7de2bbd196efaa Mon Sep 17 00:00:00 2001 From: Vadym Date: Fri, 26 Jan 2024 15:47:34 +0100 Subject: [PATCH 040/225] chore: makes hasUserReacted optional with default value --- .../report/ReactionList/BaseReactionList.tsx | 19 +++++++++---------- .../ReactionList/HeaderReactionList.tsx | 14 +++++++------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index a49c4d832f30..09471faf0b42 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -24,7 +24,7 @@ type BaseReactionListProps = ReactionListProps & { /** * Returns true if the current account has reacted to the report action (with the given skin tone). */ - hasUserReacted: boolean; + hasUserReacted?: boolean; /** * Returns true if the reaction list is visible @@ -40,10 +40,10 @@ const getItemLayout = (data: ArrayLike | null | undefined, inde offset: variables.listItemHeightNormal * index, }); -function BaseReactionList(props: BaseReactionListProps) { +function BaseReactionList({hasUserReacted = false, users, isVisible = false, emojiCodes, emojiCount, emojiName, onClose}: BaseReactionListProps) { const {isSmallScreenWidth} = useWindowDimensions(); const {hoveredComponentBG, reactionListContainer, reactionListContainerFixedWidth, pv2} = useThemeStyles(); - if (!props.isVisible) { + if (!isVisible) { return null; } @@ -59,7 +59,7 @@ function BaseReactionList(props: BaseReactionListProps) { style={{maxWidth: variables.mobileResponsiveWidthBreakpoint}} hoverStyle={hoveredComponentBG} onSelectRow={() => { - props.onClose?.(); + onClose?.(); Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); }} @@ -84,14 +84,13 @@ function BaseReactionList(props: BaseReactionListProps) { return ( <> & { /** Returns true if the current account has reacted to the report action (with the given skin tone). */ - hasUserReacted: boolean; + hasUserReacted?: boolean; }; -function HeaderReactionList(props: HeaderReactionListProps) { +function HeaderReactionList({emojiCodes, emojiCount, emojiName, hasUserReacted = false}: HeaderReactionListProps) { const { flexRow, justifyContentBetween, @@ -32,11 +32,11 @@ function HeaderReactionList(props: HeaderReactionListProps) { return ( - - {props.emojiCodes.join('')} - {props.emojiCount} + + {emojiCodes.join('')} + {emojiCount} - {`:${EmojiUtils.getLocalizedEmojiName(props.emojiName, preferredLocale)}:`} + {`:${EmojiUtils.getLocalizedEmojiName(emojiName, preferredLocale)}:`} ); From ce370cd4af2b566b6fcbfca4b4aaf3233f8763d4 Mon Sep 17 00:00:00 2001 From: Vadym Date: Fri, 26 Jan 2024 15:52:42 +0100 Subject: [PATCH 041/225] chore: removes unneeded JSDocs returns and comments --- src/hooks/useBasePopoverReactionList/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hooks/useBasePopoverReactionList/index.ts b/src/hooks/useBasePopoverReactionList/index.ts index 64c5f6d8efc0..ae7afc230bf9 100644 --- a/src/hooks/useBasePopoverReactionList/index.ts +++ b/src/hooks/useBasePopoverReactionList/index.ts @@ -11,7 +11,6 @@ export default function useBasePopoverReactionList({emojiName, emojiReactions, a const [popoverAnchorPosition, setPopoverAnchorPosition] = useState({horizontal: 0, vertical: 0}); const reactionListRef = useRef(null); - // custom methods function getReactionInformation() { const selectedReaction = emojiReactions?.[emojiName]; @@ -41,8 +40,6 @@ export default function useBasePopoverReactionList({emojiName, emojiReactions, a /** * Get the BasePopoverReactionList anchor position * We calculate the achor coordinates from measureInWindow async method - * - * @returns promise */ function getReactionListMeasuredLocation(): Promise<{x: number; y: number}> { return new Promise((resolve) => { From 3fa2b09f585f5ba2567c4dc33d391802186fa5ac Mon Sep 17 00:00:00 2001 From: Vadym Date: Fri, 26 Jan 2024 15:57:07 +0100 Subject: [PATCH 042/225] fix: reuses the AnchorPosition type --- src/hooks/useBasePopoverReactionList/types.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/hooks/useBasePopoverReactionList/types.ts b/src/hooks/useBasePopoverReactionList/types.ts index 49620ccd9370..2993b2b8acb6 100644 --- a/src/hooks/useBasePopoverReactionList/types.ts +++ b/src/hooks/useBasePopoverReactionList/types.ts @@ -2,6 +2,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import type {ReactionListAnchor, ReactionListEvent} from '@pages/home/ReportScreenContext'; +import type {AnchorPosition} from '@src/styles'; import type {ReportActionReactions} from '@src/types/onyx'; type BasePopoverReactionListOnyxProps = { @@ -33,16 +34,10 @@ type BasePopoverReactionListState = { isPopoverVisible: boolean; /** The horizontal and vertical position (relative to the screen) where the popover will display. */ - popoverAnchorPosition: { - horizontal: number; - vertical: number; - }; + popoverAnchorPosition: AnchorPosition; /** The horizontal and vertical position (relative to the screen) where the cursor is. */ - cursorRelativePosition: { - horizontal: number; - vertical: number; - }; + cursorRelativePosition: AnchorPosition; }; type ShowReactionList = (event: ReactionListEvent | undefined, reactionListAnchor: ReactionListAnchor) => void; From dde9079eb50da52c3e6930b43d40fd903e3c1dfc Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 29 Jan 2024 16:18:40 +0700 Subject: [PATCH 043/225] fix three dot --- src/components/Modal/BaseModal.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index ad3bf2d6a057..5fb3bcdfe652 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -60,7 +60,7 @@ function BaseModal( */ const hideModal = useCallback( (callHideCallback = true) => { - Modal.willAlertModalBecomeVisible(false, isPopover); + Modal.willAlertModalBecomeVisible(false); if (shouldSetModalVisibility) { Modal.setModalVisibility(false); } @@ -72,14 +72,14 @@ function BaseModal( ComposerFocusManager.setReadyToFocus(); } }, - [shouldSetModalVisibility, onModalHide, fullscreen, isPopover], + [shouldSetModalVisibility, onModalHide, fullscreen], ); useEffect(() => { isVisibleRef.current = isVisible; let removeOnCloseListener: () => void; if (isVisible) { - Modal.willAlertModalBecomeVisible(true, isPopover); + Modal.willAlertModalBecomeVisible(true, isPopover || type === CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED); // To handle closing any modal already visible when this modal is mounted, i.e. PopoverReportActionContextMenu removeOnCloseListener = Modal.setCloseModal(onClose); } @@ -90,7 +90,7 @@ function BaseModal( } removeOnCloseListener(); }; - }, [isVisible, wasVisible, onClose, isPopover]); + }, [isVisible, wasVisible, onClose, isPopover, type]); useEffect( () => () => { From a1ebabc5215a313dc31ee4f6a3a1bcd7f527aee0 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 29 Jan 2024 16:29:14 +0700 Subject: [PATCH 044/225] add comment --- src/libs/actions/Modal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Modal.ts b/src/libs/actions/Modal.ts index 985fe4680d1f..6351d0165544 100644 --- a/src/libs/actions/Modal.ts +++ b/src/libs/actions/Modal.ts @@ -72,6 +72,7 @@ function setModalVisibility(isVisible: boolean) { /** * Allows other parts of app to know that an alert modal is about to open. * This will trigger as soon as a modal is opened but not yet visible while animation is running. + * isPopover indicates that the next open modal is popover or bottom docked */ function willAlertModalBecomeVisible(isVisible: boolean, isPopover = false) { Onyx.merge(ONYXKEYS.MODAL, {willAlertModalBecomeVisible: isVisible, isPopover}); From 89451556b7d6fc1bb845687363aa2765c3bd8b6e Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 30 Jan 2024 14:55:58 +0700 Subject: [PATCH 045/225] fix: Focus mode - User priority mode is switched back to focus automatically --- src/libs/actions/User.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index f118797fa659..8b8f6ef4a5da 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -582,13 +582,11 @@ function updateChatPriorityMode(mode: ValueOf, autom }, ]; - if (autoSwitchedToFocusMode) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.NVP_TRY_FOCUS_MODE, - value: true, - }); - } + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_TRY_FOCUS_MODE, + value: true, + }); const parameters: UpdateChatPriorityModeParams = { value: mode, From 1e8cff31160ef05ee63f43dcd60691681547f27e Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Thu, 1 Feb 2024 10:38:25 +0100 Subject: [PATCH 046/225] Fix typing for ValuePicker --- src/components/Form/types.ts | 5 +++-- .../ValuePicker/ValueSelectorModal.tsx | 16 +++------------- src/components/ValuePicker/index.tsx | 2 +- src/components/ValuePicker/types.ts | 8 +++----- src/pages/workspace/WorkspaceNewRoomPage.js | 9 ++++----- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index 447f3205ad68..7af027a5c0c7 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -6,6 +6,7 @@ import type CheckboxWithLabel from '@components/CheckboxWithLabel'; import type Picker from '@components/Picker'; import type SingleChoiceQuestion from '@components/SingleChoiceQuestion'; import type TextInput from '@components/TextInput'; +import type ValuePicker from '@components/ValuePicker'; import type {OnyxFormKey, OnyxValues} from '@src/ONYXKEYS'; import type Form from '@src/types/onyx/Form'; import type {BaseForm, FormValueType} from '@src/types/onyx/Form'; @@ -15,9 +16,9 @@ import type {BaseForm, FormValueType} from '@src/types/onyx/Form'; * when adding new inputs or removing old ones. * * TODO: Add remaining inputs here once these components are migrated to Typescript: - * CountrySelector | StatePicker | DatePicker | EmojiPickerButtonDropdown | RoomNameInput | ValuePicker + * CountrySelector | StatePicker | DatePicker | EmojiPickerButtonDropdown | RoomNameInput */ -type ValidInputs = typeof TextInput | typeof AmountTextInput | typeof SingleChoiceQuestion | typeof CheckboxWithLabel | typeof Picker | typeof AddressSearch; +type ValidInputs = typeof TextInput | typeof AmountTextInput | typeof SingleChoiceQuestion | typeof CheckboxWithLabel | typeof Picker | typeof AddressSearch | typeof ValuePicker; type ValueTypeKey = 'string' | 'boolean' | 'date'; diff --git a/src/components/ValuePicker/ValueSelectorModal.tsx b/src/components/ValuePicker/ValueSelectorModal.tsx index 98e276dcc9ec..18b601463d20 100644 --- a/src/components/ValuePicker/ValueSelectorModal.tsx +++ b/src/components/ValuePicker/ValueSelectorModal.tsx @@ -1,26 +1,16 @@ -import React, {useEffect, useState} from 'react'; +import React, {useMemo} from 'react'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Modal from '@components/Modal'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -import type {ValuePickerItem, ValueSelectorModalProps} from './types'; +import type {ValueSelectorModalProps} from './types'; function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, onClose, onItemSelected, shouldShowTooltips = true}: ValueSelectorModalProps) { const styles = useThemeStyles(); - const [sectionsData, setSectionsData] = useState([]); - useEffect(() => { - const itemsData = items.map((item) => ({ - value: item?.value, - alternateText: item?.description, - keyForList: item.value ?? '', - text: item?.label ?? '', - isSelected: item === selectedItem, - })); - setSectionsData(itemsData); - }, [items, selectedItem]); + const sectionsData = useMemo(() => items.map((item) => ({...item, isSelected: item === selectedItem, keyForList: item.value ?? ''})), [items, selectedItem]); return ( _.map(PolicyUtils.getActivePolicies(props.policies), (policy) => ({ - label: policy.name, - key: policy.id, + text: policy.name, value: policy.id, })), [props.policies], @@ -222,7 +221,7 @@ function WorkspaceNewRoomPage(props) { () => _.map(CONST.REPORT.WRITE_CAPABILITIES, (value) => ({ value, - label: translate(`writeCapabilityPage.writeCapability.${value}`), + text: translate(`writeCapabilityPage.writeCapability.${value}`), })), [translate], ); @@ -232,9 +231,9 @@ function WorkspaceNewRoomPage(props) { _.map( _.filter(_.values(CONST.REPORT.VISIBILITY), (visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE), (visibilityOption) => ({ - label: translate(`newRoomPage.visibilityOptions.${visibilityOption}`), + text: translate(`newRoomPage.visibilityOptions.${visibilityOption}`), value: visibilityOption, - description: translate(`newRoomPage.${visibilityOption}Description`), + alternateText: translate(`newRoomPage.${visibilityOption}Description`), }), ), [translate], From f0871a0e6d9a688000c12a5c9830eb154a3a0673 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 1 Feb 2024 17:09:57 +0700 Subject: [PATCH 047/225] lint fix --- src/pages/settings/Wallet/PaymentMethodList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index ed776ffc213f..15c97a82989a 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -30,8 +30,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {AccountData, BankAccountList, CardList, FundList} from '@src/types/onyx'; -import {BankIcon} from '@src/types/onyx/Bank'; -import {Errors} from '@src/types/onyx/OnyxCommon'; +import type {BankIcon} from '@src/types/onyx/Bank'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; import type PaymentMethod from '@src/types/onyx/PaymentMethod'; import type IconAsset from '@src/types/utils/IconAsset'; From d9ac4295c9a7c04d5faab9ce78905ad34a955ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Thu, 1 Feb 2024 16:12:59 +0100 Subject: [PATCH 048/225] data length fix --- src/components/CategoryPicker/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index ca64c1cc444a..0e57bcf4db03 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -46,7 +46,7 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC false, ); - const header = OptionsListUtils.getHeaderMessageForNonUserList(lodashGet(categoryOptions, '[0].data.length', 0) > 0, debouncedSearchValue); + const header = OptionsListUtils.getHeaderMessageForNonUserList(lodashGet(categoryOptions, '[0].data', []).length > 0, debouncedSearchValue); const policiesCount = OptionsListUtils.getEnabledCategoriesCount(_.values(policyCategories)); const isCategoriesCountBelowThreshold = policyCategoriesCount < CONST.CATEGORY_LIST_THRESHOLD; const showInput = !isCategoriesCountBelowThreshold; From ef9e29005f2f9294f3a826b3f136d715c71c0776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Thu, 1 Feb 2024 17:27:28 +0100 Subject: [PATCH 049/225] type update --- src/libs/OptionsListUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index aa1f23c0e5ba..91d675299f9f 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -64,6 +64,7 @@ type CategorySection = { type Category = { name: string; enabled: boolean; + isSelected?: boolean; }; type Hierarchy = Record; From 829bebb25cf12ac4bf90ff541027eada3cc1ee5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 2 Feb 2024 14:15:49 +0100 Subject: [PATCH 050/225] tests update --- tests/unit/OptionsListUtilsTest.js | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 961df1fa3e90..1857d89c8ae4 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -721,6 +721,7 @@ describe('OptionsListUtils', () => { searchText: 'Food', tooltipText: 'Food', isDisabled: false, + isSelected: false, }, { text: ' Meat', @@ -728,6 +729,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Meat', tooltipText: 'Meat', isDisabled: false, + isSelected: false, }, { text: 'Restaurant', @@ -735,6 +737,7 @@ describe('OptionsListUtils', () => { searchText: 'Restaurant', tooltipText: 'Restaurant', isDisabled: false, + isSelected: false, }, ], }, @@ -751,6 +754,7 @@ describe('OptionsListUtils', () => { searchText: 'Food', tooltipText: 'Food', isDisabled: false, + isSelected: false, }, { text: 'Food: Meat', @@ -758,6 +762,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Meat', tooltipText: 'Food: Meat', isDisabled: false, + isSelected: false, }, ], }, @@ -840,6 +845,7 @@ describe('OptionsListUtils', () => { searchText: 'Medical', tooltipText: 'Medical', isDisabled: false, + isSelected: false, }, ], }, @@ -854,6 +860,7 @@ describe('OptionsListUtils', () => { searchText: 'Restaurant', tooltipText: 'Restaurant', isDisabled: false, + isSelected: false, }, ], }, @@ -868,6 +875,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars', tooltipText: 'Cars', isDisabled: true, + isSelected: false, }, { text: ' Audi', @@ -875,6 +883,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars: Audi', tooltipText: 'Audi', isDisabled: false, + isSelected: false, }, { text: ' Mercedes-Benz', @@ -882,6 +891,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars: Mercedes-Benz', tooltipText: 'Mercedes-Benz', isDisabled: false, + isSelected: false, }, { text: 'Food', @@ -889,6 +899,7 @@ describe('OptionsListUtils', () => { searchText: 'Food', tooltipText: 'Food', isDisabled: false, + isSelected: false, }, { text: ' Meat', @@ -896,6 +907,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Meat', tooltipText: 'Meat', isDisabled: false, + isSelected: false, }, { text: ' Milk', @@ -903,6 +915,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Milk', tooltipText: 'Milk', isDisabled: false, + isSelected: false, }, { text: 'Restaurant', @@ -910,6 +923,7 @@ describe('OptionsListUtils', () => { searchText: 'Restaurant', tooltipText: 'Restaurant', isDisabled: false, + isSelected: false, }, { text: 'Travel', @@ -917,6 +931,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel', tooltipText: 'Travel', isDisabled: true, + isSelected: false, }, { text: ' Meals', @@ -924,6 +939,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals', tooltipText: 'Meals', isDisabled: false, + isSelected: false, }, { text: ' Breakfast', @@ -931,6 +947,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals: Breakfast', tooltipText: 'Breakfast', isDisabled: false, + isSelected: false, }, { text: ' Lunch', @@ -938,6 +955,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals: Lunch', tooltipText: 'Lunch', isDisabled: false, + isSelected: false, }, ], }, @@ -954,6 +972,7 @@ describe('OptionsListUtils', () => { searchText: 'Food', tooltipText: 'Food', isDisabled: false, + isSelected: false, }, { text: 'Food: Meat', @@ -961,6 +980,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Meat', tooltipText: 'Food: Meat', isDisabled: false, + isSelected: false, }, { text: 'Food: Milk', @@ -968,6 +988,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Milk', tooltipText: 'Food: Milk', isDisabled: false, + isSelected: false, }, ], }, @@ -993,6 +1014,7 @@ describe('OptionsListUtils', () => { searchText: 'Medical', tooltipText: 'Medical', isDisabled: false, + isSelected: false, }, ], }, @@ -1439,6 +1461,7 @@ describe('OptionsListUtils', () => { searchText: 'Meals', tooltipText: 'Meals', isDisabled: false, + isSelected: false, }, { text: 'Restaurant', @@ -1446,6 +1469,7 @@ describe('OptionsListUtils', () => { searchText: 'Restaurant', tooltipText: 'Restaurant', isDisabled: false, + isSelected: false, }, { text: 'Food', @@ -1453,6 +1477,7 @@ describe('OptionsListUtils', () => { searchText: 'Food', tooltipText: 'Food', isDisabled: false, + isSelected: false, }, { text: ' Meat', @@ -1460,6 +1485,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Meat', tooltipText: 'Meat', isDisabled: false, + isSelected: false, }, { text: ' Milk', @@ -1467,6 +1493,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Milk', tooltipText: 'Milk', isDisabled: false, + isSelected: false, }, { text: 'Cars', @@ -1474,6 +1501,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars', tooltipText: 'Cars', isDisabled: true, + isSelected: false, }, { text: ' Audi', @@ -1481,6 +1509,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars: Audi', tooltipText: 'Audi', isDisabled: false, + isSelected: false, }, { text: ' Mercedes-Benz', @@ -1488,6 +1517,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars: Mercedes-Benz', tooltipText: 'Mercedes-Benz', isDisabled: false, + isSelected: false, }, { text: 'Travel', @@ -1495,6 +1525,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel', tooltipText: 'Travel', isDisabled: true, + isSelected: false, }, { text: ' Meals', @@ -1502,6 +1533,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals', tooltipText: 'Meals', isDisabled: false, + isSelected: false, }, { text: ' Breakfast', @@ -1509,6 +1541,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals: Breakfast', tooltipText: 'Breakfast', isDisabled: false, + isSelected: false, }, { text: ' Lunch', @@ -1516,6 +1549,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals: Lunch', tooltipText: 'Lunch', isDisabled: false, + isSelected: false, }, { text: 'Plain', @@ -1523,6 +1557,7 @@ describe('OptionsListUtils', () => { searchText: 'Plain', tooltipText: 'Plain', isDisabled: false, + isSelected: false, }, { text: 'Audi', @@ -1530,6 +1565,7 @@ describe('OptionsListUtils', () => { searchText: 'Audi', tooltipText: 'Audi', isDisabled: false, + isSelected: false, }, { text: 'Health', @@ -1537,6 +1573,7 @@ describe('OptionsListUtils', () => { searchText: 'Health', tooltipText: 'Health', isDisabled: false, + isSelected: false, }, { text: 'A', @@ -1544,6 +1581,7 @@ describe('OptionsListUtils', () => { searchText: 'A', tooltipText: 'A', isDisabled: true, + isSelected: false, }, { text: ' B', @@ -1551,6 +1589,7 @@ describe('OptionsListUtils', () => { searchText: 'A: B', tooltipText: 'B', isDisabled: true, + isSelected: false, }, { text: ' C', @@ -1558,6 +1597,7 @@ describe('OptionsListUtils', () => { searchText: 'A: B: C', tooltipText: 'C', isDisabled: false, + isSelected: false, }, { text: ' D', @@ -1565,6 +1605,7 @@ describe('OptionsListUtils', () => { searchText: 'A: B: C: D', tooltipText: 'D', isDisabled: true, + isSelected: false, }, { text: ' E', @@ -1572,6 +1613,7 @@ describe('OptionsListUtils', () => { searchText: 'A: B: C: D: E', tooltipText: 'E', isDisabled: false, + isSelected: false, }, ]; const resultOneLine = [ @@ -1581,6 +1623,7 @@ describe('OptionsListUtils', () => { searchText: 'Meals', tooltipText: 'Meals', isDisabled: false, + isSelected: false, }, { text: 'Restaurant', @@ -1588,6 +1631,7 @@ describe('OptionsListUtils', () => { searchText: 'Restaurant', tooltipText: 'Restaurant', isDisabled: false, + isSelected: false, }, { text: 'Food', @@ -1595,6 +1639,7 @@ describe('OptionsListUtils', () => { searchText: 'Food', tooltipText: 'Food', isDisabled: false, + isSelected: false, }, { text: 'Food: Meat', @@ -1602,6 +1647,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Meat', tooltipText: 'Food: Meat', isDisabled: false, + isSelected: false, }, { text: 'Food: Milk', @@ -1609,6 +1655,7 @@ describe('OptionsListUtils', () => { searchText: 'Food: Milk', tooltipText: 'Food: Milk', isDisabled: false, + isSelected: false, }, { text: 'Cars: Audi', @@ -1616,6 +1663,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars: Audi', tooltipText: 'Cars: Audi', isDisabled: false, + isSelected: false, }, { text: 'Cars: Mercedes-Benz', @@ -1623,6 +1671,7 @@ describe('OptionsListUtils', () => { searchText: 'Cars: Mercedes-Benz', tooltipText: 'Cars: Mercedes-Benz', isDisabled: false, + isSelected: false, }, { text: 'Travel: Meals', @@ -1630,6 +1679,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals', tooltipText: 'Travel: Meals', isDisabled: false, + isSelected: false, }, { text: 'Travel: Meals: Breakfast', @@ -1637,6 +1687,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals: Breakfast', tooltipText: 'Travel: Meals: Breakfast', isDisabled: false, + isSelected: false, }, { text: 'Travel: Meals: Lunch', @@ -1644,6 +1695,7 @@ describe('OptionsListUtils', () => { searchText: 'Travel: Meals: Lunch', tooltipText: 'Travel: Meals: Lunch', isDisabled: false, + isSelected: false, }, { text: 'Plain', @@ -1651,6 +1703,7 @@ describe('OptionsListUtils', () => { searchText: 'Plain', tooltipText: 'Plain', isDisabled: false, + isSelected: false, }, { text: 'Audi', @@ -1658,6 +1711,7 @@ describe('OptionsListUtils', () => { searchText: 'Audi', tooltipText: 'Audi', isDisabled: false, + isSelected: false, }, { text: 'Health', @@ -1665,6 +1719,7 @@ describe('OptionsListUtils', () => { searchText: 'Health', tooltipText: 'Health', isDisabled: false, + isSelected: false, }, { text: 'A: B: C', @@ -1672,6 +1727,7 @@ describe('OptionsListUtils', () => { searchText: 'A: B: C', tooltipText: 'A: B: C', isDisabled: false, + isSelected: false, }, { text: 'A: B: C: D: E', @@ -1679,6 +1735,7 @@ describe('OptionsListUtils', () => { searchText: 'A: B: C: D: E', tooltipText: 'A: B: C: D: E', isDisabled: false, + isSelected: false, }, ]; From b0c8780a0fb3ccf27f0557ebfe1ceb27efc6b3af Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 2 Feb 2024 15:30:40 +0000 Subject: [PATCH 051/225] [TS migration] Migrate react-native-onyx mock --- __mocks__/react-native-onyx.js | 27 --------------------------- __mocks__/react-native-onyx.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 27 deletions(-) delete mode 100644 __mocks__/react-native-onyx.js create mode 100644 __mocks__/react-native-onyx.ts diff --git a/__mocks__/react-native-onyx.js b/__mocks__/react-native-onyx.js deleted file mode 100644 index d44c73e824d3..000000000000 --- a/__mocks__/react-native-onyx.js +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable rulesdir/prefer-onyx-connect-in-libs */ -import Onyx, {withOnyx} from 'react-native-onyx'; - -let connectCallbackDelay = 0; -function addDelayToConnectCallback(delay) { - connectCallbackDelay = delay; -} - -export default { - ...Onyx, - connect: (mapping) => - Onyx.connect({ - ...mapping, - callback: (...params) => { - if (connectCallbackDelay > 0) { - setTimeout(() => { - mapping.callback(...params); - }, connectCallbackDelay); - } else { - mapping.callback(...params); - } - }, - }), - addDelayToConnectCallback, -}; -export {withOnyx}; -/* eslint-enable rulesdir/prefer-onyx-connect-in-libs */ diff --git a/__mocks__/react-native-onyx.ts b/__mocks__/react-native-onyx.ts new file mode 100644 index 000000000000..6d59d2ec66e9 --- /dev/null +++ b/__mocks__/react-native-onyx.ts @@ -0,0 +1,34 @@ +/* eslint-disable rulesdir/prefer-onyx-connect-in-libs */ +import Onyx, {ConnectOptions, OnyxEntry, OnyxKey, withOnyx} from 'react-native-onyx'; +import { OnyxCollection } from 'react-native-onyx/dist/types'; + +let connectCallbackDelay = 0; +function addDelayToConnectCallback(delay: number) { + connectCallbackDelay = delay; +} + +type ReactNativeOnyxMock = { + addDelayToConnectCallback: (delay: number) => void +} & typeof Onyx + +const reactNativeOnyxMock: ReactNativeOnyxMock = { + ...Onyx, + connect: (mapping: ConnectOptions) => + Onyx.connect({ + ...mapping, + callback: (value: OnyxEntry | OnyxCollection, key?: TKey) => { + if (connectCallbackDelay > 0) { + setTimeout(() => { + (mapping.callback as (value: OnyxEntry | OnyxCollection, key?: TKey) => void)?.(value, key) + }, connectCallbackDelay); + } else { + (mapping.callback as (value: OnyxEntry | OnyxCollection, key?: TKey) => void)?.(value, key); + } + }, + }), + addDelayToConnectCallback, +} + +export default reactNativeOnyxMock +export {withOnyx}; +/* eslint-enable rulesdir/prefer-onyx-connect-in-libs */ From 20ad77bc48f9d5ee1fbcead1e1da79974ae829e8 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Mon, 5 Feb 2024 16:23:28 +0000 Subject: [PATCH 052/225] [TS migration][react-native-onyx mock] Addressed suggestion --- __mocks__/react-native-onyx.ts | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/__mocks__/react-native-onyx.ts b/__mocks__/react-native-onyx.ts index 6d59d2ec66e9..89491e589a21 100644 --- a/__mocks__/react-native-onyx.ts +++ b/__mocks__/react-native-onyx.ts @@ -1,6 +1,6 @@ /* eslint-disable rulesdir/prefer-onyx-connect-in-libs */ -import Onyx, {ConnectOptions, OnyxEntry, OnyxKey, withOnyx} from 'react-native-onyx'; -import { OnyxCollection } from 'react-native-onyx/dist/types'; +import type {ConnectOptions, OnyxKey} from 'react-native-onyx'; +import Onyx, { withOnyx} from 'react-native-onyx'; let connectCallbackDelay = 0; function addDelayToConnectCallback(delay: number) { @@ -8,27 +8,32 @@ function addDelayToConnectCallback(delay: number) { } type ReactNativeOnyxMock = { - addDelayToConnectCallback: (delay: number) => void -} & typeof Onyx + addDelayToConnectCallback: (delay: number) => void; +} & typeof Onyx; + +type ConnectionCallback = NonNullable['callback']>; +type ConnectionCallbackParams = Parameters>; const reactNativeOnyxMock: ReactNativeOnyxMock = { ...Onyx, - connect: (mapping: ConnectOptions) => - Onyx.connect({ - ...mapping, - callback: (value: OnyxEntry | OnyxCollection, key?: TKey) => { + connect: (mapping: ConnectOptions) => { + const callback = (...params: ConnectionCallbackParams) => { if (connectCallbackDelay > 0) { setTimeout(() => { - (mapping.callback as (value: OnyxEntry | OnyxCollection, key?: TKey) => void)?.(value, key) + (mapping.callback as (...args: ConnectionCallbackParams) => void)?.(...params); }, connectCallbackDelay); } else { - (mapping.callback as (value: OnyxEntry | OnyxCollection, key?: TKey) => void)?.(value, key); + (mapping.callback as (...args: ConnectionCallbackParams) => void)?.(...params); } - }, - }), + }; + return Onyx.connect({ + ...mapping, + callback, + }); + }, addDelayToConnectCallback, -} +}; -export default reactNativeOnyxMock +export default reactNativeOnyxMock; export {withOnyx}; /* eslint-enable rulesdir/prefer-onyx-connect-in-libs */ From 358c1be66d4230ec5eb50c512b27fba6c524e98a Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 23:32:37 +0700 Subject: [PATCH 053/225] fix: remove unnecessary disable lint --- src/pages/settings/Wallet/WalletPage/CardDetails.tsx | 3 +-- src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx index ce2dbc6b2cb5..77458384c214 100644 --- a/src/pages/settings/Wallet/WalletPage/CardDetails.tsx +++ b/src/pages/settings/Wallet/WalletPage/CardDetails.tsx @@ -16,10 +16,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {PrivatePersonalDetails} from '@src/types/onyx'; -const defaultPrivatePersonalDetails = { +const defaultPrivatePersonalDetails: PrivatePersonalDetails = { address: { street: '', - street2: '', city: '', state: '', zip: '', diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index 87038b163ad7..1f369b1f6999 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -84,7 +84,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi }); const [showConfirmDeleteModal, setShowConfirmDeleteModal] = useState(false); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const hasBankAccount = !_.isEmpty(bankAccountList) || !_.isEmpty(fundList); const hasWallet = !_.isEmpty(userWallet); const hasActivatedWallet = ([CONST.WALLET.TIER_NAME.GOLD, CONST.WALLET.TIER_NAME.PLATINUM] as string[]).includes(userWallet?.tierName ?? ''); @@ -219,7 +218,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi Navigation.navigate(ROUTES.SETTINGS_ADD_DEBIT_CARD); return; } - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing if (paymentType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT || paymentType === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { BankAccounts.openPersonalBankAccountSetupView(); return; @@ -343,7 +341,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi !(paymentMethod.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && paymentMethod.selectedPaymentMethod.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS); // Determines whether or not the modal popup is mounted from the bottom of the screen instead of the side mount on Web or Desktop screens - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const isPopoverBottomMount = anchorPosition.anchorPositionTop === 0 || isSmallScreenWidth; const alertTextStyle = [styles.inlineSystemMessage, styles.flexShrink1]; const alertViewStyle = [styles.flexRow, styles.alignItemsCenter, styles.w100, styles.ph5]; @@ -401,7 +398,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi // eslint-disable-next-line @typescript-eslint/naming-convention onSuccessfulKYC={(_iouPaymentType?: TransferMethod, source?: Source) => navigateToWalletOrTransferBalancePage(source)} onSelectPaymentMethod={(selectedPaymentMethod: string) => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing if (hasActivatedWallet || selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { return; } @@ -495,7 +491,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing onListContentSizeChange={shouldShowAddPaymentMenu || shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} /> @@ -513,7 +508,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi actionPaymentMethodType={shouldShowDefaultDeleteMenu ? paymentMethod.selectedPaymentMethodType : ''} activePaymentMethodID={shouldShowDefaultDeleteMenu ? getSelectedPaymentMethodID() : ''} buttonRef={addPaymentMethodAnchorRef} - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing onListContentSizeChange={shouldShowAddPaymentMenu || shouldShowDefaultDeleteMenu ? setMenuPosition : () => {}} shouldEnableScroll={false} style={styles.mt5} From 57d806a0fdc631a3bbbc23b12c75ee4853afe66b Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 23:35:30 +0700 Subject: [PATCH 054/225] fix: remove unnecessary disable lint --- src/pages/settings/Wallet/PaymentMethodList.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 15c97a82989a..63c9dfabe151 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -140,7 +140,6 @@ function shouldShowDefaultBadge(filteredPaymentMethods: PaymentMethod[], isDefau } const defaultablePaymentMethodCount = filteredPaymentMethods.filter( - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (method) => method.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT || method.accountType === CONST.PAYMENT_METHODS.DEBIT_CARD, ).length; return defaultablePaymentMethodCount > 1; @@ -205,7 +204,6 @@ function PaymentMethodList({ interactive: isExpensifyCard, canDismissError: isExpensifyCard, errors: card.errors, - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing brickRoadIndicator: card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR @@ -227,7 +225,6 @@ function PaymentMethodList({ if (!isOffline) { combinedPaymentMethods = combinedPaymentMethods.filter( - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (paymentMethod) => paymentMethod.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !_.isEmpty(paymentMethod.errors), ); } From 2633478b6748f7c1450f330fe33c927ef4c6bf51 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 23:51:42 +0700 Subject: [PATCH 055/225] typecheck fix --- src/pages/settings/Wallet/WalletEmptyState.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Wallet/WalletEmptyState.tsx b/src/pages/settings/Wallet/WalletEmptyState.tsx index 13a85a1ffb42..42d24d3c1e04 100644 --- a/src/pages/settings/Wallet/WalletEmptyState.tsx +++ b/src/pages/settings/Wallet/WalletEmptyState.tsx @@ -48,6 +48,7 @@ function WalletEmptyState({onAddPaymentMethod}: WalletEmptyStateProps) { > Date: Mon, 5 Feb 2024 22:52:41 +0100 Subject: [PATCH 056/225] TS Migration #25175: Update and rename AppDownloadLinks.js to AppDownloadLinks.tsx Updates related to Typescript Migration #25175. -Migrated component to Typescript -Extended MenuItemProps type as DownloadMenuItem type -Typed menu items as an array of DownloadMenuItems -Removed withWindowDimensions/withLocalize PropTypes and compose() from export per guidelines on migration -Used Hooks for localize instead for translation -Removed _ in JSX mapping of menu items. Saw other TSX files that removes and I was getting ESLint warnings. _ is noted as a best practice but given other TSX files have removed I followed suit. I also saw old ESlint ignores being used when removing _ but I didn't get flagged without this. -Removed onKeyDown attribute and blur() from MenuItem. I saw this nowhere else in the codebase and the removal had no impact on app use functionality (please advise if there's something I am missing but it appeared antiquated) -This component receives props from Navigation but I do not see elsewhere in the codebase that we are explicitly listing these as props via typescript (navigation props should inherintly apply to all pages) -TS warnings made me list translationKey as type TranslationPaths (warning only appears when not providing the translate from useLocalize() with a '' - others working on TS migrations may want to know this) -No ESlint warnings -No VSCode warnings or errors -No errors when testing -Only issue was on iOS simulator there's no App Store app so it doesn't know how to handle the download iOS click but the link is fine and it works everywhere else. -No other components or other files in codebase import this file so change from .js to .tsx should not require changes to imports anywhere. This component is routed to via its name as with other pages in codebase. --- ...pDownloadLinks.js => AppDownloadLinks.tsx} | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) rename src/pages/settings/{AppDownloadLinks.js => AppDownloadLinks.tsx} (58%) diff --git a/src/pages/settings/AppDownloadLinks.js b/src/pages/settings/AppDownloadLinks.tsx similarity index 58% rename from src/pages/settings/AppDownloadLinks.js rename to src/pages/settings/AppDownloadLinks.tsx index 522e15d8e9a8..f5932250cad9 100644 --- a/src/pages/settings/AppDownloadLinks.js +++ b/src/pages/settings/AppDownloadLinks.tsx @@ -1,87 +1,87 @@ -import React from 'react'; +import React, {useRef} from 'react'; import {ScrollView} from 'react-native'; -import _ from 'underscore'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import Navigation from '@libs/Navigation/Navigation'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as Link from '@userActions/Link'; +import type { View } from 'react-native'; +import type { TranslationPaths } from '@src/languages/types'; +import type { MenuItemProps } from '@components/MenuItem'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; -const propTypes = { - ...withLocalizePropTypes, - ...windowDimensionsPropTypes, -}; - -function AppDownloadLinksPage(props) { +function AppDownloadLinksPage() { const styles = useThemeStyles(); - let popoverAnchor; + const { translate } = useLocalize(); + const popoverAnchor = useRef(null); - const menuItems = [ + type DownloadMenuItem = MenuItemProps & { + translationKey: TranslationPaths, + openAppDownloadLink: () => void, + downloadLink: string, + }; + + const menuItems: DownloadMenuItem[] = [ { translationKey: 'initialSettingsPage.appDownloadLinks.android.label', - icon: Expensicons.Android, - iconRight: Expensicons.NewWindow, - action: () => { + openAppDownloadLink: () => { Link.openExternalLink(CONST.APP_DOWNLOAD_LINKS.ANDROID); }, - link: CONST.APP_DOWNLOAD_LINKS.ANDROID, + downloadLink: CONST.APP_DOWNLOAD_LINKS.ANDROID, + icon: Expensicons.Android, + iconRight: Expensicons.NewWindow, }, { translationKey: 'initialSettingsPage.appDownloadLinks.ios.label', - icon: Expensicons.Apple, - iconRight: Expensicons.NewWindow, - action: () => { + openAppDownloadLink: () => { Link.openExternalLink(CONST.APP_DOWNLOAD_LINKS.IOS, true); }, - link: CONST.APP_DOWNLOAD_LINKS.IOS, + downloadLink: CONST.APP_DOWNLOAD_LINKS.IOS, + icon: Expensicons.Apple, + iconRight: Expensicons.NewWindow, }, { translationKey: 'initialSettingsPage.appDownloadLinks.desktop.label', - icon: Expensicons.Monitor, - iconRight: Expensicons.NewWindow, - action: () => { + openAppDownloadLink: () => { Link.openExternalLink(CONST.APP_DOWNLOAD_LINKS.DESKTOP); }, - link: CONST.APP_DOWNLOAD_LINKS.DESKTOP, + downloadLink: CONST.APP_DOWNLOAD_LINKS.DESKTOP, + icon: Expensicons.Monitor, + iconRight: Expensicons.NewWindow, }, ]; return ( Navigation.goBack()} + title={translate('initialSettingsPage.aboutPage.appDownloadLinks')} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_ABOUT)} /> - {_.map(menuItems, (item) => ( + {menuItems.map((item: DownloadMenuItem) => ( item.action()} - onSecondaryInteraction={(e) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, item.link, popoverAnchor)} - onKeyDown={(event) => { - event.target.blur(); - }} - ref={(el) => (popoverAnchor = el)} - title={props.translate(item.translationKey)} + onPress={item.openAppDownloadLink} + onSecondaryInteraction={(e) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, item.downloadLink, popoverAnchor.current)} + ref={popoverAnchor} + title={translate(item.translationKey)} icon={item.icon} - iconRight={item.iconRight} + iconRight={item.iconRight} shouldBlockSelection shouldShowRightIcon - /> + /> ))} ); } -AppDownloadLinksPage.propTypes = propTypes; AppDownloadLinksPage.displayName = 'AppDownloadLinksPage'; -export default compose(withWindowDimensions, withLocalize)(AppDownloadLinksPage); +export default AppDownloadLinksPage; + From 9b283367fe625f2583cd29de3847441dce1349d8 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 6 Feb 2024 14:10:40 +0000 Subject: [PATCH 057/225] [TS migration] Migrate config.local from e2e to Typescript --- tests/e2e/README.md | 4 ++-- tests/e2e/{config.local.js => config.local.ts} | 5 +++-- tests/e2e/testRunner.js | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) rename tests/e2e/{config.local.js => config.local.ts} (88%) diff --git a/tests/e2e/README.md b/tests/e2e/README.md index a142530d4388..64d11d3b2ca4 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -25,11 +25,11 @@ Ideally you want to run these tests on your branch before you want to merge your The tests can be run with the following CLI options: -- `--config`: Extend/Overwrite the default config with your values, e.g. `--config config.local.js` +- `--config`: Extend/Overwrite the default config with your values, e.g. `--config config.local.ts` - `--includes`: Expects a string/regexp to filter the tests to run, e.g. `--includes "login|signup"` - `--skipInstallDeps`: Skips the `npm install` step, useful during development - `--development`: Applies some default configurations: - - Sets the config to `config.local.js`, which executes the tests with fewer iterations + - Sets the config to `config.local.ts`, which executes the tests with fewer iterations - Runs the tests only on the current branch - `--buildMode`: There are three build modes, the default is `full`: 1. **full**: rebuilds the full native app in (e2e) release mode diff --git a/tests/e2e/config.local.js b/tests/e2e/config.local.ts similarity index 88% rename from tests/e2e/config.local.js rename to tests/e2e/config.local.ts index 45b946b91aeb..0ad64170885f 100644 --- a/tests/e2e/config.local.js +++ b/tests/e2e/config.local.ts @@ -1,9 +1,10 @@ -module.exports = { +const config = { MAIN_APP_PACKAGE: 'com.expensify.chat.e2e', DELTA_APP_PACKAGE: 'com.expensify.chat.e2edelta', MAIN_APP_PATH: './android/app/build/outputs/apk/e2e/release/app-e2e-release.apk', DELTA_APP_PATH: './android/app/build/outputs/apk/e2edelta/release/app-e2edelta-release.apk', - BOOT_COOL_DOWN: 1 * 1000, RUNS: 8, }; + +module.exports = config; diff --git a/tests/e2e/testRunner.js b/tests/e2e/testRunner.js index 98faff32397d..6fa594e3e2eb 100644 --- a/tests/e2e/testRunner.js +++ b/tests/e2e/testRunner.js @@ -73,7 +73,7 @@ let buildMode = 'full'; // When we are in dev mode we want to apply certain default params and configs const isDevMode = args.includes('--development'); if (isDevMode) { - setConfigPath('config.local.js'); + setConfigPath('config.local.ts'); buildMode = 'js-only'; } From a5aa7d0fc748b3d595a5ab80dd4dcd40d72b9004 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 6 Feb 2024 21:56:24 +0700 Subject: [PATCH 058/225] revert change --- src/SCREENS.ts | 5 ----- .../OptionsSelector/BaseOptionsSelector.js | 3 --- .../AppNavigator/ModalStackNavigators.tsx | 5 ----- src/pages/NewChatPage.js | 1 - src/pages/ReferralDetailsPage.tsx | 14 -------------- src/pages/SearchPage.js | 2 -- ...poraryForRefactorRequestParticipantsSelector.js | 1 - 7 files changed, 31 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 61aa208f0a98..e2f0e9745561 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -250,11 +250,6 @@ const SCREENS = { REIMBURSEMENT_ACCOUNT: 'ReimbursementAccount', GET_ASSISTANCE: 'GetAssistance', REFERRAL_DETAILS: 'Referral_Details', - REFERRAL_DETAILS_MONEY_REQUEST: 'Referral_Details_Money_Request', - REFERRAL_DETAILS_START_CHAT: 'Referral_Details_Start_Chat', - REFERRAL_DETAILS_SEND_MONEY: 'Referral_Details_Send_Money', - REFERRAL_DETAILS_REFER_FRIEND: 'Referral_Details_Refer_Friend', - REFERRAL_DETAILS_SHARE_CODE: 'Referral_Details_Share_Code', KEYBOARD_SHORTCUTS: 'KeyboardShortcuts', } as const; diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 5c860c7be178..345680e809f3 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -47,9 +47,6 @@ const propTypes = { /** Referral content type */ referralContentType: PropTypes.string, - /** Referral route */ - referralRoute: PropTypes.string, - ...optionsSelectorPropTypes, ...withLocalizePropTypes, ...withThemeStylesPropTypes, diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 5fdb3b8d846e..110c13fa07bf 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -282,11 +282,6 @@ const SignInModalStackNavigator = createModalStackNavigator({ [SCREENS.REFERRAL_DETAILS]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, - [SCREENS.REFERRAL_DETAILS_MONEY_REQUEST]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, - [SCREENS.REFERRAL_DETAILS_START_CHAT]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, - [SCREENS.REFERRAL_DETAILS_SEND_MONEY]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, - [SCREENS.REFERRAL_DETAILS_REFER_FRIEND]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, - [SCREENS.REFERRAL_DETAILS_SHARE_CODE]: () => require('../../../pages/ReferralDetailsPage').default as React.ComponentType, }); const ProcessMoneyRequestHoldStackNavigator = createModalStackNavigator({ diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 420dc487ef71..44131de01fa6 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -24,7 +24,6 @@ import * as Report from '@userActions/Report'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; diff --git a/src/pages/ReferralDetailsPage.tsx b/src/pages/ReferralDetailsPage.tsx index 38d7fe7110ee..f5a5aef14373 100644 --- a/src/pages/ReferralDetailsPage.tsx +++ b/src/pages/ReferralDetailsPage.tsx @@ -18,7 +18,6 @@ import type {ReferralDetailsNavigatorParamList} from '@libs/Navigation/types'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type {Account} from '@src/types/onyx'; import * as ReportActionContextMenu from './home/report/ContextMenu/ReportActionContextMenu'; @@ -37,7 +36,6 @@ function ReferralDetailsPage({route, account}: ReferralDetailsPageProps) { const popoverAnchor = useRef(null); const {isExecuting, singleExecution} = useSingleExecution(); let {contentType} = route.params; - const {transactionID, reportID} = route.params; if (!Object.values(CONST.REFERRAL_PROGRAM.CONTENT_TYPES).includes(contentType)) { contentType = CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND; @@ -49,18 +47,6 @@ function ReferralDetailsPage({route, account}: ReferralDetailsPageProps) { const shouldShowClipboard = contentType === CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND || isShareCode; const referralLink = `${CONST.REFERRAL_PROGRAM.LINK}${account?.primaryLogin ? `/?thanks=${account.primaryLogin}` : ''}`; - function getFallbackRoute() { - const fallbackRoutes = { - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.MONEY_REQUEST]: ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(CONST.IOU.TYPE.REQUEST, transactionID, reportID), - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SEND_MONEY]: ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(CONST.IOU.TYPE.SEND), - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT]: ROUTES.NEW_CHAT, - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND]: ROUTES.SEARCH, - [CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE]: ROUTES.SETTINGS_SHARE_CODE, - }; - - return fallbackRoutes[contentType]; - } - return ( Date: Tue, 6 Feb 2024 22:16:31 +0700 Subject: [PATCH 059/225] add new solution --- src/ROUTES.ts | 2 +- src/components/ReferralProgramCTA.tsx | 2 +- src/libs/Navigation/types.ts | 1 + src/pages/ReferralDetailsPage.tsx | 10 ++++++++++ src/pages/ShareCodePage.tsx | 2 +- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 016e4267803b..328884b3b4bb 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -488,7 +488,7 @@ const ROUTES = { // Referral program promotion REFERRAL_DETAILS_MODAL: { route: 'referral/:contentType', - getRoute: (contentType: string) => `referral/${contentType}` as const, + getRoute: (contentType: string, backTo?: string) => getUrlWithBackToParam(`referral/${contentType}`, backTo), }, PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational', } as const; diff --git a/src/components/ReferralProgramCTA.tsx b/src/components/ReferralProgramCTA.tsx index f1c7539cc6b5..d6a07a25e7b9 100644 --- a/src/components/ReferralProgramCTA.tsx +++ b/src/components/ReferralProgramCTA.tsx @@ -30,7 +30,7 @@ function ReferralProgramCTA({referralContentType, onCloseButtonPress = () => {}} return ( { - Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(referralContentType)); + Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(referralContentType, Navigation.getActiveRouteWithoutParams())); }} style={[styles.w100, styles.br2, styles.highlightBG, styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, {gap: 10, padding: 10}, styles.pl5]} accessibilityLabel="referral" diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 48ef69f27768..53cf6a91b700 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -341,6 +341,7 @@ type SignInNavigatorParamList = { type ReferralDetailsNavigatorParamList = { [SCREENS.REFERRAL_DETAILS]: { contentType: ValueOf; + backTo: string; }; }; diff --git a/src/pages/ReferralDetailsPage.tsx b/src/pages/ReferralDetailsPage.tsx index f5a5aef14373..d8a27e171933 100644 --- a/src/pages/ReferralDetailsPage.tsx +++ b/src/pages/ReferralDetailsPage.tsx @@ -14,10 +14,12 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; +import Navigation from '@libs/Navigation/Navigation'; import type {ReferralDetailsNavigatorParamList} from '@libs/Navigation/types'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type {Account} from '@src/types/onyx'; import * as ReportActionContextMenu from './home/report/ContextMenu/ReportActionContextMenu'; @@ -36,6 +38,7 @@ function ReferralDetailsPage({route, account}: ReferralDetailsPageProps) { const popoverAnchor = useRef(null); const {isExecuting, singleExecution} = useSingleExecution(); let {contentType} = route.params; + const {backTo} = route.params; if (!Object.values(CONST.REFERRAL_PROGRAM.CONTENT_TYPES).includes(contentType)) { contentType = CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND; @@ -60,6 +63,13 @@ function ReferralDetailsPage({route, account}: ReferralDetailsPageProps) { headerContainerStyles={[styles.staticHeaderImage, styles.justifyContentEnd]} backgroundColor={theme.PAGE_THEMES[SCREENS.REFERRAL_DETAILS].backgroundColor} testID={ReferralDetailsPage.displayName} + onBackButtonPress={() => { + if (backTo) { + Navigation.goBack(backTo as Route); + return; + } + Navigation.goBack(); + }} > {contentHeader} {contentBody} diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index 831f0eb8f1d8..5e703b392b04 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -116,7 +116,7 @@ function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodeP Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE))} + onPress={() => Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE, Navigation.getActiveRouteWithoutParams()))} /> From 3297913db780c51e7100672a3fdb61aff5e66f26 Mon Sep 17 00:00:00 2001 From: hkopser99 <144170694+hkopser99@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:59:50 +0100 Subject: [PATCH 060/225] Update AppDownloadLinks.tsx --- src/pages/settings/AppDownloadLinks.tsx | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/pages/settings/AppDownloadLinks.tsx b/src/pages/settings/AppDownloadLinks.tsx index f5932250cad9..009c00996d7a 100644 --- a/src/pages/settings/AppDownloadLinks.tsx +++ b/src/pages/settings/AppDownloadLinks.tsx @@ -1,29 +1,29 @@ import React, {useRef} from 'react'; import {ScrollView} from 'react-native'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; +import type {View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; +import type {MenuItemProps} from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as Link from '@userActions/Link'; -import type { View } from 'react-native'; -import type { TranslationPaths } from '@src/languages/types'; -import type { MenuItemProps } from '@components/MenuItem'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; function AppDownloadLinksPage() { const styles = useThemeStyles(); - const { translate } = useLocalize(); + const {translate} = useLocalize(); const popoverAnchor = useRef(null); type DownloadMenuItem = MenuItemProps & { - translationKey: TranslationPaths, - openAppDownloadLink: () => void, - downloadLink: string, + translationKey: TranslationPaths; + openAppDownloadLink: () => void; + downloadLink: string; }; const menuItems: DownloadMenuItem[] = [ @@ -71,10 +71,10 @@ function AppDownloadLinksPage() { ref={popoverAnchor} title={translate(item.translationKey)} icon={item.icon} - iconRight={item.iconRight} + iconRight={item.iconRight} shouldBlockSelection shouldShowRightIcon - /> + /> ))} @@ -84,4 +84,3 @@ function AppDownloadLinksPage() { AppDownloadLinksPage.displayName = 'AppDownloadLinksPage'; export default AppDownloadLinksPage; - From 148af58b52b5d9dd35c0e461e312f8b1024fbd3a Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Tue, 6 Feb 2024 21:18:15 +0100 Subject: [PATCH 061/225] reintroduce animated input for Markdown composer --- src/components/AnimatedMarkdownTextInput.tsx | 37 ++++++++++++++++++++ src/components/Composer/index.native.tsx | 4 +-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 src/components/AnimatedMarkdownTextInput.tsx diff --git a/src/components/AnimatedMarkdownTextInput.tsx b/src/components/AnimatedMarkdownTextInput.tsx new file mode 100644 index 000000000000..f51c54fbe1e9 --- /dev/null +++ b/src/components/AnimatedMarkdownTextInput.tsx @@ -0,0 +1,37 @@ +import type {ForwardedRef} from 'react'; +import React from 'react'; +import type {MarkdownTextInputProps} from '@expensify/react-native-live-markdown'; +import type {TextInput} from 'react-native'; +import {MarkdownTextInput} from '@expensify/react-native-live-markdown'; +import Animated from 'react-native-reanimated'; +import useTheme from '@hooks/useTheme'; + +// Convert the underlying TextInput into an Animated component so that we can take an animated ref and pass it to a worklet +const AnimatedTextInput = Animated.createAnimatedComponent(MarkdownTextInput); + +type AnimatedTextInputRef = typeof AnimatedTextInput & TextInput & HTMLInputElement; + +function RNTextInputWithRef(props: MarkdownTextInputProps, ref: ForwardedRef) { + const theme = useTheme(); + + return ( + { + if (typeof ref !== 'function') { + return; + } + ref(refHandle as AnimatedTextInputRef); + }} + // eslint-disable-next-line + {...props} + /> + ); +} + +RNTextInputWithRef.displayName = 'RNTextInputWithRef'; + +export default React.forwardRef(RNTextInputWithRef); +export type {AnimatedTextInputRef}; diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index a3c037211d4c..15d4825049ac 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -1,9 +1,9 @@ -import {MarkdownTextInput} from '@expensify/react-native-live-markdown'; import type {ForwardedRef} from 'react'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {TextInput} from 'react-native'; import {StyleSheet} from 'react-native'; -import type {AnimatedTextInputRef} from '@components/RNTextInput'; +import type {AnimatedTextInputRef} from '@components/AnimatedMarkdownTextInput'; +import MarkdownTextInput from '@components/AnimatedMarkdownTextInput'; import useMarkdownStyle from '@hooks/useMarkdownStyle'; import useResetComposerFocus from '@hooks/useResetComposerFocus'; import useStyleUtils from '@hooks/useStyleUtils'; From 5a13a561790f81c138e4230f88ec5b46d4bb5f06 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 7 Feb 2024 15:37:34 +0800 Subject: [PATCH 062/225] don't early return if email is empty --- src/libs/actions/Policy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 866206895d5e..2fdb449627d2 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1562,7 +1562,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string const policyID = generatePolicyID(); const workspaceName = generateDefaultWorkspaceName(sessionEmail); const employeeAccountID = iouReport.ownerAccountID; - const employeeEmail = iouReport.ownerEmail; + const employeeEmail = iouReport.ownerEmail ?? ''; const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); const oldPersonalPolicyID = iouReport.policyID; const iouReportID = iouReport.reportID; @@ -1582,7 +1582,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string expenseCreatedReportActionID: workspaceChatCreatedReportActionID, } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); - if (!employeeAccountID || !employeeEmail) { + if (!employeeAccountID) { return; } From ffebb9cdfaf2920787c864a9ec5bb25869c9e542 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 7 Feb 2024 08:37:47 +0000 Subject: [PATCH 063/225] [TS migration][react-native-onyx-mock] Fixed lint --- __mocks__/react-native-onyx.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/__mocks__/react-native-onyx.ts b/__mocks__/react-native-onyx.ts index 89491e589a21..beed7be9cbdf 100644 --- a/__mocks__/react-native-onyx.ts +++ b/__mocks__/react-native-onyx.ts @@ -1,6 +1,6 @@ /* eslint-disable rulesdir/prefer-onyx-connect-in-libs */ import type {ConnectOptions, OnyxKey} from 'react-native-onyx'; -import Onyx, { withOnyx} from 'react-native-onyx'; +import Onyx, {withOnyx} from 'react-native-onyx'; let connectCallbackDelay = 0; function addDelayToConnectCallback(delay: number) { @@ -36,4 +36,3 @@ const reactNativeOnyxMock: ReactNativeOnyxMock = { export default reactNativeOnyxMock; export {withOnyx}; -/* eslint-enable rulesdir/prefer-onyx-connect-in-libs */ From c0c76652433710a5494ce73ae836f62e550cb8af Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Wed, 7 Feb 2024 10:13:56 +0100 Subject: [PATCH 064/225] run prettier --- src/components/AnimatedMarkdownTextInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AnimatedMarkdownTextInput.tsx b/src/components/AnimatedMarkdownTextInput.tsx index f51c54fbe1e9..ce40fba6c159 100644 --- a/src/components/AnimatedMarkdownTextInput.tsx +++ b/src/components/AnimatedMarkdownTextInput.tsx @@ -1,8 +1,8 @@ +import type {MarkdownTextInputProps} from '@expensify/react-native-live-markdown'; +import {MarkdownTextInput} from '@expensify/react-native-live-markdown'; import type {ForwardedRef} from 'react'; import React from 'react'; -import type {MarkdownTextInputProps} from '@expensify/react-native-live-markdown'; import type {TextInput} from 'react-native'; -import {MarkdownTextInput} from '@expensify/react-native-live-markdown'; import Animated from 'react-native-reanimated'; import useTheme from '@hooks/useTheme'; From 4ba03739d7c8f4b8b5b891614982d5c4323f3ce1 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Wed, 7 Feb 2024 10:38:43 +0100 Subject: [PATCH 065/225] rename wrapping component to RNMarkdownTextInput --- src/components/Composer/index.native.tsx | 4 ++-- ...nimatedMarkdownTextInput.tsx => RNMarkdownTextInput.tsx} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/components/{AnimatedMarkdownTextInput.tsx => RNMarkdownTextInput.tsx} (84%) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index 15d4825049ac..592b62b68c4e 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -2,8 +2,8 @@ import type {ForwardedRef} from 'react'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {TextInput} from 'react-native'; import {StyleSheet} from 'react-native'; -import type {AnimatedTextInputRef} from '@components/AnimatedMarkdownTextInput'; -import MarkdownTextInput from '@components/AnimatedMarkdownTextInput'; +import type {AnimatedTextInputRef} from '@components/RNMarkdownTextInput'; +import MarkdownTextInput from '@components/RNMarkdownTextInput'; import useMarkdownStyle from '@hooks/useMarkdownStyle'; import useResetComposerFocus from '@hooks/useResetComposerFocus'; import useStyleUtils from '@hooks/useStyleUtils'; diff --git a/src/components/AnimatedMarkdownTextInput.tsx b/src/components/RNMarkdownTextInput.tsx similarity index 84% rename from src/components/AnimatedMarkdownTextInput.tsx rename to src/components/RNMarkdownTextInput.tsx index ce40fba6c159..ca761c1ee285 100644 --- a/src/components/AnimatedMarkdownTextInput.tsx +++ b/src/components/RNMarkdownTextInput.tsx @@ -11,7 +11,7 @@ const AnimatedTextInput = Animated.createAnimatedComponent(MarkdownTextInput); type AnimatedTextInputRef = typeof AnimatedTextInput & TextInput & HTMLInputElement; -function RNTextInputWithRef(props: MarkdownTextInputProps, ref: ForwardedRef) { +function RNMarkdownTextInput(props: MarkdownTextInputProps, ref: ForwardedRef) { const theme = useTheme(); return ( @@ -31,7 +31,7 @@ function RNTextInputWithRef(props: MarkdownTextInputProps, ref: ForwardedRef Date: Wed, 7 Feb 2024 15:01:41 +0100 Subject: [PATCH 066/225] migrate sanitizeStringForJSONParseTest to TypeScript --- ...orJSONParseTest.js => sanitizeStringForJSONParseTest.ts} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename tests/unit/{sanitizeStringForJSONParseTest.js => sanitizeStringForJSONParseTest.ts} (86%) diff --git a/tests/unit/sanitizeStringForJSONParseTest.js b/tests/unit/sanitizeStringForJSONParseTest.ts similarity index 86% rename from tests/unit/sanitizeStringForJSONParseTest.js rename to tests/unit/sanitizeStringForJSONParseTest.ts index a3a3219ed576..1a65040316c9 100644 --- a/tests/unit/sanitizeStringForJSONParseTest.js +++ b/tests/unit/sanitizeStringForJSONParseTest.ts @@ -4,7 +4,7 @@ import sanitizeStringForJSONParse from '../../.github/libs/sanitizeStringForJSON const badInputs = [null, undefined, 42, true]; // Invalid JSON Data should be able to get parsed and the parsed result should match the input text. -const invalidJSONData = [ +const invalidJSONData: string[][] = [ ['Hello \t world!', 'Hello \t world!'], ['Hello \n world!', 'Hello \n world!'], ['Hello \n\tworld!', 'Hello \n\tworld!'], @@ -21,7 +21,7 @@ const invalidJSONData = [ ]; // Valid JSON Data should be able to get parsed and the input text should be unmodified. -const validJSONData = [ +const validJSONData: string[][] = [ ['', ''], ['Hello world!', 'Hello world!'], ['Hello\\\\world!', 'Hello\\\\world!'], @@ -30,6 +30,7 @@ const validJSONData = [ describe('santizeStringForJSONParse', () => { describe.each(badInputs)('willDetectBadInputs', (input) => { test('sanitizeStringForJSONParse', () => { + // @ts-expect-error TODO: Remove this once sanitizeStringForJSONParse (https://github.com/Expensify/App/issues/25360) is migrated to TypeScript. expect(() => sanitizeStringForJSONParse(input)).toThrow(); }); }); @@ -37,6 +38,7 @@ describe('santizeStringForJSONParse', () => { describe.each(invalidJSONData)('canHandleInvalidJSON', (input, expectedOutput) => { test('sanitizeStringForJSONParse', () => { const badJSON = `{"key": "${input}"}`; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- it's supposed to throw an error expect(() => JSON.parse(badJSON)).toThrow(); const goodJSON = JSON.parse(`{"key": "${sanitizeStringForJSONParse(input)}"}`); expect(goodJSON.key).toStrictEqual(expectedOutput); From 09aadfd11c3aa3c4479eb63390f1b8ad42dba133 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 8 Feb 2024 10:04:44 +0100 Subject: [PATCH 067/225] migrate NumberUtilsTest to TypeScript --- tests/unit/{NumberUtilsTest.js => NumberUtilsTest.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/unit/{NumberUtilsTest.js => NumberUtilsTest.ts} (97%) diff --git a/tests/unit/NumberUtilsTest.js b/tests/unit/NumberUtilsTest.ts similarity index 97% rename from tests/unit/NumberUtilsTest.js rename to tests/unit/NumberUtilsTest.ts index 3fc547476be1..6ec6919569e0 100644 --- a/tests/unit/NumberUtilsTest.js +++ b/tests/unit/NumberUtilsTest.ts @@ -5,7 +5,7 @@ describe('libs/NumberUtils', () => { const id = NumberUtils.rand64(); expect(typeof id).toBe('string'); // eslint-disable-next-line no-undef - expect(BigInt(id)).toBeLessThanOrEqual(BigInt(9223372036854775807)); + expect(BigInt(id)).toBeLessThanOrEqual(BigInt(9223372036854775807n)); // eslint-disable-next-line no-undef expect(BigInt(id)).toBeGreaterThanOrEqual(0); }); From b1b7451b5e460639040a44194215b3db11291f77 Mon Sep 17 00:00:00 2001 From: vadymbokatov <138146362+vadymbokatov@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:09:36 +0100 Subject: [PATCH 068/225] Update src/hooks/useBasePopoverReactionList/index.ts Co-authored-by: Getabalew Tesfaye <75031127+getusha@users.noreply.github.com> --- src/hooks/useBasePopoverReactionList/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useBasePopoverReactionList/index.ts b/src/hooks/useBasePopoverReactionList/index.ts index ae7afc230bf9..5f598d9eb1dd 100644 --- a/src/hooks/useBasePopoverReactionList/index.ts +++ b/src/hooks/useBasePopoverReactionList/index.ts @@ -117,7 +117,7 @@ export default function useBasePopoverReactionList({emojiName, emojiReactions, a } // Hide the list when all reactions are removed - const users = emojiReactions?.[emojiName].users; + const users = emojiReactions?.[emojiName]?.users; const isEmptyList = users && Object.keys(users).length === 0; if (!isEmptyList) { From 0052faea252bcafbc472ee5725b033a12711ee5d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 8 Feb 2024 14:31:54 +0100 Subject: [PATCH 069/225] migrate GooglePlacesUtilsTest to TypeScript --- src/libs/GooglePlacesUtils.ts | 1 + ...ePlacesUtilsTest.js => GooglePlacesUtilsTest.ts} | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) rename tests/unit/{GooglePlacesUtilsTest.js => GooglePlacesUtilsTest.ts} (90%) diff --git a/src/libs/GooglePlacesUtils.ts b/src/libs/GooglePlacesUtils.ts index 43fac2b9a785..312a0dc61c1f 100644 --- a/src/libs/GooglePlacesUtils.ts +++ b/src/libs/GooglePlacesUtils.ts @@ -51,3 +51,4 @@ function getPlaceAutocompleteTerms(addressTerms: AddressTerm[]): GetPlaceAutocom } export {getAddressComponents, getPlaceAutocompleteTerms}; +export type {AddressComponent, FieldsToExtract, AddressTerm}; diff --git a/tests/unit/GooglePlacesUtilsTest.js b/tests/unit/GooglePlacesUtilsTest.ts similarity index 90% rename from tests/unit/GooglePlacesUtilsTest.js rename to tests/unit/GooglePlacesUtilsTest.ts index b94068f521ff..b34d859771e5 100644 --- a/tests/unit/GooglePlacesUtilsTest.js +++ b/tests/unit/GooglePlacesUtilsTest.ts @@ -1,13 +1,15 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import * as GooglePlacesUtils from '../../src/libs/GooglePlacesUtils'; +import type {AddressComponent, AddressTerm, FieldsToExtract} from '../../src/libs/GooglePlacesUtils'; -const standardObjectToFind = { +const standardObjectToFind: FieldsToExtract = { sublocality: 'long_name', administrative_area_level_1: 'short_name', postal_code: 'long_name', 'doesnt-exist': 'long_name', }; -const objectWithCountryToFind = { +const objectWithCountryToFind: FieldsToExtract = { sublocality: 'long_name', administrative_area_level_1: 'short_name', postal_code: 'long_name', @@ -15,7 +17,7 @@ const objectWithCountryToFind = { country: 'long_name', }; -const addressComponents = [ +const addressComponents: AddressComponent[] = [ { long_name: 'Bushwick', short_name: 'Bushwick', @@ -43,10 +45,7 @@ const addressComponents = [ }, ]; -const autoCompleteTerms = [ - {offset: 0, value: 'Bangladesh Border Road'}, - {offset: 24, value: 'Bangladesh'}, -]; +const autoCompleteTerms: AddressTerm[] = [{value: 'Bangladesh Border Road'}, {value: 'Bangladesh'}]; describe('GooglePlacesUtilsTest', () => { describe('getAddressComponents', () => { From b23edff9bd1e8613e113e88c192a607b305dfcb1 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 8 Feb 2024 15:37:58 +0100 Subject: [PATCH 070/225] [TS migration] Migrate 'TrieTest.js' test --- tests/unit/{TrieTest.js => TrieTest.ts} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename tests/unit/{TrieTest.js => TrieTest.ts} (91%) diff --git a/tests/unit/TrieTest.js b/tests/unit/TrieTest.ts similarity index 91% rename from tests/unit/TrieTest.js rename to tests/unit/TrieTest.ts index ad9958990ae4..d08fa8d7d520 100644 --- a/tests/unit/TrieTest.js +++ b/tests/unit/TrieTest.ts @@ -1,4 +1,4 @@ -import Trie from '../../src/libs/Trie'; +import Trie from '@src/libs/Trie'; describe('Trie', () => { it('Test if a node can be found in the Trie', () => { @@ -8,8 +8,8 @@ describe('Trie', () => { wordTrie.add('joy', {code: '😂'}); wordTrie.add('rofl', {code: '🤣'}); expect(wordTrie.search('eyes')).toBeNull(); - expect(wordTrie.search('joy').metaData).toEqual({code: '😂'}); - expect(wordTrie.search('gRiN').metaData).toEqual({code: '😁'}); + expect(wordTrie.search('joy')?.metaData).toEqual({code: '😂'}); + expect(wordTrie.search('gRiN')?.metaData).toEqual({code: '😁'}); }); it('Test finding all leaf nodes starting with a substring', () => { @@ -65,13 +65,13 @@ describe('Trie', () => { const wordTrie = new Trie(); wordTrie.add('John', {code: '👨🏼'}); wordTrie.update('John', {code: '👨🏻'}); - expect(wordTrie.search('John').metaData).toEqual({code: '👨🏻'}); + expect(wordTrie.search('John')?.metaData).toEqual({code: '👨🏻'}); }); it('Test throwing an error when try to update a word that does not exist in the Trie.', () => { const wordTrie = new Trie(); expect(() => { - wordTrie.update('smile'); + wordTrie.update('smile', {}); }).toThrow('Word does not exist in the Trie'); }); }); From e9761c395dfd8b46a0b0838103eff055f3baa3b8 Mon Sep 17 00:00:00 2001 From: Vadym Date: Thu, 8 Feb 2024 15:45:03 +0100 Subject: [PATCH 071/225] fix: adds the missing dependencies in the useEffect --- src/hooks/useBasePopoverReactionList/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useBasePopoverReactionList/index.ts b/src/hooks/useBasePopoverReactionList/index.ts index 5f598d9eb1dd..3134526e8507 100644 --- a/src/hooks/useBasePopoverReactionList/index.ts +++ b/src/hooks/useBasePopoverReactionList/index.ts @@ -125,7 +125,7 @@ export default function useBasePopoverReactionList({emojiName, emojiReactions, a } hideReactionList(); - }); + }, [emojiReactions, emojiName, isPopoverVisible, reportActionID, preferredLocale]); return {isPopoverVisible, cursorRelativePosition, popoverAnchorPosition, getReactionInformation, hideReactionList, reactionListRef, showReactionList}; } From e98afd1a1282cf4ea9a9a49533bf365712479d3f Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 9 Feb 2024 10:04:34 +0100 Subject: [PATCH 072/225] modify imports to use @src --- tests/unit/GooglePlacesUtilsTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/GooglePlacesUtilsTest.ts b/tests/unit/GooglePlacesUtilsTest.ts index b34d859771e5..9a7649158c8f 100644 --- a/tests/unit/GooglePlacesUtilsTest.ts +++ b/tests/unit/GooglePlacesUtilsTest.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import * as GooglePlacesUtils from '../../src/libs/GooglePlacesUtils'; -import type {AddressComponent, AddressTerm, FieldsToExtract} from '../../src/libs/GooglePlacesUtils'; +import * as GooglePlacesUtils from '@src/libs/GooglePlacesUtils'; +import type {AddressComponent, AddressTerm, FieldsToExtract} from '@src/libs/GooglePlacesUtils'; const standardObjectToFind: FieldsToExtract = { sublocality: 'long_name', From c29b7e0f0bec165e7a717c9ec6794472663473bb Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 9 Feb 2024 09:28:11 +0000 Subject: [PATCH 073/225] [TS migration][config.local] Code improvements --- tests/e2e/config.local.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/e2e/config.local.ts b/tests/e2e/config.local.ts index 0ad64170885f..40f7afde3985 100644 --- a/tests/e2e/config.local.ts +++ b/tests/e2e/config.local.ts @@ -1,4 +1,6 @@ -const config = { +type Config = Record; + +const config: Config = { MAIN_APP_PACKAGE: 'com.expensify.chat.e2e', DELTA_APP_PACKAGE: 'com.expensify.chat.e2edelta', MAIN_APP_PATH: './android/app/build/outputs/apk/e2e/release/app-e2e-release.apk', @@ -7,4 +9,4 @@ const config = { RUNS: 8, }; -module.exports = config; +export default config; From 69e09b05bf295cc7b8df54317b66fc38870a150c Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 9 Feb 2024 17:21:36 +0100 Subject: [PATCH 074/225] ref: move console test to TS --- .../e2e/compare/output/{console.js => console.ts} | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) rename tests/e2e/compare/output/{console.js => console.ts} (76%) diff --git a/tests/e2e/compare/output/console.js b/tests/e2e/compare/output/console.ts similarity index 76% rename from tests/e2e/compare/output/console.js rename to tests/e2e/compare/output/console.ts index cfcd7019bbdd..3027a88d546b 100644 --- a/tests/e2e/compare/output/console.js +++ b/tests/e2e/compare/output/console.ts @@ -1,14 +1,22 @@ const {formatDurationDiffChange} = require('./format'); -const printRegularLine = (entry) => { +type Entry = { + name: string; +}; + +type Data = { + significance: Entry[]; + meaningless: Entry[]; +}; + +const printRegularLine = (entry: Entry) => { console.debug(` - ${entry.name}: ${formatDurationDiffChange(entry)}`); }; /** * Prints the result simply to console. - * @param {Object} data */ -module.exports = (data) => { +module.exports = (data: Data) => { // No need to log errors or warnings as these were be logged on the fly console.debug(''); console.debug('❇️ Performance comparison results:'); From e6854394b21a81c0a936690d4b79183099f8e938 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Sun, 11 Feb 2024 01:39:48 +0700 Subject: [PATCH 075/225] consistent system paid message --- src/libs/ReportUtils.ts | 8 ++++++-- .../home/report/ContextMenu/ContextMenuActions.tsx | 10 +++++++++- src/pages/home/report/ReportActionItemMessage.tsx | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 26280f95447d..4884f9ecd319 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2232,6 +2232,7 @@ function getReportPreviewMessage( isPreviewMessageForParentChatReport = false, policy: OnyxEntry = null, isForListPreview = false, + shouldHidePayer = false, ): string { const reportActionMessage = reportAction?.message?.[0].html ?? ''; @@ -2296,7 +2297,10 @@ function getReportPreviewMessage( if (isSettled(report.reportID) || (report.isWaitingOnBankAccount && isPreviewMessageForParentChatReport)) { // A settled report preview message can come in three formats "paid ... elsewhere" or "paid ... with Expensify" let translatePhraseKey: TranslationPaths = 'iou.paidElsewhereWithAmount'; - if ( + + if (isPreviewMessageForParentChatReport) { + translatePhraseKey = 'iou.payerPaidAmount'; + } else if ( [CONST.IOU.PAYMENT_TYPE.VBBA, CONST.IOU.PAYMENT_TYPE.EXPENSIFY].some((paymentType) => paymentType === originalMessage?.paymentType) || !!reportActionMessage.match(/ (with Expensify|using Expensify)$/) || report.isWaitingOnBankAccount @@ -2304,7 +2308,7 @@ function getReportPreviewMessage( translatePhraseKey = 'iou.paidWithExpensifyWithAmount'; } - let actualPayerName = report.managerID === currentUserAccountID ? '' : getDisplayNameForParticipant(report.managerID, true); + let actualPayerName = report.managerID === currentUserAccountID || shouldHidePayer ? '' : getDisplayNameForParticipant(report.managerID, true); actualPayerName = actualPayerName && isForListPreview && !isPreviewMessageForParentChatReport ? `${actualPayerName}:` : actualPayerName; const payerDisplayName = isPreviewMessageForParentChatReport ? payerName : actualPayerName; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 64a7d1813255..cb34ffccb0ce 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -354,7 +354,15 @@ const ContextMenuActions: ContextMenuAction[] = [ const modifyExpenseMessage = ModifiedExpenseMessage.getForReportAction(reportID, reportAction); Clipboard.setString(modifyExpenseMessage); } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { - const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction); + const displayMessage = ReportUtils.getReportPreviewMessage( + ReportUtils.getReport(ReportUtils.getOriginalReportID(reportID, reportAction)), + reportAction, + false, + false, + null, + false, + true, + ); Clipboard.setString(displayMessage); } else if (ReportActionsUtils.isCreatedTaskReportAction(reportAction)) { const taskPreviewMessage = TaskUtils.getTaskCreatedMessage(reportAction); diff --git a/src/pages/home/report/ReportActionItemMessage.tsx b/src/pages/home/report/ReportActionItemMessage.tsx index 0ffb8b9f6964..ad571eb66f10 100644 --- a/src/pages/home/report/ReportActionItemMessage.tsx +++ b/src/pages/home/report/ReportActionItemMessage.tsx @@ -58,7 +58,7 @@ function ReportActionItemMessage({action, displayAsGroup, reportID, style, isHid const originalMessage = action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? action.originalMessage : null; const iouReportID = originalMessage?.IOUReportID; if (iouReportID) { - iouMessage = ReportUtils.getReportPreviewMessage(ReportUtils.getReport(iouReportID), action); + iouMessage = ReportUtils.getReportPreviewMessage(ReportUtils.getReport(iouReportID), action, false, false, null, false, true); } } From e520939240f1ad8f2d23e4e0554ee40f722209b4 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Sun, 11 Feb 2024 11:00:15 +0700 Subject: [PATCH 076/225] 36218 cached viewport height conflict event --- src/hooks/useDebouncedState.ts | 2 +- src/hooks/useWindowDimensions/index.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hooks/useDebouncedState.ts b/src/hooks/useDebouncedState.ts index 3677c85f3081..6fda8f7d54d4 100644 --- a/src/hooks/useDebouncedState.ts +++ b/src/hooks/useDebouncedState.ts @@ -17,7 +17,7 @@ import CONST from '@src/CONST'; * @example * const [value, debouncedValue, setValue] = useDebouncedState("", 300); */ -function useDebouncedState(initialValue: T, delay = CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME): [T, T, (value: T) => void] { +function useDebouncedState(initialValue: T, delay: number = CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME): [T, T, (value: T) => void] { const [value, setValue] = useState(initialValue); const [debouncedValue, setDebouncedValue] = useState(initialValue); const debouncedSetDebouncedValue = useRef(debounce(setDebouncedValue, delay)).current; diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index 4ba2c4ad9b41..1d905ce55d79 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -1,8 +1,10 @@ -import {useEffect, useRef, useState} from 'react'; +import {useEffect, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import {Dimensions, useWindowDimensions} from 'react-native'; +import useDebouncedState from '@hooks/useDebouncedState'; import * as Browser from '@libs/Browser'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import type WindowDimensions from './types'; const initalViewportHeight = window.visualViewport?.height ?? window.innerHeight; @@ -23,7 +25,7 @@ export default function (useCachedViewportHeight = false): WindowDimensions { const isMediumScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint && windowWidth <= variables.tabletResponsiveWidthBreakpoint; const isLargeScreenWidth = windowWidth > variables.tabletResponsiveWidthBreakpoint; - const [cachedViewportHeight, setCachedViewportHeight] = useState(windowHeight); + const [, cachedViewportHeight, setCachedViewportHeight] = useDebouncedState(windowHeight, CONST.TIMING.RESIZE_DEBOUNCE_TIME); const handleFocusIn = useRef((event: FocusEvent) => { const targetElement = event.target as HTMLElement; @@ -66,6 +68,7 @@ export default function (useCachedViewportHeight = false): WindowDimensions { return; } setCachedViewportHeight(windowHeight); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [windowHeight, isCachedViewportHeight]); useEffect(() => { From 2e986c27344ce1f268bb351d083951402ba4f7de Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Mon, 12 Feb 2024 04:55:46 +0100 Subject: [PATCH 077/225] fix: Fixing the getWorkspaceIcon function to return ther default workspace avatar for default rooms --- src/libs/ReportUtils.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 26280f95447d..74050f95a555 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1452,9 +1452,11 @@ function getIconsForParticipants(participants: number[], personalDetails: OnyxCo */ function getWorkspaceIcon(report: OnyxEntry, policy: OnyxEntry = null): Icon { const workspaceName = getPolicyName(report, false, policy); - const policyExpenseChatAvatarSource = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar - ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar - : getDefaultWorkspaceAvatar(workspaceName); + const rootParentReport = getRootParentReport(report); + const policyExpenseChatAvatarSource = + (isEmptyObject(rootParentReport) || !isDefaultRoom(rootParentReport)) && allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar + ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar + : getDefaultWorkspaceAvatar(workspaceName); const workspaceIcon: Icon = { source: policyExpenseChatAvatarSource ?? '', From 563f076798c0e17ed05de0259f6d17146df73295 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 12 Feb 2024 17:15:40 +0800 Subject: [PATCH 078/225] don't apply padding if loading --- src/pages/home/HeaderView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 29f2264dc77d..d02e2eb76d9f 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -217,7 +217,7 @@ function HeaderView(props) { dataSet={{dragArea: true}} > - + {isLoading ? ( ) : ( From fd779ad3a12bf819dea1d3667fc4e5765bb71cdb Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Mon, 12 Feb 2024 10:15:49 +0100 Subject: [PATCH 079/225] add review suggestions --- src/components/Composer/index.native.tsx | 6 +++--- src/components/RNMarkdownTextInput.tsx | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index 592b62b68c4e..166be722c794 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -2,7 +2,7 @@ import type {ForwardedRef} from 'react'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {TextInput} from 'react-native'; import {StyleSheet} from 'react-native'; -import type {AnimatedTextInputRef} from '@components/RNMarkdownTextInput'; +import type {AnimatedMarkdownTextInputRef} from '@components/RNMarkdownTextInput'; import MarkdownTextInput from '@components/RNMarkdownTextInput'; import useMarkdownStyle from '@hooks/useMarkdownStyle'; import useResetComposerFocus from '@hooks/useResetComposerFocus'; @@ -31,7 +31,7 @@ function Composer( }: ComposerProps, ref: ForwardedRef, ) { - const textInput = useRef(null); + const textInput = useRef(null); const {isFocused, shouldResetFocus} = useResetComposerFocus(textInput); const theme = useTheme(); const markdownStyle = useMarkdownStyle(); @@ -42,7 +42,7 @@ function Composer( * Set the TextInput Ref * @param {Element} el */ - const setTextInputRef = useCallback((el: AnimatedTextInputRef) => { + const setTextInputRef = useCallback((el: AnimatedMarkdownTextInputRef) => { textInput.current = el; if (typeof ref !== 'function' || textInput.current === null) { return; diff --git a/src/components/RNMarkdownTextInput.tsx b/src/components/RNMarkdownTextInput.tsx index ca761c1ee285..d36af6e13826 100644 --- a/src/components/RNMarkdownTextInput.tsx +++ b/src/components/RNMarkdownTextInput.tsx @@ -7,15 +7,15 @@ import Animated from 'react-native-reanimated'; import useTheme from '@hooks/useTheme'; // Convert the underlying TextInput into an Animated component so that we can take an animated ref and pass it to a worklet -const AnimatedTextInput = Animated.createAnimatedComponent(MarkdownTextInput); +const AnimatedMarkdownTextInput = Animated.createAnimatedComponent(MarkdownTextInput); -type AnimatedTextInputRef = typeof AnimatedTextInput & TextInput & HTMLInputElement; +type AnimatedMarkdownTextInputRef = typeof AnimatedMarkdownTextInput & TextInput & HTMLInputElement; -function RNMarkdownTextInput(props: MarkdownTextInputProps, ref: ForwardedRef) { +function RNMarkdownTextInputWithRef(props: MarkdownTextInputProps, ref: ForwardedRef) { const theme = useTheme(); return ( - Date: Mon, 12 Feb 2024 11:12:23 +0100 Subject: [PATCH 080/225] fix: move to es6 modules --- tests/e2e/compare/output/console.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/e2e/compare/output/console.ts b/tests/e2e/compare/output/console.ts index 3027a88d546b..2ec1a8ecde84 100644 --- a/tests/e2e/compare/output/console.ts +++ b/tests/e2e/compare/output/console.ts @@ -1,4 +1,4 @@ -const {formatDurationDiffChange} = require('./format'); +import {formatDurationDiffChange} from './format'; type Entry = { name: string; @@ -16,7 +16,7 @@ const printRegularLine = (entry: Entry) => { /** * Prints the result simply to console. */ -module.exports = (data: Data) => { +const consoleMock = (data: Data) => { // No need to log errors or warnings as these were be logged on the fly console.debug(''); console.debug('❇️ Performance comparison results:'); @@ -29,3 +29,5 @@ module.exports = (data: Data) => { console.debug(''); }; + +export default consoleMock; From 8b90adf32c39c5b32e44790d920266900285ca71 Mon Sep 17 00:00:00 2001 From: hkopser99 <144170694+hkopser99@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:42:08 +0100 Subject: [PATCH 081/225] Update AppDownloadLinks.tsx Moved Type declaration out of component scope. --- src/pages/settings/AppDownloadLinks.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/settings/AppDownloadLinks.tsx b/src/pages/settings/AppDownloadLinks.tsx index 009c00996d7a..5ded324e5b45 100644 --- a/src/pages/settings/AppDownloadLinks.tsx +++ b/src/pages/settings/AppDownloadLinks.tsx @@ -15,17 +15,17 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; +type DownloadMenuItem = MenuItemProps & { + translationKey: TranslationPaths; + openAppDownloadLink: () => void; + downloadLink: string; +}; + function AppDownloadLinksPage() { const styles = useThemeStyles(); const {translate} = useLocalize(); const popoverAnchor = useRef(null); - type DownloadMenuItem = MenuItemProps & { - translationKey: TranslationPaths; - openAppDownloadLink: () => void; - downloadLink: string; - }; - const menuItems: DownloadMenuItem[] = [ { translationKey: 'initialSettingsPage.appDownloadLinks.android.label', From 129f4512f3947c55a0ed2153431dcc31445eecac Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 12 Feb 2024 15:34:08 +0100 Subject: [PATCH 082/225] ref: move FileUtilsTest to TS --- tests/unit/{FileUtilsTest.js => FileUtilsTest.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/unit/{FileUtilsTest.js => FileUtilsTest.ts} (100%) diff --git a/tests/unit/FileUtilsTest.js b/tests/unit/FileUtilsTest.ts similarity index 100% rename from tests/unit/FileUtilsTest.js rename to tests/unit/FileUtilsTest.ts From 133149a92ee6a0add5ddaf90fde001378626eed3 Mon Sep 17 00:00:00 2001 From: Nikhil Dewoolkar Date: Mon, 12 Feb 2024 22:03:47 +0530 Subject: [PATCH 083/225] Update MentionUserRenderer.tsx --- .../HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index ad9cfb4e6384..890835072852 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -43,7 +43,7 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona navigationRoute = ROUTES.PROFILE.getRoute(htmlAttribAccountID); } else if ('data' in tnode && !isEmptyObject(tnode.data)) { // We need to remove the LTR unicode and leading @ from data as it is not part of the login - displayNameOrLogin = tnode.data.replace(CONST.UNICODE.LTR, '').slice(1); + displayNameOrLogin = tnode.data.replace(CONST.UNICODE.LTR, '').slice(1).toLowerCase(); accountID = PersonalDetailsUtils.getAccountIDsByLogins([displayNameOrLogin])?.[0]; navigationRoute = ROUTES.DETAILS.getRoute(displayNameOrLogin); From fbfc712198929c6872641108845f98d274facbf9 Mon Sep 17 00:00:00 2001 From: Sheena Trepanier Date: Mon, 12 Feb 2024 14:18:42 -0800 Subject: [PATCH 084/225] Update Troubleshooting.md Per https://github.com/Expensify/Expensify/issues/357408 We need to make some minor copy updates to satisfy Bancorp requirements for the new card program. --- .../company-cards/Troubleshooting.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md index 01d94b219801..15e27a202b2f 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md +++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md @@ -49,16 +49,16 @@ The card will only appear in the drop-down list for assignment once it’s activ # Troubleshooting issues assigning company cards ## Why do bank connections break? -Banks often make changes to safeguard your confidential information, and when they do, we need to update the connection between Expensify and the bank. We have a team of engineers that works closely with banks to monitor this and update our software accordingly when this happens. +Banks often make changes to safeguard your confidential information, and when they do, we need to update the connection between Expensify and the bank. We have a team of engineers who work closely with banks to monitor this and update our software accordingly when this happens. The first step is to check if there have been any changes to your bank information. Have you recently changed your banking password without updating it in Expensify? Has your banking username or card number been updated? Did you update your security questions for your bank? -If you've answered "yes" to any of these questions, a Domain Admins need to update this information in Expensify and manually reestablish the connection by heading to *Settings* > *Domains* > _Domain Name_ > *Company Cards* > *Fix*. The Domain Admin will be prompted to enter the new credentials/updated information and this should reestablish the connection. +If you've answered "yes" to any of these questions, a Domain Admins need to update this information in Expensify and manually re-establish the connection by heading to *Settings* > *Domains* > _Domain Name_ > *Company Cards* > *Fix*. The Domain Admin will be prompted to enter the new credentials/updated information and this should reestablish the connection. ## How do I resolve errors while I’m trying to import my card?* Make sure you're importing your card in the correct spot in Expensify and selecting the right bank connection. For company cards, use the master administrative credentials to import your set of cards at *Settings* > *Domains* > _Domain Name_ > *Company Cards* > *Import Card*. Please note there are some things that cannot be bypassed within Expensify, including two-factor authentication being enabled within your bank account. This will prevent the connection from remaining stable and will need to be turned off on the bank side. ## What are the most reliable bank connections in Expensify?* -The most reliable corporate card to use with Expensify is the Expensify Card. We offer daily settlement, unapproved expense limits, and real-time compliance for secure and efficient spending, as well as 2% cash back. Click here to learn more or apply. +The most reliable corporate card to use with Expensify is the Expensify Visa® Commercial Card. We offer daily settlement, unapproved expense limits, and real-time compliance for secure and efficient spending, as well as 2% cash back. Click here to learn more or apply. Additionally, we've teamed up with major banks worldwide to ensure a smooth import of credit card transactions into your accounts. Corporate cards from the following banks also offer the most dependable connections in Expensify: - American Express - Bank of America From c049078806871a81b04d925b479246e2ee097ca1 Mon Sep 17 00:00:00 2001 From: Sheena Trepanier Date: Mon, 12 Feb 2024 15:27:41 -0800 Subject: [PATCH 085/225] Update Troubleshooting.md Missed an update to cash back, just added it. --- .../company-cards/Troubleshooting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md index 15e27a202b2f..bf4b21440b3c 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md +++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Troubleshooting.md @@ -58,7 +58,7 @@ Make sure you're importing your card in the correct spot in Expensify and select Please note there are some things that cannot be bypassed within Expensify, including two-factor authentication being enabled within your bank account. This will prevent the connection from remaining stable and will need to be turned off on the bank side. ## What are the most reliable bank connections in Expensify?* -The most reliable corporate card to use with Expensify is the Expensify Visa® Commercial Card. We offer daily settlement, unapproved expense limits, and real-time compliance for secure and efficient spending, as well as 2% cash back. Click here to learn more or apply. +The most reliable corporate card to use with Expensify is the Expensify Visa® Commercial Card. We offer daily settlement, unapproved expense limits, and real-time compliance for secure and efficient spending, as well as 2% cash back (_Applies to USD purchases only._) Click here to learn more or apply. Additionally, we've teamed up with major banks worldwide to ensure a smooth import of credit card transactions into your accounts. Corporate cards from the following banks also offer the most dependable connections in Expensify: - American Express - Bank of America From 1925a110d042671a84ba4613ee1e345894f36711 Mon Sep 17 00:00:00 2001 From: Vadym Date: Tue, 13 Feb 2024 10:24:31 +0100 Subject: [PATCH 086/225] fix: popover position --- .../PopoverReactionList/BasePopoverReactionList.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx index 6a464c46cce8..9f024ac92846 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.tsx @@ -37,10 +37,6 @@ function BasePopoverReactionList(props: BasePopoverReactionListPropsWithLocalWit fullscreen withoutOverlay anchorRef={reactionListRef} - anchorAlignment={{ - horizontal: 'left', - vertical: 'top', - }} > Date: Tue, 13 Feb 2024 12:16:10 +0100 Subject: [PATCH 087/225] Fix bug with Group chat stacked avatars are different in Search list and LHN --- src/components/SelectionList/BaseListItem.tsx | 140 +++++++++--------- .../SelectionList/BaseSelectionList.tsx | 2 + src/components/SelectionList/UserListItem.tsx | 37 ++++- src/components/SelectionList/types.ts | 13 ++ src/pages/SearchPage/index.js | 1 + 5 files changed, 121 insertions(+), 72 deletions(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index 71845931ba52..553fee4887db 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -25,6 +25,7 @@ function BaseListItem({ onDismissError = () => {}, rightHandSideComponent, keyForList, + shouldUseOnySubscriptAvatar = true, }: BaseListItemProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -63,77 +64,84 @@ function BaseListItem({ onMouseDown={shouldPreventDefaultFocusOnSelectRow ? (e) => e.preventDefault() : undefined} nativeID={keyForList} > - - {canSelectMultiple && ( + {({hovered}) => ( + <> - - {item.isSelected && ( - - )} - - - )} + {canSelectMultiple && ( + + + {item.isSelected && ( + + )} + + + )} - onSelectRow(item)} - showTooltip={showTooltip} - /> + onSelectRow(item)} + showTooltip={showTooltip} + isFocused={isFocused} + isHovered={hovered} + shouldUseOnySubscriptAvatar={shouldUseOnySubscriptAvatar} + /> - {!canSelectMultiple && item.isSelected && !rightHandSideComponent && ( - - - - + {!canSelectMultiple && item.isSelected && !rightHandSideComponent && ( + + + + + + )} + {rightHandSideComponentRender()} - )} - {rightHandSideComponentRender()} - - {isUserItem && item.invitedSecondaryLogin && ( - - {translate('workspace.people.invitedBySecondaryLogin', {secondaryLogin: item.invitedSecondaryLogin})} - + {isUserItem && item.invitedSecondaryLogin && ( + + {translate('workspace.people.invitedBySecondaryLogin', {secondaryLogin: item.invitedSecondaryLogin})} + + )} + )} diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index c77e244b4c92..ca9213a0fcaa 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -60,6 +60,7 @@ function BaseSelectionList( rightHandSideComponent, isLoadingNewOptions = false, onLayout, + shouldUseOnySubscriptAvatar = true, }: BaseSelectionListProps, inputRef: ForwardedRef, ) { @@ -300,6 +301,7 @@ function BaseSelectionList( shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} keyForList={item.keyForList} + shouldUseOnySubscriptAvatar={shouldUseOnySubscriptAvatar} /> ); }; diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index fede09c1b435..c9fb9a92f9fb 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -1,20 +1,45 @@ import React from 'react'; import {View} from 'react-native'; +import MultipleAvatars from '@components/MultipleAvatars'; import SubscriptAvatar from '@components/SubscriptAvatar'; import TextWithTooltip from '@components/TextWithTooltip'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import type {UserListItemProps} from './types'; -function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style}: UserListItemProps) { +function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style, isFocused, isHovered, shouldUseOnySubscriptAvatar = true}: UserListItemProps) { const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + + const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; + const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; + const hoveredBackgroundColor = !!styles.sidebarLinkHover && 'backgroundColor' in styles.sidebarLinkHover ? styles.sidebarLinkHover.backgroundColor : theme.sidebar; + return ( <> {!!item.icons && ( - + <> + {item.shouldShowSubscript ?? shouldUseOnySubscriptAvatar ? ( + + ) : ( + + )} + )} & { @@ -81,6 +84,12 @@ type UserListItemProps = CommonListItemProps & { /** Additional styles to apply to text */ style?: StyleProp; + + /** Is item hovered */ + isHovered?: boolean; + + /** Whether this item should use only the subscript avatar */ + shouldUseOnySubscriptAvatar?: boolean; }; type RadioItem = { @@ -115,6 +124,7 @@ type BaseListItemProps = CommonListItemProps = { @@ -239,6 +249,9 @@ type BaseSelectionListProps = Partial void; + + /** Whether this item should use only the subscript avatar */ + shouldUseOnySubscriptAvatar?: boolean; }; type ItemLayout = { diff --git a/src/pages/SearchPage/index.js b/src/pages/SearchPage/index.js index 7c472296dfe1..d9cae3ff8496 100644 --- a/src/pages/SearchPage/index.js +++ b/src/pages/SearchPage/index.js @@ -164,6 +164,7 @@ function SearchPage({betas, reports, isSearchingForReports}) { showLoadingPlaceholder={!didScreenTransitionEnd || !isOptionsDataReady} footerContent={SearchPageFooterInstance} isLoadingNewOptions={isSearchingForReports} + shouldUseOnySubscriptAvatar={false} /> From 4fd550f589ad731b34e6ff7b62f75090dd646b69 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Tue, 13 Feb 2024 12:25:28 +0100 Subject: [PATCH 088/225] Fix prettier issues --- src/components/SelectionList/BaseSelectionList.tsx | 2 +- src/components/SelectionList/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index ca9213a0fcaa..3a052bc2be1e 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -301,7 +301,7 @@ function BaseSelectionList( shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} keyForList={item.keyForList} - shouldUseOnySubscriptAvatar={shouldUseOnySubscriptAvatar} + shouldUseOnySubscriptAvatar={shouldUseOnySubscriptAvatar} /> ); }; diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 0b9e561e8db2..cbba278292b8 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -249,7 +249,7 @@ type BaseSelectionListProps = Partial void; - + /** Whether this item should use only the subscript avatar */ shouldUseOnySubscriptAvatar?: boolean; }; From c99ce964bada195c2d866e628de95cb8319a5075 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 13 Feb 2024 12:48:14 +0100 Subject: [PATCH 089/225] Migrate GitUtils and GitUtilsTests --- .../createOrUpdateStagingDeploy.js | 2 +- .../getDeployPullRequestList.js | 2 +- .github/libs/{GitUtils.js => GitUtils.ts} | 52 ++++++++----------- .github/libs/versionUpdater.js | 6 +-- tests/unit/CIGitLogicTest.sh | 12 ++--- .../unit/{GitUtilsTest.js => GitUtilsTest.ts} | 46 +++++++++------- tests/unit/createOrUpdateStagingDeployTest.js | 2 +- tests/unit/markPullRequestsAsDeployedTest.js | 2 +- .../utils/{bumpVersion.mjs => bumpVersion.ts} | 2 +- ...viousVersion.mjs => getPreviousVersion.ts} | 2 +- tests/utils/getPullRequestsMergedBetween.mjs | 14 ----- tests/utils/getPullRequestsMergedBetween.ts | 17 ++++++ 12 files changed, 81 insertions(+), 78 deletions(-) rename .github/libs/{GitUtils.js => GitUtils.ts} (76%) rename tests/unit/{GitUtilsTest.js => GitUtilsTest.ts} (57%) rename tests/utils/{bumpVersion.mjs => bumpVersion.ts} (98%) rename tests/utils/{getPreviousVersion.mjs => getPreviousVersion.ts} (97%) delete mode 100755 tests/utils/getPullRequestsMergedBetween.mjs create mode 100755 tests/utils/getPullRequestsMergedBetween.ts diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js index f0e45257bbef..4441348a3c36 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.js @@ -4,7 +4,7 @@ const _ = require('underscore'); const core = require('@actions/core'); const CONST = require('../../../libs/CONST'); const GithubUtils = require('../../../libs/GithubUtils'); -const GitUtils = require('../../../libs/GitUtils'); +const GitUtils = require('../../../libs/GitUtils').default; async function run() { // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time diff --git a/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js b/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js index a64ebfc240ba..da0dde4ff1ca 100644 --- a/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js +++ b/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.js @@ -2,7 +2,7 @@ const _ = require('underscore'); const core = require('@actions/core'); const github = require('@actions/github'); const ActionUtils = require('../../../libs/ActionUtils'); -const GitUtils = require('../../../libs/GitUtils'); +const GitUtils = require('../../../libs/GitUtils').default; const GithubUtils = require('../../../libs/GithubUtils'); async function run() { diff --git a/.github/libs/GitUtils.js b/.github/libs/GitUtils.ts similarity index 76% rename from .github/libs/GitUtils.js rename to .github/libs/GitUtils.ts index 2076763fbb55..051a3f3dc40b 100644 --- a/.github/libs/GitUtils.js +++ b/.github/libs/GitUtils.ts @@ -1,14 +1,18 @@ -const _ = require('underscore'); -const {spawn, execSync} = require('child_process'); -const CONST = require('./CONST'); -const sanitizeStringForJSONParse = require('./sanitizeStringForJSONParse'); -const {getPreviousVersion, SEMANTIC_VERSION_LEVELS} = require('../libs/versionUpdater'); +import {execSync, spawn} from 'child_process'; +import * as CONST from './CONST'; +import sanitizeStringForJSONParse from './sanitizeStringForJSONParse'; +import * as VERSION_UPDATER from './versionUpdater'; + +type CommitType = { + commit: string; + subject: string; + authorName: string; +}; /** - * @param {String} tag - * @param {String} [shallowExcludeTag] when fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) + * When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) */ -function fetchTag(tag, shallowExcludeTag = '') { +function fetchTag(tag: string, shallowExcludeTag = '') { let shouldRetry = true; let needsRepack = false; while (shouldRetry) { @@ -47,19 +51,15 @@ function fetchTag(tag, shallowExcludeTag = '') { /** * Get merge logs between two tags (inclusive) as a JavaScript object. - * - * @param {String} fromTag - * @param {String} toTag - * @returns {Promise>>} */ -function getCommitHistoryAsJSON(fromTag, toTag) { +function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise { // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousVersion(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); + const previousPatchVersion = VERSION_UPDATER.getPreviousVersion(fromTag, VERSION_UPDATER.SEMANTIC_VERSION_LEVELS.PATCH); fetchTag(fromTag, previousPatchVersion); fetchTag(toTag, previousPatchVersion); console.log('Getting pull requests merged between the following tags:', fromTag, toTag); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let stdout = ''; let stderr = ''; const args = ['log', '--format={"commit": "%H", "authorName": "%an", "subject": "%s"},', `${fromTag}...${toTag}`]; @@ -89,26 +89,23 @@ function getCommitHistoryAsJSON(fromTag, toTag) { // Then remove newlines, format as JSON and convert to a proper JS object const json = `[${sanitizedOutput}]`.replace(/(\r\n|\n|\r)/gm, '').replace('},]', '}]'); - return JSON.parse(json); + return JSON.parse(json) as CommitType[]; }); } /** * Parse merged PRs, excluding those from irrelevant branches. - * - * @param {Array>} commits - * @returns {Array} */ -function getValidMergedPRs(commits) { - const mergedPRs = new Set(); - _.each(commits, (commit) => { +function getValidMergedPRs(commits: CommitType[]): number[] { + const mergedPRs = new Set(); + commits.forEach((commit) => { const author = commit.authorName; if (author === CONST.OS_BOTIFY) { return; } const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); - if (!_.isArray(match) || match.length < 2) { + if (!Array.isArray(match) || match.length < 2) { return; } @@ -128,12 +125,8 @@ function getValidMergedPRs(commits) { /** * Takes in two git tags and returns a list of PR numbers of all PRs merged between those two tags - * - * @param {String} fromTag - * @param {String} toTag - * @returns {Promise>} – Pull request numbers */ -async function getPullRequestsMergedBetween(fromTag, toTag) { +async function getPullRequestsMergedBetween(fromTag: string, toTag: string) { console.log(`Looking for commits made between ${fromTag} and ${toTag}...`); const commitList = await getCommitHistoryAsJSON(fromTag, toTag); console.log(`Commits made between ${fromTag} and ${toTag}:`, commitList); @@ -144,7 +137,8 @@ async function getPullRequestsMergedBetween(fromTag, toTag) { return pullRequestNumbers; } -module.exports = { +export default { getValidMergedPRs, getPullRequestsMergedBetween, }; +export type {CommitType}; diff --git a/.github/libs/versionUpdater.js b/.github/libs/versionUpdater.js index 78e8085621bd..5526ee38d2ea 100644 --- a/.github/libs/versionUpdater.js +++ b/.github/libs/versionUpdater.js @@ -65,8 +65,8 @@ const incrementPatch = (major, minor, patch) => { /** * Increments a build version * - * @param {Number} version - * @param {Number} level + * @param {String} version + * @param {String} level * @returns {String} */ const incrementVersion = (version, level) => { @@ -127,7 +127,7 @@ function getPreviousVersion(currentVersion, level) { return getVersionStringFromNumber(major, minor, patch, build - 1); } -module.exports = { +export { getVersionNumberFromString, getVersionStringFromNumber, incrementVersion, diff --git a/tests/unit/CIGitLogicTest.sh b/tests/unit/CIGitLogicTest.sh index f35ead3542d3..73fd01f5c0a0 100755 --- a/tests/unit/CIGitLogicTest.sh +++ b/tests/unit/CIGitLogicTest.sh @@ -10,9 +10,9 @@ declare -r GIT_REMOTE="$HOME/dummyGitRemotes/DumDumRepo" declare -r SEMVER_LEVEL_BUILD='BUILD' declare -r SEMVER_LEVEL_PATCH='PATCH' -declare -r bumpVersion="$TEST_DIR/utils/bumpVersion.mjs" -declare -r getPreviousVersion="$TEST_DIR/utils/getPreviousVersion.mjs" -declare -r getPullRequestsMergedBetween="$TEST_DIR/utils/getPullRequestsMergedBetween.mjs" +declare -r bumpVersion="$TEST_DIR/utils/bumpVersion.ts" +declare -r getPreviousVersion="$TEST_DIR/utils/getPreviousVersion.ts" +declare -r getPullRequestsMergedBetween="$TEST_DIR/utils/getPullRequestsMergedBetween.ts" source "$SCRIPTS_DIR/shellUtils.sh" @@ -76,7 +76,7 @@ function bump_version { info "Bumping version..." setup_git_as_osbotify git switch main - npm --no-git-tag-version version "$(node "$bumpVersion" "$(print_version)" "$1")" + npm --no-git-tag-version version "$(ts-node "$bumpVersion" "$(print_version)" "$1")" git add package.json package-lock.json git commit -m "Update version to $(print_version)" git push origin main @@ -142,7 +142,7 @@ function cherry_pick_pr { checkout_repo setup_git_as_osbotify - PREVIOUS_PATCH_VERSION="$(node "$getPreviousVersion" "$(print_version)" "$SEMVER_LEVEL_PATCH")" + PREVIOUS_PATCH_VERSION="$(ts-node "$getPreviousVersion" "$(print_version)" "$SEMVER_LEVEL_PATCH")" git fetch origin main staging --no-tags --shallow-exclude="$PREVIOUS_PATCH_VERSION" git switch staging @@ -201,7 +201,7 @@ function deploy_production { function assert_prs_merged_between { checkout_repo - output=$(node "$getPullRequestsMergedBetween" "$1" "$2") + output=$(ts-node "$getPullRequestsMergedBetween" "$1" "$2") info "Checking output of getPullRequestsMergedBetween $1 $2" assert_equal "$output" "$3" } diff --git a/tests/unit/GitUtilsTest.js b/tests/unit/GitUtilsTest.ts similarity index 57% rename from tests/unit/GitUtilsTest.js rename to tests/unit/GitUtilsTest.ts index 945a97a8ddd7..f82fa0e28ada 100644 --- a/tests/unit/GitUtilsTest.js +++ b/tests/unit/GitUtilsTest.ts @@ -1,38 +1,44 @@ -const GitUtils = require('../../.github/libs/GitUtils'); +import type {CommitType} from '../../.github/libs/GitUtils'; +import GitUtils from '../../.github/libs/GitUtils'; -const data = [ +type ExampleDataType = { + input: CommitType[]; + expectedOutput: number[]; +}; + +const data: ExampleDataType[] = [ { input: [], expectedOutput: [], }, { - input: [{commit: '1', subject: 'Some random commit message', author: 'test@gmail.com'}], + input: [{commit: '1', subject: 'Some random commit message', authorName: 'test@gmail.com'}], expectedOutput: [], }, { input: [ - {commit: '1', subject: 'Start adding StagingDeployCash logic', author: 'test@gmail.com'}, - {commit: '2', subject: 'Setting up bones', author: 'test@gmail.com'}, - {commit: '3', subject: 'Merge pull request #337 from Expensify/francoisUpdateQbdSyncManager', author: 'test@gmail.com'}, - {commit: '4', subject: 'Merge pull request #336 from Expensify/andrew-pr-cla', author: 'test@gmail.com'}, - {commit: '5', subject: 'Update QBD Sync Manager version', author: 'test@gmail.com'}, - {commit: '6', subject: 'Only run CLA on PR comments or events', author: 'test@gmail.com'}, - {commit: '7', subject: 'Merge pull request #331 from Expensify/marcaaron-killMoment', author: 'test@gmail.com'}, - {commit: '8', subject: 'Merge pull request #330 from Expensify/andrew-cla-update', author: 'test@gmail.com'}, - {commit: '9', subject: 'Merge pull request #333 from Expensify/Rory-AddOnOffSwitchTooltip', author: 'test@gmail.com'}, - {commit: '10', subject: 'Setup OnOffSwitch component with tooltips', author: 'test@gmail.com'}, - {commit: '11', subject: 'Merge pull request #332 from Expensify/alex-mechler-patch-1', author: 'test@gmail.com'}, - {commit: '12', subject: 'Return to old hash-based deploy instrcutions', author: 'test@gmail.com'}, - {commit: '12', subject: 'Remove DEFAULT_START_DATE & DEFAULT_END_DATE altogether', author: 'test@gmail.com'}, + {commit: '1', subject: 'Start adding StagingDeployCash logic', authorName: 'test@gmail.com'}, + {commit: '2', subject: 'Setting up bones', authorName: 'test@gmail.com'}, + {commit: '3', subject: 'Merge pull request #337 from Expensify/francoisUpdateQbdSyncManager', authorName: 'test@gmail.com'}, + {commit: '4', subject: 'Merge pull request #336 from Expensify/andrew-pr-cla', authorName: 'test@gmail.com'}, + {commit: '5', subject: 'Update QBD Sync Manager version', authorName: 'test@gmail.com'}, + {commit: '6', subject: 'Only run CLA on PR comments or events', authorName: 'test@gmail.com'}, + {commit: '7', subject: 'Merge pull request #331 from Expensify/marcaaron-killMoment', authorName: 'test@gmail.com'}, + {commit: '8', subject: 'Merge pull request #330 from Expensify/andrew-cla-update', authorName: 'test@gmail.com'}, + {commit: '9', subject: 'Merge pull request #333 from Expensify/Rory-AddOnOffSwitchTooltip', authorName: 'test@gmail.com'}, + {commit: '10', subject: 'Setup OnOffSwitch component with tooltips', authorName: 'test@gmail.com'}, + {commit: '11', subject: 'Merge pull request #332 from Expensify/alex-mechler-patch-1', authorName: 'test@gmail.com'}, + {commit: '12', subject: 'Return to old hash-based deploy instrcutions', authorName: 'test@gmail.com'}, + {commit: '12', subject: 'Remove DEFAULT_START_DATE & DEFAULT_END_DATE altogether', authorName: 'test@gmail.com'}, ], expectedOutput: [337, 336, 331, 330, 333, 332], }, { input: [ - {commit: '1', subject: 'Merge pull request #1521 from parasharrajat/parasharrajat/pdf-render', author: 'test@gmail.com'}, - {commit: '3', subject: 'Update version to 1.0.1-470', author: 'test@gmail.com'}, - {commit: '4', subject: '[IS-1500] Updated textalignInput utility', author: 'test@gmail.com'}, - {commit: '5', subject: 'fix: set pdf width on large screens', author: 'test@gmail.com'}, + {commit: '1', subject: 'Merge pull request #1521 from parasharrajat/parasharrajat/pdf-render', authorName: 'test@gmail.com'}, + {commit: '3', subject: 'Update version to 1.0.1-470', authorName: 'test@gmail.com'}, + {commit: '4', subject: '[IS-1500] Updated textalignInput utility', authorName: 'test@gmail.com'}, + {commit: '5', subject: 'fix: set pdf width on large screens', authorName: 'test@gmail.com'}, ], expectedOutput: [1521], }, diff --git a/tests/unit/createOrUpdateStagingDeployTest.js b/tests/unit/createOrUpdateStagingDeployTest.js index 31b1a9346169..15a795842667 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.js +++ b/tests/unit/createOrUpdateStagingDeployTest.js @@ -6,7 +6,7 @@ const fns = require('date-fns'); const {vol} = require('memfs'); const path = require('path'); const CONST = require('../../.github/libs/CONST'); -const GitUtils = require('../../.github/libs/GitUtils'); +const GitUtils = require('../../.github/libs/GitUtils').default; const GithubUtils = require('../../.github/libs/GithubUtils'); const run = require('../../.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy'); diff --git a/tests/unit/markPullRequestsAsDeployedTest.js b/tests/unit/markPullRequestsAsDeployedTest.js index e9e68236ef99..81da0a5c9371 100644 --- a/tests/unit/markPullRequestsAsDeployedTest.js +++ b/tests/unit/markPullRequestsAsDeployedTest.js @@ -2,7 +2,7 @@ * @jest-environment node */ const _ = require('underscore'); -const GitUtils = require('../../.github/libs/GitUtils'); +const GitUtils = require('../../.github/libs/GitUtils').default; const GithubUtils = require('../../.github/libs/GithubUtils'); let run; diff --git a/tests/utils/bumpVersion.mjs b/tests/utils/bumpVersion.ts similarity index 98% rename from tests/utils/bumpVersion.mjs rename to tests/utils/bumpVersion.ts index 19471ee7f905..e187de508dab 100644 --- a/tests/utils/bumpVersion.mjs +++ b/tests/utils/bumpVersion.ts @@ -1,5 +1,5 @@ #!/usr/bin/env node -import {incrementVersion} from '../../.github/libs/versionUpdater.js'; +import {incrementVersion} from '../../.github/libs/versionUpdater'; const version = process.argv[2]; const level = process.argv[3]; diff --git a/tests/utils/getPreviousVersion.mjs b/tests/utils/getPreviousVersion.ts similarity index 97% rename from tests/utils/getPreviousVersion.mjs rename to tests/utils/getPreviousVersion.ts index 31ca672776fa..1eecf2fe5180 100644 --- a/tests/utils/getPreviousVersion.mjs +++ b/tests/utils/getPreviousVersion.ts @@ -1,5 +1,5 @@ #!/usr/bin/env node -import {getPreviousVersion} from '../../.github/libs/versionUpdater.js'; +import {getPreviousVersion} from '../../.github/libs/versionUpdater'; const currentVersion = process.argv[2]; const level = process.argv[3]; diff --git a/tests/utils/getPullRequestsMergedBetween.mjs b/tests/utils/getPullRequestsMergedBetween.mjs deleted file mode 100755 index 0afb1499e67f..000000000000 --- a/tests/utils/getPullRequestsMergedBetween.mjs +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env node -import GitUtils from '../../.github/libs/GitUtils.js'; - -const fromRef = process.argv[2]; -const toRef = process.argv[3]; - -/* eslint-disable no-console */ -const realConsoleLog = console.log; -console.log = () => {}; - -const output = await GitUtils.getPullRequestsMergedBetween(fromRef, toRef); - -console.log = realConsoleLog; -console.log(output); diff --git a/tests/utils/getPullRequestsMergedBetween.ts b/tests/utils/getPullRequestsMergedBetween.ts new file mode 100755 index 000000000000..2bf5a0835710 --- /dev/null +++ b/tests/utils/getPullRequestsMergedBetween.ts @@ -0,0 +1,17 @@ +#!/usr/bin/env node +import GitUtils from '../../.github/libs/GitUtils'; + +const fromRef = process.argv[2]; +const toRef = process.argv[3]; + +/* eslint-disable no-console */ +const realConsoleLog = console.log; +console.log = () => {}; + +async function main() { + const output = await GitUtils.getPullRequestsMergedBetween(fromRef, toRef); + + console.log = realConsoleLog; + console.log(output); +} +main(); From 63ce097ea2d01054d32a8197a58a550460c94b14 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 13 Feb 2024 12:53:38 +0100 Subject: [PATCH 090/225] Install ts-node --- tests/unit/CIGitLogicTest.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/CIGitLogicTest.sh b/tests/unit/CIGitLogicTest.sh index 73fd01f5c0a0..49a916d7b9dc 100755 --- a/tests/unit/CIGitLogicTest.sh +++ b/tests/unit/CIGitLogicTest.sh @@ -76,6 +76,7 @@ function bump_version { info "Bumping version..." setup_git_as_osbotify git switch main + npm i ts-node npm --no-git-tag-version version "$(ts-node "$bumpVersion" "$(print_version)" "$1")" git add package.json package-lock.json git commit -m "Update version to $(print_version)" @@ -142,6 +143,7 @@ function cherry_pick_pr { checkout_repo setup_git_as_osbotify + npm i ts-node PREVIOUS_PATCH_VERSION="$(ts-node "$getPreviousVersion" "$(print_version)" "$SEMVER_LEVEL_PATCH")" git fetch origin main staging --no-tags --shallow-exclude="$PREVIOUS_PATCH_VERSION" @@ -201,6 +203,7 @@ function deploy_production { function assert_prs_merged_between { checkout_repo + npm i ts-node output=$(ts-node "$getPullRequestsMergedBetween" "$1" "$2") info "Checking output of getPullRequestsMergedBetween $1 $2" assert_equal "$output" "$3" From a3e8a8d550800171414bb598cc3f4e3cbaf6583a Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 13 Feb 2024 12:57:55 +0100 Subject: [PATCH 091/225] rebuild gh actions --- .../javascript/authorChecklist/index.js | 33 +- .../actions/javascript/bumpVersion/index.js | 59 ++- .../createOrUpdateStagingDeploy/index.js | 376 ++++++++++-------- .../getDeployPullRequestList/index.js | 376 ++++++++++-------- .../javascript/getPreviousVersion/index.js | 57 ++- .../javascript/verifySignedCommits/index.js | 31 +- 6 files changed, 526 insertions(+), 406 deletions(-) diff --git a/.github/actions/javascript/authorChecklist/index.js b/.github/actions/javascript/authorChecklist/index.js index 137020670c10..26277693e888 100644 --- a/.github/actions/javascript/authorChecklist/index.js +++ b/.github/actions/javascript/authorChecklist/index.js @@ -1,6 +1,7 @@ /** * NOTE: This is a compiled file. DO NOT directly edit this file. */ +import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module"; /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ @@ -18686,7 +18687,7 @@ module.exports = eval("require")("encoding"); /***/ ((module) => { "use strict"; -module.exports = require("assert"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); /***/ }), @@ -18694,7 +18695,7 @@ module.exports = require("assert"); /***/ ((module) => { "use strict"; -module.exports = require("crypto"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); /***/ }), @@ -18702,7 +18703,7 @@ module.exports = require("crypto"); /***/ ((module) => { "use strict"; -module.exports = require("events"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); /***/ }), @@ -18710,7 +18711,7 @@ module.exports = require("events"); /***/ ((module) => { "use strict"; -module.exports = require("fs"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); /***/ }), @@ -18718,7 +18719,7 @@ module.exports = require("fs"); /***/ ((module) => { "use strict"; -module.exports = require("http"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); /***/ }), @@ -18726,7 +18727,7 @@ module.exports = require("http"); /***/ ((module) => { "use strict"; -module.exports = require("https"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); /***/ }), @@ -18734,7 +18735,7 @@ module.exports = require("https"); /***/ ((module) => { "use strict"; -module.exports = require("net"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); /***/ }), @@ -18742,7 +18743,7 @@ module.exports = require("net"); /***/ ((module) => { "use strict"; -module.exports = require("os"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); /***/ }), @@ -18750,7 +18751,7 @@ module.exports = require("os"); /***/ ((module) => { "use strict"; -module.exports = require("path"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); /***/ }), @@ -18758,7 +18759,7 @@ module.exports = require("path"); /***/ ((module) => { "use strict"; -module.exports = require("punycode"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("punycode"); /***/ }), @@ -18766,7 +18767,7 @@ module.exports = require("punycode"); /***/ ((module) => { "use strict"; -module.exports = require("stream"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); /***/ }), @@ -18774,7 +18775,7 @@ module.exports = require("stream"); /***/ ((module) => { "use strict"; -module.exports = require("tls"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); /***/ }), @@ -18782,7 +18783,7 @@ module.exports = require("tls"); /***/ ((module) => { "use strict"; -module.exports = require("tty"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tty"); /***/ }), @@ -18790,7 +18791,7 @@ module.exports = require("tty"); /***/ ((module) => { "use strict"; -module.exports = require("url"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); /***/ }), @@ -18798,7 +18799,7 @@ module.exports = require("url"); /***/ ((module) => { "use strict"; -module.exports = require("util"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); /***/ }), @@ -18806,7 +18807,7 @@ module.exports = require("util"); /***/ ((module) => { "use strict"; -module.exports = require("zlib"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("zlib"); /***/ }), diff --git a/.github/actions/javascript/bumpVersion/index.js b/.github/actions/javascript/bumpVersion/index.js index 1132c137061e..d17760baa91f 100644 --- a/.github/actions/javascript/bumpVersion/index.js +++ b/.github/actions/javascript/bumpVersion/index.js @@ -104,8 +104,20 @@ exports.updateiOSVersion = function updateiOSVersion(version) { /***/ }), /***/ 8007: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { +"use strict"; +__nccwpck_require__.r(__webpack_exports__); +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ "MAX_INCREMENTS": () => (/* binding */ MAX_INCREMENTS), +/* harmony export */ "SEMANTIC_VERSION_LEVELS": () => (/* binding */ SEMANTIC_VERSION_LEVELS), +/* harmony export */ "getPreviousVersion": () => (/* binding */ getPreviousVersion), +/* harmony export */ "getVersionNumberFromString": () => (/* binding */ getVersionNumberFromString), +/* harmony export */ "getVersionStringFromNumber": () => (/* binding */ getVersionStringFromNumber), +/* harmony export */ "incrementMinor": () => (/* binding */ incrementMinor), +/* harmony export */ "incrementPatch": () => (/* binding */ incrementPatch), +/* harmony export */ "incrementVersion": () => (/* binding */ incrementVersion) +/* harmony export */ }); const _ = __nccwpck_require__(5067); const SEMANTIC_VERSION_LEVELS = { @@ -173,8 +185,8 @@ const incrementPatch = (major, minor, patch) => { /** * Increments a build version * - * @param {Number} version - * @param {Number} level + * @param {String} version + * @param {String} level * @returns {String} */ const incrementVersion = (version, level) => { @@ -235,18 +247,7 @@ function getPreviousVersion(currentVersion, level) { return getVersionStringFromNumber(major, minor, patch, build - 1); } -module.exports = { - getVersionNumberFromString, - getVersionStringFromNumber, - incrementVersion, - - // For tests - MAX_INCREMENTS, - SEMANTIC_VERSION_LEVELS, - incrementMinor, - incrementPatch, - getPreviousVersion, -}; + /***/ }), @@ -5954,6 +5955,34 @@ module.exports = underscoreNodeF._; /******/ } /******/ /************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nccwpck_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __nccwpck_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ /******/ /* webpack/runtime/compat */ /******/ /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index 4116862da777..c09c1b8621f3 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -13,7 +13,7 @@ const _ = __nccwpck_require__(5067); const core = __nccwpck_require__(2186); const CONST = __nccwpck_require__(4097); const GithubUtils = __nccwpck_require__(7999); -const GitUtils = __nccwpck_require__(669); +const GitUtils = (__nccwpck_require__(1547)["default"]); async function run() { // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time @@ -177,163 +177,6 @@ CONST.APP_REPO_GIT_URL = `git@github.com:${CONST.GITHUB_OWNER}/${CONST.APP_REPO} module.exports = CONST; -/***/ }), - -/***/ 669: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -const _ = __nccwpck_require__(5067); -const {spawn, execSync} = __nccwpck_require__(2081); -const CONST = __nccwpck_require__(4097); -const sanitizeStringForJSONParse = __nccwpck_require__(9338); -const {getPreviousVersion, SEMANTIC_VERSION_LEVELS} = __nccwpck_require__(8007); - -/** - * @param {String} tag - * @param {String} [shallowExcludeTag] when fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) - */ -function fetchTag(tag, shallowExcludeTag = '') { - let shouldRetry = true; - let needsRepack = false; - while (shouldRetry) { - try { - let command = ''; - if (needsRepack) { - // We have seen some scenarios where this fixes the git fetch. - // Why? Who knows... https://github.com/Expensify/App/pull/31459 - command = 'git repack -d'; - console.log(`Running command: ${command}`); - execSync(command); - } - - command = `git fetch origin tag ${tag} --no-tags`; - - // Note that this condition is only ever NOT true in the 1.0.0-0 edge case - if (shallowExcludeTag && shallowExcludeTag !== tag) { - command += ` --shallow-exclude=${shallowExcludeTag}`; - } - - console.log(`Running command: ${command}`); - execSync(command); - shouldRetry = false; - } catch (e) { - console.error(e); - if (!needsRepack) { - console.log('Attempting to repack and retry...'); - needsRepack = true; - } else { - console.error("Repack didn't help, giving up..."); - shouldRetry = false; - } - } - } -} - -/** - * Get merge logs between two tags (inclusive) as a JavaScript object. - * - * @param {String} fromTag - * @param {String} toTag - * @returns {Promise>>} - */ -function getCommitHistoryAsJSON(fromTag, toTag) { - // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousVersion(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); - fetchTag(fromTag, previousPatchVersion); - fetchTag(toTag, previousPatchVersion); - - console.log('Getting pull requests merged between the following tags:', fromTag, toTag); - return new Promise((resolve, reject) => { - let stdout = ''; - let stderr = ''; - const args = ['log', '--format={"commit": "%H", "authorName": "%an", "subject": "%s"},', `${fromTag}...${toTag}`]; - console.log(`Running command: git ${args.join(' ')}`); - const spawnedProcess = spawn('git', args); - spawnedProcess.on('message', console.log); - spawnedProcess.stdout.on('data', (chunk) => { - console.log(chunk.toString()); - stdout += chunk.toString(); - }); - spawnedProcess.stderr.on('data', (chunk) => { - console.error(chunk.toString()); - stderr += chunk.toString(); - }); - spawnedProcess.on('close', (code) => { - if (code !== 0) { - return reject(new Error(`${stderr}`)); - } - - resolve(stdout); - }); - spawnedProcess.on('error', (err) => reject(err)); - }).then((stdout) => { - // Sanitize just the text within commit subjects as that's the only potentially un-parseable text. - const sanitizedOutput = stdout.replace(/(?<="subject": ").*?(?="})/g, (subject) => sanitizeStringForJSONParse(subject)); - - // Then remove newlines, format as JSON and convert to a proper JS object - const json = `[${sanitizedOutput}]`.replace(/(\r\n|\n|\r)/gm, '').replace('},]', '}]'); - - return JSON.parse(json); - }); -} - -/** - * Parse merged PRs, excluding those from irrelevant branches. - * - * @param {Array>} commits - * @returns {Array} - */ -function getValidMergedPRs(commits) { - const mergedPRs = new Set(); - _.each(commits, (commit) => { - const author = commit.authorName; - if (author === CONST.OS_BOTIFY) { - return; - } - - const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); - if (!_.isArray(match) || match.length < 2) { - return; - } - - const pr = Number.parseInt(match[1], 10); - if (mergedPRs.has(pr)) { - // If a PR shows up in the log twice, that means that the PR was deployed in the previous checklist. - // That also means that we don't want to include it in the current checklist, so we remove it now. - mergedPRs.delete(pr); - return; - } - - mergedPRs.add(pr); - }); - - return Array.from(mergedPRs); -} - -/** - * Takes in two git tags and returns a list of PR numbers of all PRs merged between those two tags - * - * @param {String} fromTag - * @param {String} toTag - * @returns {Promise>} – Pull request numbers - */ -async function getPullRequestsMergedBetween(fromTag, toTag) { - console.log(`Looking for commits made between ${fromTag} and ${toTag}...`); - const commitList = await getCommitHistoryAsJSON(fromTag, toTag); - console.log(`Commits made between ${fromTag} and ${toTag}:`, commitList); - - // Find which commit messages correspond to merged PR's - const pullRequestNumbers = getValidMergedPRs(commitList).sort((a, b) => a - b); - console.log(`List of pull requests merged between ${fromTag} and ${toTag}`, pullRequestNumbers); - return pullRequestNumbers; -} - -module.exports = { - getValidMergedPRs, - getPullRequestsMergedBetween, -}; - - /***/ }), /***/ 7999: @@ -909,8 +752,20 @@ module.exports = function (inputString) { /***/ }), /***/ 8007: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { +"use strict"; +__nccwpck_require__.r(__webpack_exports__); +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ "MAX_INCREMENTS": () => (/* binding */ MAX_INCREMENTS), +/* harmony export */ "SEMANTIC_VERSION_LEVELS": () => (/* binding */ SEMANTIC_VERSION_LEVELS), +/* harmony export */ "getPreviousVersion": () => (/* binding */ getPreviousVersion), +/* harmony export */ "getVersionNumberFromString": () => (/* binding */ getVersionNumberFromString), +/* harmony export */ "getVersionStringFromNumber": () => (/* binding */ getVersionStringFromNumber), +/* harmony export */ "incrementMinor": () => (/* binding */ incrementMinor), +/* harmony export */ "incrementPatch": () => (/* binding */ incrementPatch), +/* harmony export */ "incrementVersion": () => (/* binding */ incrementVersion) +/* harmony export */ }); const _ = __nccwpck_require__(5067); const SEMANTIC_VERSION_LEVELS = { @@ -978,8 +833,8 @@ const incrementPatch = (major, minor, patch) => { /** * Increments a build version * - * @param {Number} version - * @param {Number} level + * @param {String} version + * @param {String} level * @returns {String} */ const incrementVersion = (version, level) => { @@ -1040,18 +895,7 @@ function getPreviousVersion(currentVersion, level) { return getVersionStringFromNumber(major, minor, patch, build - 1); } -module.exports = { - getVersionNumberFromString, - getVersionStringFromNumber, - incrementVersion, - - // For tests - MAX_INCREMENTS, - SEMANTIC_VERSION_LEVELS, - incrementMinor, - incrementPatch, - getPreviousVersion, -}; + /***/ }), @@ -16836,6 +16680,164 @@ function wrappy (fn, cb) { } +/***/ }), + +/***/ 1547: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const child_process_1 = __nccwpck_require__(2081); +const CONST = __importStar(__nccwpck_require__(4097)); +const sanitizeStringForJSONParse_1 = __importDefault(__nccwpck_require__(9338)); +const VERSION_UPDATER = __importStar(__nccwpck_require__(8007)); +/** + * When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) + */ +function fetchTag(tag, shallowExcludeTag = '') { + let shouldRetry = true; + let needsRepack = false; + while (shouldRetry) { + try { + let command = ''; + if (needsRepack) { + // We have seen some scenarios where this fixes the git fetch. + // Why? Who knows... https://github.com/Expensify/App/pull/31459 + command = 'git repack -d'; + console.log(`Running command: ${command}`); + (0, child_process_1.execSync)(command); + } + command = `git fetch origin tag ${tag} --no-tags`; + // Note that this condition is only ever NOT true in the 1.0.0-0 edge case + if (shallowExcludeTag && shallowExcludeTag !== tag) { + command += ` --shallow-exclude=${shallowExcludeTag}`; + } + console.log(`Running command: ${command}`); + (0, child_process_1.execSync)(command); + shouldRetry = false; + } + catch (e) { + console.error(e); + if (!needsRepack) { + console.log('Attempting to repack and retry...'); + needsRepack = true; + } + else { + console.error("Repack didn't help, giving up..."); + shouldRetry = false; + } + } + } +} +/** + * Get merge logs between two tags (inclusive) as a JavaScript object. + */ +function getCommitHistoryAsJSON(fromTag, toTag) { + // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = VERSION_UPDATER.getPreviousVersion(fromTag, VERSION_UPDATER.SEMANTIC_VERSION_LEVELS.PATCH); + fetchTag(fromTag, previousPatchVersion); + fetchTag(toTag, previousPatchVersion); + console.log('Getting pull requests merged between the following tags:', fromTag, toTag); + return new Promise((resolve, reject) => { + let stdout = ''; + let stderr = ''; + const args = ['log', '--format={"commit": "%H", "authorName": "%an", "subject": "%s"},', `${fromTag}...${toTag}`]; + console.log(`Running command: git ${args.join(' ')}`); + const spawnedProcess = (0, child_process_1.spawn)('git', args); + spawnedProcess.on('message', console.log); + spawnedProcess.stdout.on('data', (chunk) => { + console.log(chunk.toString()); + stdout += chunk.toString(); + }); + spawnedProcess.stderr.on('data', (chunk) => { + console.error(chunk.toString()); + stderr += chunk.toString(); + }); + spawnedProcess.on('close', (code) => { + if (code !== 0) { + return reject(new Error(`${stderr}`)); + } + resolve(stdout); + }); + spawnedProcess.on('error', (err) => reject(err)); + }).then((stdout) => { + // Sanitize just the text within commit subjects as that's the only potentially un-parseable text. + const sanitizedOutput = stdout.replace(/(?<="subject": ").*?(?="})/g, (subject) => (0, sanitizeStringForJSONParse_1.default)(subject)); + // Then remove newlines, format as JSON and convert to a proper JS object + const json = `[${sanitizedOutput}]`.replace(/(\r\n|\n|\r)/gm, '').replace('},]', '}]'); + return JSON.parse(json); + }); +} +/** + * Parse merged PRs, excluding those from irrelevant branches. + */ +function getValidMergedPRs(commits) { + const mergedPRs = new Set(); + commits.forEach((commit) => { + const author = commit.authorName; + if (author === CONST.OS_BOTIFY) { + return; + } + const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); + if (!Array.isArray(match) || match.length < 2) { + return; + } + const pr = Number.parseInt(match[1], 10); + if (mergedPRs.has(pr)) { + // If a PR shows up in the log twice, that means that the PR was deployed in the previous checklist. + // That also means that we don't want to include it in the current checklist, so we remove it now. + mergedPRs.delete(pr); + return; + } + mergedPRs.add(pr); + }); + return Array.from(mergedPRs); +} +/** + * Takes in two git tags and returns a list of PR numbers of all PRs merged between those two tags + */ +async function getPullRequestsMergedBetween(fromTag, toTag) { + console.log(`Looking for commits made between ${fromTag} and ${toTag}...`); + const commitList = await getCommitHistoryAsJSON(fromTag, toTag); + console.log(`Commits made between ${fromTag} and ${toTag}:`, commitList); + // Find which commit messages correspond to merged PR's + const pullRequestNumbers = getValidMergedPRs(commitList).sort((a, b) => a - b); + console.log(`List of pull requests merged between ${fromTag} and ${toTag}`, pullRequestNumbers); + return pullRequestNumbers; +} +exports["default"] = { + getValidMergedPRs, + getPullRequestsMergedBetween, +}; + + /***/ }), /***/ 2877: @@ -19226,6 +19228,34 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ } /******/ /************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nccwpck_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __nccwpck_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ /******/ /* webpack/runtime/compat */ /******/ /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index ac5d0e26998a..837dfd82adf1 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -11,7 +11,7 @@ const _ = __nccwpck_require__(5067); const core = __nccwpck_require__(2186); const github = __nccwpck_require__(5438); const ActionUtils = __nccwpck_require__(970); -const GitUtils = __nccwpck_require__(669); +const GitUtils = (__nccwpck_require__(1547)["default"]); const GithubUtils = __nccwpck_require__(7999); async function run() { @@ -120,163 +120,6 @@ CONST.APP_REPO_GIT_URL = `git@github.com:${CONST.GITHUB_OWNER}/${CONST.APP_REPO} module.exports = CONST; -/***/ }), - -/***/ 669: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -const _ = __nccwpck_require__(5067); -const {spawn, execSync} = __nccwpck_require__(2081); -const CONST = __nccwpck_require__(4097); -const sanitizeStringForJSONParse = __nccwpck_require__(9338); -const {getPreviousVersion, SEMANTIC_VERSION_LEVELS} = __nccwpck_require__(8007); - -/** - * @param {String} tag - * @param {String} [shallowExcludeTag] when fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) - */ -function fetchTag(tag, shallowExcludeTag = '') { - let shouldRetry = true; - let needsRepack = false; - while (shouldRetry) { - try { - let command = ''; - if (needsRepack) { - // We have seen some scenarios where this fixes the git fetch. - // Why? Who knows... https://github.com/Expensify/App/pull/31459 - command = 'git repack -d'; - console.log(`Running command: ${command}`); - execSync(command); - } - - command = `git fetch origin tag ${tag} --no-tags`; - - // Note that this condition is only ever NOT true in the 1.0.0-0 edge case - if (shallowExcludeTag && shallowExcludeTag !== tag) { - command += ` --shallow-exclude=${shallowExcludeTag}`; - } - - console.log(`Running command: ${command}`); - execSync(command); - shouldRetry = false; - } catch (e) { - console.error(e); - if (!needsRepack) { - console.log('Attempting to repack and retry...'); - needsRepack = true; - } else { - console.error("Repack didn't help, giving up..."); - shouldRetry = false; - } - } - } -} - -/** - * Get merge logs between two tags (inclusive) as a JavaScript object. - * - * @param {String} fromTag - * @param {String} toTag - * @returns {Promise>>} - */ -function getCommitHistoryAsJSON(fromTag, toTag) { - // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history - const previousPatchVersion = getPreviousVersion(fromTag, SEMANTIC_VERSION_LEVELS.PATCH); - fetchTag(fromTag, previousPatchVersion); - fetchTag(toTag, previousPatchVersion); - - console.log('Getting pull requests merged between the following tags:', fromTag, toTag); - return new Promise((resolve, reject) => { - let stdout = ''; - let stderr = ''; - const args = ['log', '--format={"commit": "%H", "authorName": "%an", "subject": "%s"},', `${fromTag}...${toTag}`]; - console.log(`Running command: git ${args.join(' ')}`); - const spawnedProcess = spawn('git', args); - spawnedProcess.on('message', console.log); - spawnedProcess.stdout.on('data', (chunk) => { - console.log(chunk.toString()); - stdout += chunk.toString(); - }); - spawnedProcess.stderr.on('data', (chunk) => { - console.error(chunk.toString()); - stderr += chunk.toString(); - }); - spawnedProcess.on('close', (code) => { - if (code !== 0) { - return reject(new Error(`${stderr}`)); - } - - resolve(stdout); - }); - spawnedProcess.on('error', (err) => reject(err)); - }).then((stdout) => { - // Sanitize just the text within commit subjects as that's the only potentially un-parseable text. - const sanitizedOutput = stdout.replace(/(?<="subject": ").*?(?="})/g, (subject) => sanitizeStringForJSONParse(subject)); - - // Then remove newlines, format as JSON and convert to a proper JS object - const json = `[${sanitizedOutput}]`.replace(/(\r\n|\n|\r)/gm, '').replace('},]', '}]'); - - return JSON.parse(json); - }); -} - -/** - * Parse merged PRs, excluding those from irrelevant branches. - * - * @param {Array>} commits - * @returns {Array} - */ -function getValidMergedPRs(commits) { - const mergedPRs = new Set(); - _.each(commits, (commit) => { - const author = commit.authorName; - if (author === CONST.OS_BOTIFY) { - return; - } - - const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); - if (!_.isArray(match) || match.length < 2) { - return; - } - - const pr = Number.parseInt(match[1], 10); - if (mergedPRs.has(pr)) { - // If a PR shows up in the log twice, that means that the PR was deployed in the previous checklist. - // That also means that we don't want to include it in the current checklist, so we remove it now. - mergedPRs.delete(pr); - return; - } - - mergedPRs.add(pr); - }); - - return Array.from(mergedPRs); -} - -/** - * Takes in two git tags and returns a list of PR numbers of all PRs merged between those two tags - * - * @param {String} fromTag - * @param {String} toTag - * @returns {Promise>} – Pull request numbers - */ -async function getPullRequestsMergedBetween(fromTag, toTag) { - console.log(`Looking for commits made between ${fromTag} and ${toTag}...`); - const commitList = await getCommitHistoryAsJSON(fromTag, toTag); - console.log(`Commits made between ${fromTag} and ${toTag}:`, commitList); - - // Find which commit messages correspond to merged PR's - const pullRequestNumbers = getValidMergedPRs(commitList).sort((a, b) => a - b); - console.log(`List of pull requests merged between ${fromTag} and ${toTag}`, pullRequestNumbers); - return pullRequestNumbers; -} - -module.exports = { - getValidMergedPRs, - getPullRequestsMergedBetween, -}; - - /***/ }), /***/ 7999: @@ -852,8 +695,20 @@ module.exports = function (inputString) { /***/ }), /***/ 8007: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { +"use strict"; +__nccwpck_require__.r(__webpack_exports__); +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ "MAX_INCREMENTS": () => (/* binding */ MAX_INCREMENTS), +/* harmony export */ "SEMANTIC_VERSION_LEVELS": () => (/* binding */ SEMANTIC_VERSION_LEVELS), +/* harmony export */ "getPreviousVersion": () => (/* binding */ getPreviousVersion), +/* harmony export */ "getVersionNumberFromString": () => (/* binding */ getVersionNumberFromString), +/* harmony export */ "getVersionStringFromNumber": () => (/* binding */ getVersionStringFromNumber), +/* harmony export */ "incrementMinor": () => (/* binding */ incrementMinor), +/* harmony export */ "incrementPatch": () => (/* binding */ incrementPatch), +/* harmony export */ "incrementVersion": () => (/* binding */ incrementVersion) +/* harmony export */ }); const _ = __nccwpck_require__(5067); const SEMANTIC_VERSION_LEVELS = { @@ -921,8 +776,8 @@ const incrementPatch = (major, minor, patch) => { /** * Increments a build version * - * @param {Number} version - * @param {Number} level + * @param {String} version + * @param {String} level * @returns {String} */ const incrementVersion = (version, level) => { @@ -983,18 +838,7 @@ function getPreviousVersion(currentVersion, level) { return getVersionStringFromNumber(major, minor, patch, build - 1); } -module.exports = { - getVersionNumberFromString, - getVersionStringFromNumber, - incrementVersion, - - // For tests - MAX_INCREMENTS, - SEMANTIC_VERSION_LEVELS, - incrementMinor, - incrementPatch, - getPreviousVersion, -}; + /***/ }), @@ -14068,6 +13912,164 @@ function wrappy (fn, cb) { } +/***/ }), + +/***/ 1547: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const child_process_1 = __nccwpck_require__(2081); +const CONST = __importStar(__nccwpck_require__(4097)); +const sanitizeStringForJSONParse_1 = __importDefault(__nccwpck_require__(9338)); +const VERSION_UPDATER = __importStar(__nccwpck_require__(8007)); +/** + * When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) + */ +function fetchTag(tag, shallowExcludeTag = '') { + let shouldRetry = true; + let needsRepack = false; + while (shouldRetry) { + try { + let command = ''; + if (needsRepack) { + // We have seen some scenarios where this fixes the git fetch. + // Why? Who knows... https://github.com/Expensify/App/pull/31459 + command = 'git repack -d'; + console.log(`Running command: ${command}`); + (0, child_process_1.execSync)(command); + } + command = `git fetch origin tag ${tag} --no-tags`; + // Note that this condition is only ever NOT true in the 1.0.0-0 edge case + if (shallowExcludeTag && shallowExcludeTag !== tag) { + command += ` --shallow-exclude=${shallowExcludeTag}`; + } + console.log(`Running command: ${command}`); + (0, child_process_1.execSync)(command); + shouldRetry = false; + } + catch (e) { + console.error(e); + if (!needsRepack) { + console.log('Attempting to repack and retry...'); + needsRepack = true; + } + else { + console.error("Repack didn't help, giving up..."); + shouldRetry = false; + } + } + } +} +/** + * Get merge logs between two tags (inclusive) as a JavaScript object. + */ +function getCommitHistoryAsJSON(fromTag, toTag) { + // Fetch tags, exclude commits reachable from the previous patch version (i.e: previous checklist), so that we don't have to fetch the full history + const previousPatchVersion = VERSION_UPDATER.getPreviousVersion(fromTag, VERSION_UPDATER.SEMANTIC_VERSION_LEVELS.PATCH); + fetchTag(fromTag, previousPatchVersion); + fetchTag(toTag, previousPatchVersion); + console.log('Getting pull requests merged between the following tags:', fromTag, toTag); + return new Promise((resolve, reject) => { + let stdout = ''; + let stderr = ''; + const args = ['log', '--format={"commit": "%H", "authorName": "%an", "subject": "%s"},', `${fromTag}...${toTag}`]; + console.log(`Running command: git ${args.join(' ')}`); + const spawnedProcess = (0, child_process_1.spawn)('git', args); + spawnedProcess.on('message', console.log); + spawnedProcess.stdout.on('data', (chunk) => { + console.log(chunk.toString()); + stdout += chunk.toString(); + }); + spawnedProcess.stderr.on('data', (chunk) => { + console.error(chunk.toString()); + stderr += chunk.toString(); + }); + spawnedProcess.on('close', (code) => { + if (code !== 0) { + return reject(new Error(`${stderr}`)); + } + resolve(stdout); + }); + spawnedProcess.on('error', (err) => reject(err)); + }).then((stdout) => { + // Sanitize just the text within commit subjects as that's the only potentially un-parseable text. + const sanitizedOutput = stdout.replace(/(?<="subject": ").*?(?="})/g, (subject) => (0, sanitizeStringForJSONParse_1.default)(subject)); + // Then remove newlines, format as JSON and convert to a proper JS object + const json = `[${sanitizedOutput}]`.replace(/(\r\n|\n|\r)/gm, '').replace('},]', '}]'); + return JSON.parse(json); + }); +} +/** + * Parse merged PRs, excluding those from irrelevant branches. + */ +function getValidMergedPRs(commits) { + const mergedPRs = new Set(); + commits.forEach((commit) => { + const author = commit.authorName; + if (author === CONST.OS_BOTIFY) { + return; + } + const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-staging)/); + if (!Array.isArray(match) || match.length < 2) { + return; + } + const pr = Number.parseInt(match[1], 10); + if (mergedPRs.has(pr)) { + // If a PR shows up in the log twice, that means that the PR was deployed in the previous checklist. + // That also means that we don't want to include it in the current checklist, so we remove it now. + mergedPRs.delete(pr); + return; + } + mergedPRs.add(pr); + }); + return Array.from(mergedPRs); +} +/** + * Takes in two git tags and returns a list of PR numbers of all PRs merged between those two tags + */ +async function getPullRequestsMergedBetween(fromTag, toTag) { + console.log(`Looking for commits made between ${fromTag} and ${toTag}...`); + const commitList = await getCommitHistoryAsJSON(fromTag, toTag); + console.log(`Commits made between ${fromTag} and ${toTag}:`, commitList); + // Find which commit messages correspond to merged PR's + const pullRequestNumbers = getValidMergedPRs(commitList).sort((a, b) => a - b); + console.log(`List of pull requests merged between ${fromTag} and ${toTag}`, pullRequestNumbers); + return pullRequestNumbers; +} +exports["default"] = { + getValidMergedPRs, + getPullRequestsMergedBetween, +}; + + /***/ }), /***/ 2877: @@ -16430,6 +16432,34 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ } /******/ /************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nccwpck_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __nccwpck_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ /******/ /* webpack/runtime/compat */ /******/ /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; diff --git a/.github/actions/javascript/getPreviousVersion/index.js b/.github/actions/javascript/getPreviousVersion/index.js index 63498e68fcdf..545f4472ec72 100644 --- a/.github/actions/javascript/getPreviousVersion/index.js +++ b/.github/actions/javascript/getPreviousVersion/index.js @@ -5,8 +5,20 @@ /******/ var __webpack_modules__ = ({ /***/ 7: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { +"use strict"; +__nccwpck_require__.r(__webpack_exports__); +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ "MAX_INCREMENTS": () => (/* binding */ MAX_INCREMENTS), +/* harmony export */ "SEMANTIC_VERSION_LEVELS": () => (/* binding */ SEMANTIC_VERSION_LEVELS), +/* harmony export */ "getPreviousVersion": () => (/* binding */ getPreviousVersion), +/* harmony export */ "getVersionNumberFromString": () => (/* binding */ getVersionNumberFromString), +/* harmony export */ "getVersionStringFromNumber": () => (/* binding */ getVersionStringFromNumber), +/* harmony export */ "incrementMinor": () => (/* binding */ incrementMinor), +/* harmony export */ "incrementPatch": () => (/* binding */ incrementPatch), +/* harmony export */ "incrementVersion": () => (/* binding */ incrementVersion) +/* harmony export */ }); const _ = __nccwpck_require__(67); const SEMANTIC_VERSION_LEVELS = { @@ -74,8 +86,8 @@ const incrementPatch = (major, minor, patch) => { /** * Increments a build version * - * @param {Number} version - * @param {Number} level + * @param {String} version + * @param {String} level * @returns {String} */ const incrementVersion = (version, level) => { @@ -136,18 +148,7 @@ function getPreviousVersion(currentVersion, level) { return getVersionStringFromNumber(major, minor, patch, build - 1); } -module.exports = { - getVersionNumberFromString, - getVersionStringFromNumber, - incrementVersion, - // For tests - MAX_INCREMENTS, - SEMANTIC_VERSION_LEVELS, - incrementMinor, - incrementPatch, - getPreviousVersion, -}; /***/ }), @@ -5143,6 +5144,34 @@ module.exports = underscoreNodeF._; /******/ } /******/ /************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nccwpck_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __nccwpck_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ /******/ /* webpack/runtime/compat */ /******/ /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; diff --git a/.github/actions/javascript/verifySignedCommits/index.js b/.github/actions/javascript/verifySignedCommits/index.js index 61b0e2063d30..f32bda6bae1e 100644 --- a/.github/actions/javascript/verifySignedCommits/index.js +++ b/.github/actions/javascript/verifySignedCommits/index.js @@ -1,6 +1,7 @@ /** * NOTE: This is a compiled file. DO NOT directly edit this file. */ +import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module"; /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ @@ -13649,7 +13650,7 @@ module.exports = eval("require")("encoding"); /***/ ((module) => { "use strict"; -module.exports = require("assert"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); /***/ }), @@ -13657,7 +13658,7 @@ module.exports = require("assert"); /***/ ((module) => { "use strict"; -module.exports = require("crypto"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); /***/ }), @@ -13665,7 +13666,7 @@ module.exports = require("crypto"); /***/ ((module) => { "use strict"; -module.exports = require("events"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); /***/ }), @@ -13673,7 +13674,7 @@ module.exports = require("events"); /***/ ((module) => { "use strict"; -module.exports = require("fs"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); /***/ }), @@ -13681,7 +13682,7 @@ module.exports = require("fs"); /***/ ((module) => { "use strict"; -module.exports = require("http"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); /***/ }), @@ -13689,7 +13690,7 @@ module.exports = require("http"); /***/ ((module) => { "use strict"; -module.exports = require("https"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); /***/ }), @@ -13697,7 +13698,7 @@ module.exports = require("https"); /***/ ((module) => { "use strict"; -module.exports = require("net"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); /***/ }), @@ -13705,7 +13706,7 @@ module.exports = require("net"); /***/ ((module) => { "use strict"; -module.exports = require("os"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); /***/ }), @@ -13713,7 +13714,7 @@ module.exports = require("os"); /***/ ((module) => { "use strict"; -module.exports = require("path"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); /***/ }), @@ -13721,7 +13722,7 @@ module.exports = require("path"); /***/ ((module) => { "use strict"; -module.exports = require("punycode"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("punycode"); /***/ }), @@ -13729,7 +13730,7 @@ module.exports = require("punycode"); /***/ ((module) => { "use strict"; -module.exports = require("stream"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); /***/ }), @@ -13737,7 +13738,7 @@ module.exports = require("stream"); /***/ ((module) => { "use strict"; -module.exports = require("tls"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); /***/ }), @@ -13745,7 +13746,7 @@ module.exports = require("tls"); /***/ ((module) => { "use strict"; -module.exports = require("url"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); /***/ }), @@ -13753,7 +13754,7 @@ module.exports = require("url"); /***/ ((module) => { "use strict"; -module.exports = require("util"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); /***/ }), @@ -13761,7 +13762,7 @@ module.exports = require("util"); /***/ ((module) => { "use strict"; -module.exports = require("zlib"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("zlib"); /***/ }), From a2663b81cc872df4c5d4eb684d1525e737dd062b Mon Sep 17 00:00:00 2001 From: Yauheni Date: Tue, 13 Feb 2024 13:39:05 +0100 Subject: [PATCH 092/225] Update condition for UserListItem icons --- src/components/SelectionList/UserListItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index c9fb9a92f9fb..3fd61177aa6a 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -21,7 +21,7 @@ function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style <> {!!item.icons && ( <> - {item.shouldShowSubscript ?? shouldUseOnySubscriptAvatar ? ( + {shouldUseOnySubscriptAvatar || item.shouldShowSubscript ? ( Date: Tue, 13 Feb 2024 14:20:48 +0100 Subject: [PATCH 093/225] GH actions fix --- .../javascript/authorChecklist/index.js | 33 +++++++++---------- .../javascript/verifySignedCommits/index.js | 31 +++++++++-------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/.github/actions/javascript/authorChecklist/index.js b/.github/actions/javascript/authorChecklist/index.js index 26277693e888..137020670c10 100644 --- a/.github/actions/javascript/authorChecklist/index.js +++ b/.github/actions/javascript/authorChecklist/index.js @@ -1,7 +1,6 @@ /** * NOTE: This is a compiled file. DO NOT directly edit this file. */ -import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module"; /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ @@ -18687,7 +18686,7 @@ module.exports = eval("require")("encoding"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); +module.exports = require("assert"); /***/ }), @@ -18695,7 +18694,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); +module.exports = require("crypto"); /***/ }), @@ -18703,7 +18702,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); +module.exports = require("events"); /***/ }), @@ -18711,7 +18710,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); +module.exports = require("fs"); /***/ }), @@ -18719,7 +18718,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); +module.exports = require("http"); /***/ }), @@ -18727,7 +18726,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); +module.exports = require("https"); /***/ }), @@ -18735,7 +18734,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); +module.exports = require("net"); /***/ }), @@ -18743,7 +18742,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); +module.exports = require("os"); /***/ }), @@ -18751,7 +18750,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); +module.exports = require("path"); /***/ }), @@ -18759,7 +18758,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("punycode"); +module.exports = require("punycode"); /***/ }), @@ -18767,7 +18766,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("punycode"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); +module.exports = require("stream"); /***/ }), @@ -18775,7 +18774,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); +module.exports = require("tls"); /***/ }), @@ -18783,7 +18782,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tty"); +module.exports = require("tty"); /***/ }), @@ -18791,7 +18790,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tty"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); +module.exports = require("url"); /***/ }), @@ -18799,7 +18798,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); +module.exports = require("util"); /***/ }), @@ -18807,7 +18806,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("zlib"); +module.exports = require("zlib"); /***/ }), diff --git a/.github/actions/javascript/verifySignedCommits/index.js b/.github/actions/javascript/verifySignedCommits/index.js index f32bda6bae1e..61b0e2063d30 100644 --- a/.github/actions/javascript/verifySignedCommits/index.js +++ b/.github/actions/javascript/verifySignedCommits/index.js @@ -1,7 +1,6 @@ /** * NOTE: This is a compiled file. DO NOT directly edit this file. */ -import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module"; /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ @@ -13650,7 +13649,7 @@ module.exports = eval("require")("encoding"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); +module.exports = require("assert"); /***/ }), @@ -13658,7 +13657,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); +module.exports = require("crypto"); /***/ }), @@ -13666,7 +13665,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); +module.exports = require("events"); /***/ }), @@ -13674,7 +13673,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); +module.exports = require("fs"); /***/ }), @@ -13682,7 +13681,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); +module.exports = require("http"); /***/ }), @@ -13690,7 +13689,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); +module.exports = require("https"); /***/ }), @@ -13698,7 +13697,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); +module.exports = require("net"); /***/ }), @@ -13706,7 +13705,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); +module.exports = require("os"); /***/ }), @@ -13714,7 +13713,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); +module.exports = require("path"); /***/ }), @@ -13722,7 +13721,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("punycode"); +module.exports = require("punycode"); /***/ }), @@ -13730,7 +13729,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("punycode"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); +module.exports = require("stream"); /***/ }), @@ -13738,7 +13737,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); +module.exports = require("tls"); /***/ }), @@ -13746,7 +13745,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); +module.exports = require("url"); /***/ }), @@ -13754,7 +13753,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); +module.exports = require("util"); /***/ }), @@ -13762,7 +13761,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); /***/ ((module) => { "use strict"; -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("zlib"); +module.exports = require("zlib"); /***/ }), From 43e97c2bf8fc52ce90292f88447bf2f15b0d6b58 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 13 Feb 2024 14:30:17 +0100 Subject: [PATCH 094/225] Change method of ts-node install --- .github/workflows/test.yml | 3 +++ tests/unit/CIGitLogicTest.sh | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6540a0fdd583..9e4891b4662e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,5 +62,8 @@ jobs: - name: Setup Node uses: ./.github/actions/composite/setupNode + - name: Install ts-node + uses: npm i ts-node + - name: Test CI git logic run: tests/unit/CIGitLogicTest.sh diff --git a/tests/unit/CIGitLogicTest.sh b/tests/unit/CIGitLogicTest.sh index 49a916d7b9dc..73fd01f5c0a0 100755 --- a/tests/unit/CIGitLogicTest.sh +++ b/tests/unit/CIGitLogicTest.sh @@ -76,7 +76,6 @@ function bump_version { info "Bumping version..." setup_git_as_osbotify git switch main - npm i ts-node npm --no-git-tag-version version "$(ts-node "$bumpVersion" "$(print_version)" "$1")" git add package.json package-lock.json git commit -m "Update version to $(print_version)" @@ -143,7 +142,6 @@ function cherry_pick_pr { checkout_repo setup_git_as_osbotify - npm i ts-node PREVIOUS_PATCH_VERSION="$(ts-node "$getPreviousVersion" "$(print_version)" "$SEMVER_LEVEL_PATCH")" git fetch origin main staging --no-tags --shallow-exclude="$PREVIOUS_PATCH_VERSION" @@ -203,7 +201,6 @@ function deploy_production { function assert_prs_merged_between { checkout_repo - npm i ts-node output=$(ts-node "$getPullRequestsMergedBetween" "$1" "$2") info "Checking output of getPullRequestsMergedBetween $1 $2" assert_equal "$output" "$3" From 7fd32965c786992a7e87651eee8455f9f04da9e6 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 13 Feb 2024 14:50:33 +0100 Subject: [PATCH 095/225] Fix test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e4891b4662e..4f0203ec5e09 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,7 +63,7 @@ jobs: uses: ./.github/actions/composite/setupNode - name: Install ts-node - uses: npm i ts-node + run: npm i ts-node - name: Test CI git logic run: tests/unit/CIGitLogicTest.sh From 381320cee46c0054b8450a4a5e2a7e1d41473b56 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 13 Feb 2024 14:59:54 +0100 Subject: [PATCH 096/225] install ts-node globally --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f0203ec5e09..bdc14950a337 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,7 +63,7 @@ jobs: uses: ./.github/actions/composite/setupNode - name: Install ts-node - run: npm i ts-node + run: npm i -g ts-node - name: Test CI git logic run: tests/unit/CIGitLogicTest.sh From 6a94f3e92e97ec79c928bc4779b65b735b86c7f7 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 13 Feb 2024 15:11:21 +0100 Subject: [PATCH 097/225] Display correct name and avatar for notificstions --- src/libs/ReportUtils.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 26280f95447d..e33421206a60 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1600,6 +1600,15 @@ function getPersonalDetailsForAccountID(accountID: number): Partial Date: Tue, 13 Feb 2024 17:05:25 +0100 Subject: [PATCH 098/225] use right avatar --- src/libs/UserUtils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index 52c3ecef156c..3d89893a06a9 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -98,6 +98,9 @@ function getDefaultAvatar(accountID = -1, avatarURL?: string): IconAsset { if (Number(accountID) === CONST.ACCOUNT_ID.CONCIERGE) { return ConciergeAvatar; } + if (Number(accountID) === CONST.ACCOUNT_ID.NOTIFICATIONS) { + return NotificationsAvatar; + } // There are 24 possible default avatars, so we choose which one this user has based // on a simple modulo operation of their login number. Note that Avatar count starts at 1. From 34309fea6afc2973cc92ef6481d9531cc21ac3a7 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Tue, 13 Feb 2024 17:08:03 +0100 Subject: [PATCH 099/225] Remove shouldUseOnySubscriptAvatar --- src/components/SelectionList/BaseListItem.tsx | 2 -- src/components/SelectionList/BaseSelectionList.tsx | 2 -- src/components/SelectionList/UserListItem.tsx | 4 ++-- src/components/SelectionList/types.ts | 7 ------- src/pages/SearchPage/index.js | 1 - 5 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index 553fee4887db..0290d7bfda98 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -25,7 +25,6 @@ function BaseListItem({ onDismissError = () => {}, rightHandSideComponent, keyForList, - shouldUseOnySubscriptAvatar = true, }: BaseListItemProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -118,7 +117,6 @@ function BaseListItem({ showTooltip={showTooltip} isFocused={isFocused} isHovered={hovered} - shouldUseOnySubscriptAvatar={shouldUseOnySubscriptAvatar} /> {!canSelectMultiple && item.isSelected && !rightHandSideComponent && ( diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 3a052bc2be1e..c77e244b4c92 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -60,7 +60,6 @@ function BaseSelectionList( rightHandSideComponent, isLoadingNewOptions = false, onLayout, - shouldUseOnySubscriptAvatar = true, }: BaseSelectionListProps, inputRef: ForwardedRef, ) { @@ -301,7 +300,6 @@ function BaseSelectionList( shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} keyForList={item.keyForList} - shouldUseOnySubscriptAvatar={shouldUseOnySubscriptAvatar} /> ); }; diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 3fd61177aa6a..f48f9e04077a 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -8,7 +8,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import type {UserListItemProps} from './types'; -function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style, isFocused, isHovered, shouldUseOnySubscriptAvatar = true}: UserListItemProps) { +function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style, isFocused, isHovered}: UserListItemProps) { const styles = useThemeStyles(); const theme = useTheme(); const StyleUtils = useStyleUtils(); @@ -21,7 +21,7 @@ function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style <> {!!item.icons && ( <> - {shouldUseOnySubscriptAvatar || item.shouldShowSubscript ? ( + {item.shouldShowSubscript ? ( & { /** Is item hovered */ isHovered?: boolean; - - /** Whether this item should use only the subscript avatar */ - shouldUseOnySubscriptAvatar?: boolean; }; type RadioItem = { @@ -124,7 +121,6 @@ type BaseListItemProps = CommonListItemProps = { @@ -249,9 +245,6 @@ type BaseSelectionListProps = Partial void; - - /** Whether this item should use only the subscript avatar */ - shouldUseOnySubscriptAvatar?: boolean; }; type ItemLayout = { diff --git a/src/pages/SearchPage/index.js b/src/pages/SearchPage/index.js index d9cae3ff8496..7c472296dfe1 100644 --- a/src/pages/SearchPage/index.js +++ b/src/pages/SearchPage/index.js @@ -164,7 +164,6 @@ function SearchPage({betas, reports, isSearchingForReports}) { showLoadingPlaceholder={!didScreenTransitionEnd || !isOptionsDataReady} footerContent={SearchPageFooterInstance} isLoadingNewOptions={isSearchingForReports} - shouldUseOnySubscriptAvatar={false} /> From 6abbea2eefeb2ded75dcad9581d672bfc3666701 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 13 Feb 2024 17:10:42 +0100 Subject: [PATCH 100/225] Add new avatar --- .../images/avatars/notifications-avatar.svg | 22 +++++++++++++++++++ src/CONST.ts | 2 +- src/components/Icon/Expensicons.ts | 2 ++ .../ReportActionItem/MoneyRequestPreview.tsx | 2 +- src/libs/ReportUtils.ts | 1 - src/libs/UserUtils.ts | 2 +- 6 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 assets/images/avatars/notifications-avatar.svg diff --git a/assets/images/avatars/notifications-avatar.svg b/assets/images/avatars/notifications-avatar.svg new file mode 100644 index 000000000000..224baad22cf6 --- /dev/null +++ b/assets/images/avatars/notifications-avatar.svg @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/src/CONST.ts b/src/CONST.ts index eae4b8ec7a2b..e4a52b434dde 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1055,7 +1055,7 @@ const CONST = { FIRST_RESPONDER: Number(Config?.EXPENSIFY_ACCOUNT_ID_FIRST_RESPONDER ?? 9375152), HELP: Number(Config?.EXPENSIFY_ACCOUNT_ID_HELP ?? -1), INTEGRATION_TESTING_CREDS: Number(Config?.EXPENSIFY_ACCOUNT_ID_INTEGRATION_TESTING_CREDS ?? -1), - NOTIFICATIONS: Number(Config?.EXPENSIFY_ACCOUNT_ID_NOTIFICATIONS ?? 11665625), + NOTIFICATIONS: Number(11665625), PAYROLL: Number(Config?.EXPENSIFY_ACCOUNT_ID_PAYROLL ?? 9679724), QA: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA ?? 3126513), QA_TRAVIS: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA_TRAVIS ?? 8595733), diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 252d13259aea..f5aa9753ecc5 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -94,6 +94,7 @@ import Monitor from '@assets/images/monitor.svg'; import NewExpensify from '@assets/images/new-expensify.svg'; import NewWindow from '@assets/images/new-window.svg'; import NewWorkspace from '@assets/images/new-workspace.svg'; +import NotificationsAvatar from '@assets/images/avatars/notifications-avatar.svg'; import OfflineCloud from '@assets/images/offline-cloud.svg'; import Offline from '@assets/images/offline.svg'; import OldDotWireframe from '@assets/images/olddot-wireframe.svg'; @@ -237,6 +238,7 @@ export { NewExpensify, NewWindow, NewWorkspace, + NotificationsAvatar, Offline, OfflineCloud, OldDotWireframe, diff --git a/src/components/ReportActionItem/MoneyRequestPreview.tsx b/src/components/ReportActionItem/MoneyRequestPreview.tsx index e89193108d24..1e569a6f170a 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview.tsx @@ -132,7 +132,7 @@ function MoneyRequestPreview({ if (isEmptyObject(iouReport) && !isBillSplit) { return null; } - + console.log(transaction); const sessionAccountID = session?.accountID; const managerID = iouReport?.managerID ?? -1; const ownerAccountID = iouReport?.ownerAccountID ?? -1; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e33421206a60..db780364db95 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1601,7 +1601,6 @@ function getPersonalDetailsForAccountID(accountID: number): Partial Date: Tue, 13 Feb 2024 17:27:24 +0100 Subject: [PATCH 101/225] remove console log --- src/components/ReportActionItem/MoneyRequestPreview.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.tsx b/src/components/ReportActionItem/MoneyRequestPreview.tsx index 1e569a6f170a..79ac494a4371 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview.tsx @@ -132,7 +132,6 @@ function MoneyRequestPreview({ if (isEmptyObject(iouReport) && !isBillSplit) { return null; } - console.log(transaction); const sessionAccountID = session?.accountID; const managerID = iouReport?.managerID ?? -1; const ownerAccountID = iouReport?.ownerAccountID ?? -1; From 7e0f064d5f962823577aab20dac9bfba303ff000 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 13 Feb 2024 23:45:54 +0700 Subject: [PATCH 102/225] fix lint --- src/pages/ShareCodePage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index fa56338939a4..36bdb2d1e2b6 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -131,7 +131,9 @@ function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodeP Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE, Navigation.getActiveRouteWithoutParams()))} + onPress={() => + Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE, Navigation.getActiveRouteWithoutParams())) + } wrapperStyle={themeStyles.sectionMenuItemTopDescription} shouldShowRightIcon /> From 7fed7ad57e0a88661878077373120c509e130f81 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 13 Feb 2024 18:00:44 +0100 Subject: [PATCH 103/225] prettier --- src/components/Icon/Expensicons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index f5aa9753ecc5..1311ac31a3dc 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -12,6 +12,7 @@ import DeletedRoomAvatar from '@assets/images/avatars/deleted-room.svg'; import DomainRoomAvatar from '@assets/images/avatars/domain-room.svg'; import FallbackAvatar from '@assets/images/avatars/fallback-avatar.svg'; import FallbackWorkspaceAvatar from '@assets/images/avatars/fallback-workspace-avatar.svg'; +import NotificationsAvatar from '@assets/images/avatars/notifications-avatar.svg'; import ActiveRoomAvatar from '@assets/images/avatars/room.svg'; import BackArrow from '@assets/images/back-left.svg'; import Bank from '@assets/images/bank.svg'; @@ -94,7 +95,6 @@ import Monitor from '@assets/images/monitor.svg'; import NewExpensify from '@assets/images/new-expensify.svg'; import NewWindow from '@assets/images/new-window.svg'; import NewWorkspace from '@assets/images/new-workspace.svg'; -import NotificationsAvatar from '@assets/images/avatars/notifications-avatar.svg'; import OfflineCloud from '@assets/images/offline-cloud.svg'; import Offline from '@assets/images/offline.svg'; import OldDotWireframe from '@assets/images/olddot-wireframe.svg'; From b4ee4f23f9f9b713bd3651f5e2ee735e4dce647b Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 14 Feb 2024 11:59:22 +0800 Subject: [PATCH 104/225] fix skeleton vertical position --- src/components/ReportHeaderSkeletonView.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx index 5332e0c5032c..109904e5b589 100644 --- a/src/components/ReportHeaderSkeletonView.tsx +++ b/src/components/ReportHeaderSkeletonView.tsx @@ -5,7 +5,6 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import variables from '@styles/variables'; import CONST from '@src/CONST'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -22,9 +21,12 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = () const styles = useThemeStyles(); const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); + const smallScreenHeight = styles.appContentHeader.height; + const height = !isSmallScreenWidth ? styles.headerBarDesktopHeight.height : smallScreenHeight; + const heightDiff = height - smallScreenHeight; return ( - + {isSmallScreenWidth && ( From ebca48acf1636bad7f64bd01f419636497307fc6 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 14 Feb 2024 12:21:20 +0800 Subject: [PATCH 105/225] fix skeleton vertical position --- src/components/ReportHeaderSkeletonView.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx index 109904e5b589..bc4eef675170 100644 --- a/src/components/ReportHeaderSkeletonView.tsx +++ b/src/components/ReportHeaderSkeletonView.tsx @@ -23,7 +23,10 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = () const {isSmallScreenWidth} = useWindowDimensions(); const smallScreenHeight = styles.appContentHeader.height; const height = !isSmallScreenWidth ? styles.headerBarDesktopHeight.height : smallScreenHeight; - const heightDiff = height - smallScreenHeight; + const radius = 20; + const circleY = height / 2; + const circleTopY = circleY - radius; + const circleBottomY = circleY + radius; return ( @@ -51,17 +54,17 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = () From a3fbb23762043510669941c10b1e67fbe0797602 Mon Sep 17 00:00:00 2001 From: hkopser99 <144170694+hkopser99@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:40:24 +0100 Subject: [PATCH 106/225] Update AppDownloadLinks.tsx Amended openAppDownloadLink and downloadLink in DownloadMenuItem back to action and link. Returned to original, simplified logic for Navigation.goBack() --- src/pages/settings/AppDownloadLinks.tsx | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/pages/settings/AppDownloadLinks.tsx b/src/pages/settings/AppDownloadLinks.tsx index 5ded324e5b45..352b3772923a 100644 --- a/src/pages/settings/AppDownloadLinks.tsx +++ b/src/pages/settings/AppDownloadLinks.tsx @@ -13,12 +13,11 @@ import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportA import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; -import ROUTES from '@src/ROUTES'; type DownloadMenuItem = MenuItemProps & { translationKey: TranslationPaths; - openAppDownloadLink: () => void; - downloadLink: string; + action: () => void; + link: string; }; function AppDownloadLinksPage() { @@ -29,28 +28,28 @@ function AppDownloadLinksPage() { const menuItems: DownloadMenuItem[] = [ { translationKey: 'initialSettingsPage.appDownloadLinks.android.label', - openAppDownloadLink: () => { + action: () => { Link.openExternalLink(CONST.APP_DOWNLOAD_LINKS.ANDROID); }, - downloadLink: CONST.APP_DOWNLOAD_LINKS.ANDROID, + link: CONST.APP_DOWNLOAD_LINKS.ANDROID, icon: Expensicons.Android, iconRight: Expensicons.NewWindow, }, { translationKey: 'initialSettingsPage.appDownloadLinks.ios.label', - openAppDownloadLink: () => { + action: () => { Link.openExternalLink(CONST.APP_DOWNLOAD_LINKS.IOS, true); }, - downloadLink: CONST.APP_DOWNLOAD_LINKS.IOS, + link: CONST.APP_DOWNLOAD_LINKS.IOS, icon: Expensicons.Apple, iconRight: Expensicons.NewWindow, }, { translationKey: 'initialSettingsPage.appDownloadLinks.desktop.label', - openAppDownloadLink: () => { + action: () => { Link.openExternalLink(CONST.APP_DOWNLOAD_LINKS.DESKTOP); }, - downloadLink: CONST.APP_DOWNLOAD_LINKS.DESKTOP, + link: CONST.APP_DOWNLOAD_LINKS.DESKTOP, icon: Expensicons.Monitor, iconRight: Expensicons.NewWindow, }, @@ -60,14 +59,14 @@ function AppDownloadLinksPage() { Navigation.goBack(ROUTES.SETTINGS_ABOUT)} + onBackButtonPress={() => Navigation.goBack()} /> {menuItems.map((item: DownloadMenuItem) => ( ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, item.downloadLink, popoverAnchor.current)} + onPress={item.action} + onSecondaryInteraction={(e) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, item.link, popoverAnchor.current)} ref={popoverAnchor} title={translate(item.translationKey)} icon={item.icon} From 76c7490827f6716effa4eb77aadf021899aa61b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Mon, 12 Feb 2024 13:09:05 +0100 Subject: [PATCH 107/225] remove caching --- src/libs/SidebarUtils.ts | 45 -------------------- src/pages/home/sidebar/SidebarLinksData.js | 48 +++------------------- 2 files changed, 5 insertions(+), 88 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 6e46ec320066..d92915842a07 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1,6 +1,5 @@ /* eslint-disable rulesdir/prefer-underscore-method */ import Str from 'expensify-common/lib/str'; -import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; @@ -94,22 +93,6 @@ function setIsSidebarLoadedReady() { resolveSidebarIsReadyPromise(); } -// Define a cache object to store the memoized results -const reportIDsCache = new Map(); - -// Function to set a key-value pair while maintaining the maximum key limit -function setWithLimit(map: Map, key: TKey, value: TValue) { - if (map.size >= 5) { - // If the map has reached its limit, remove the first (oldest) key-value pair - const firstKey = map.keys().next().value; - map.delete(firstKey); - } - map.set(key, value); -} - -// Variable to verify if ONYX actions are loaded -let hasInitialReportActions = false; - /** * @returns An array of reportIDs sorted in the proper order */ @@ -119,34 +102,7 @@ function getOrderedReportIDs( betas: Beta[], policies: Record, priorityMode: ValueOf, - allReportActions: OnyxCollection, ): string[] { - // Generate a unique cache key based on the function arguments - const cachedReportsKey = JSON.stringify( - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - [currentReportId, allReports, betas, policies, priorityMode, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1], - (key, value: unknown) => { - /** - * Exclude some properties not to overwhelm a cached key value with huge data, - * which we don't need to store in a cacheKey - */ - if (key === 'participantAccountIDs' || key === 'participants' || key === 'lastMessageText' || key === 'visibleChatMemberAccountIDs') { - return undefined; - } - - return value; - }, - ); - - // Check if the result is already in the cache - const cachedIDs = reportIDsCache.get(cachedReportsKey); - if (cachedIDs && hasInitialReportActions) { - return cachedIDs; - } - - // This is needed to prevent caching when Onyx is empty for a second render - hasInitialReportActions = Object.values(lastReportActions).length > 0; - const isInGSDMode = priorityMode === CONST.PRIORITY_MODE.GSD; const isInDefaultMode = !isInGSDMode; const allReportsDictValues = Object.values(allReports); @@ -219,7 +175,6 @@ function getOrderedReportIDs( // Now that we have all the reports grouped and sorted, they must be flattened into an array and only return the reportID. // The order the arrays are concatenated in matters and will determine the order that the groups are displayed in the sidebar. const LHNReports = [...pinnedAndGBRReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report.reportID); - setWithLimit(reportIDsCache, cachedReportsKey, LHNReports); return LHNReports; } diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index 580cc7909fd1..bcfd804b2d23 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -1,5 +1,4 @@ import {deepEqual} from 'fast-equals'; -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useMemo, useRef} from 'react'; import {View} from 'react-native'; @@ -25,22 +24,6 @@ const propTypes = { /** List of reports */ chatReports: PropTypes.objectOf(reportPropTypes), - /** All report actions for all reports */ - allReportActions: PropTypes.objectOf( - PropTypes.arrayOf( - PropTypes.shape({ - error: PropTypes.string, - message: PropTypes.arrayOf( - PropTypes.shape({ - moderationDecision: PropTypes.shape({ - decision: PropTypes.string, - }), - }), - ), - }), - ), - ), - /** Whether the reports are loading. When false it means they are ready to be used. */ isLoadingApp: PropTypes.bool, @@ -59,21 +42,20 @@ const propTypes = { const defaultProps = { chatReports: {}, - allReportActions: {}, isLoadingApp: true, priorityMode: CONST.PRIORITY_MODE.DEFAULT, betas: [], policies: {}, }; -function SidebarLinksData({isFocused, allReportActions, betas, chatReports, currentReportID, insets, isLoadingApp, onLinkClick, policies, priorityMode, network}) { +function SidebarLinksData({isFocused, betas, chatReports, currentReportID, insets, isLoadingApp, onLinkClick, policies, priorityMode, network}) { const styles = useThemeStyles(); const {translate} = useLocalize(); const reportIDsRef = useRef(null); const isLoading = isLoadingApp; const optionListItems = useMemo(() => { - const reportIDs = SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode, allReportActions); + const reportIDs = SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode); if (deepEqual(reportIDsRef.current, reportIDs)) { return reportIDsRef.current; @@ -85,7 +67,7 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr reportIDsRef.current = reportIDs; } return reportIDsRef.current || []; - }, [allReportActions, betas, chatReports, policies, priorityMode, isLoading, network.isOffline]); + }, [betas, chatReports, policies, priorityMode, isLoading, network.isOffline]); // We need to make sure the current report is in the list of reports, but we do not want // to have to re-generate the list every time the currentReportID changes. To do that @@ -94,10 +76,10 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr // case we re-generate the list a 2nd time with the current report included. const optionListItemsWithCurrentReport = useMemo(() => { if (currentReportID && !_.contains(optionListItems, currentReportID)) { - return SidebarUtils.getOrderedReportIDs(currentReportID, chatReports, betas, policies, priorityMode, allReportActions); + return SidebarUtils.getOrderedReportIDs(currentReportID, chatReports, betas, policies, priorityMode); } return optionListItems; - }, [currentReportID, optionListItems, chatReports, betas, policies, priorityMode, allReportActions]); + }, [currentReportID, optionListItems, chatReports, betas, policies, priorityMode]); const currentReportIDRef = useRef(currentReportID); currentReportIDRef.current = currentReportID; @@ -171,21 +153,6 @@ const chatReportSelector = (report) => isDeletedParentAction: report.isDeletedParentAction, }; -/** - * @param {Object} [reportActions] - * @returns {Object|undefined} - */ -const reportActionsSelector = (reportActions) => - reportActions && - _.map(reportActions, (reportAction) => ({ - errors: lodashGet(reportAction, 'errors', []), - message: [ - { - moderationDecision: {decision: lodashGet(reportAction, 'message[0].moderationDecision.decision')}, - }, - ], - })); - /** * @param {Object} [policy] * @returns {Object|undefined} @@ -218,11 +185,6 @@ export default compose( key: ONYXKEYS.BETAS, initialValue: [], }, - allReportActions: { - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - selector: reportActionsSelector, - initialValue: {}, - }, policies: { key: ONYXKEYS.COLLECTION.POLICY, selector: policySelector, From b5b642b4f2dfaecf4acd7b9805f3e004be1126b2 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 14 Feb 2024 12:27:42 +0000 Subject: [PATCH 108/225] [TS migration][react-native-onyx] Added comment on the disabled rule --- __mocks__/react-native-onyx.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/__mocks__/react-native-onyx.ts b/__mocks__/react-native-onyx.ts index beed7be9cbdf..2a172f818b5f 100644 --- a/__mocks__/react-native-onyx.ts +++ b/__mocks__/react-native-onyx.ts @@ -1,3 +1,8 @@ +/** + * We are disabling the lint rule that doesn't allow the usage of connect onyx outside libs + * because the intent of this file is to mock the usage of react-native-onyx so we will have to mock the connect function + * as this is a file outside of the lib folder we need to disable it + */ /* eslint-disable rulesdir/prefer-onyx-connect-in-libs */ import type {ConnectOptions, OnyxKey} from 'react-native-onyx'; import Onyx, {withOnyx} from 'react-native-onyx'; From 52408bbf09afdf9d1a66a825eb182415ff4f2bdb Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 14 Feb 2024 12:33:10 +0000 Subject: [PATCH 109/225] [TS migration][react-native-onyx] Prettier run --- __mocks__/react-native-onyx.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/__mocks__/react-native-onyx.ts b/__mocks__/react-native-onyx.ts index 2a172f818b5f..d403000191b1 100644 --- a/__mocks__/react-native-onyx.ts +++ b/__mocks__/react-native-onyx.ts @@ -3,6 +3,7 @@ * because the intent of this file is to mock the usage of react-native-onyx so we will have to mock the connect function * as this is a file outside of the lib folder we need to disable it */ + /* eslint-disable rulesdir/prefer-onyx-connect-in-libs */ import type {ConnectOptions, OnyxKey} from 'react-native-onyx'; import Onyx, {withOnyx} from 'react-native-onyx'; From b1f989f58bb84fdb27d1845fffbfa975e25cee8e Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 14 Feb 2024 15:45:43 +0100 Subject: [PATCH 110/225] Prevent calling welcome.show on the settings tab --- .../createCustomBottomTabNavigator/BottomTabBar.tsx | 9 +++++++-- src/libs/actions/Welcome.ts | 4 +--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index a953175fdcb7..8f646f7a748c 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -42,8 +42,13 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps const navigationState = navigation.getState(); const routes = navigationState.routes; const currentRoute = routes[navigationState.index]; - - if (currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) { + const bottomTabRoute = getTopmostBottomTabRoute(navigationState); + if ( + // When we are redirected to the Settings tab from the OldDot, we don't want to call the Welcome.show() method. + // To prevent this, the value of the bottomTabRoute?.name is checked here + bottomTabRoute?.name === SCREENS.WORKSPACE.INITIAL || + (currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) + ) { return; } diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index 952d19117679..3f6b2dc99a8f 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -136,13 +136,11 @@ function show(routes: NavigationState['routes'], showEngagem const transitionRoute = routes.find( (route): route is NavigationState>['routes'][number] => route.name === SCREENS.TRANSITION_BETWEEN_APPS, ); - const activeRoute = Navigation.getActiveRouteWithoutParams(); - const isOnWorkspaceOverviewPage = activeRoute?.startsWith('/workspace') && activeRoute?.endsWith('/overview'); const isExitingToWorkspaceRoute = transitionRoute?.params?.exitTo === 'workspace/new'; // If we already opened the workspace settings or want the admin room to stay open, do not // navigate away to the workspace chat report - const shouldNavigateToWorkspaceChat = !isExitingToWorkspaceRoute && !isOnWorkspaceOverviewPage; + const shouldNavigateToWorkspaceChat = !isExitingToWorkspaceRoute; const workspaceChatReport = Object.values(allReports ?? {}).find((report) => { if (report) { From 0ea67f499eff3ac16ecc2ff16cd78de6a61b9d2c Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 14 Feb 2024 15:55:33 +0100 Subject: [PATCH 111/225] Use es6 imports and exports --- tests/e2e/compare/compare.js | 16 ++++++------ tests/e2e/compare/math.js | 5 +--- tests/e2e/compare/output/console.js | 6 ++--- tests/e2e/compare/output/format.js | 10 +------ tests/e2e/compare/output/markdown.js | 25 +++++++++--------- tests/e2e/compare/output/markdownTable.js | 2 +- tests/e2e/config.dev.js | 2 +- tests/e2e/config.js | 2 +- tests/e2e/config.local.js | 2 +- tests/e2e/measure/math.js | 7 +++-- tests/e2e/measure/writeTestStats.js | 7 +++-- tests/e2e/merge.js | 10 +++---- .../nativeCommands/NativeCommandsAction.js | 6 +---- tests/e2e/nativeCommands/adbBackspace.js | 6 ++--- tests/e2e/nativeCommands/adbTypeText.js | 6 ++--- tests/e2e/nativeCommands/index.js | 13 ++++------ tests/e2e/server/index.js | 16 ++++++------ tests/e2e/server/routes.js | 2 +- tests/e2e/testRunner.js | 26 +++++++++---------- tests/e2e/utils/androidReversePort.js | 10 +++---- tests/e2e/utils/execAsync.js | 6 ++--- tests/e2e/utils/getCurrentBranchName.js | 4 +-- tests/e2e/utils/installApp.js | 8 +++--- tests/e2e/utils/killApp.js | 8 +++--- tests/e2e/utils/launchApp.js | 8 +++--- tests/e2e/utils/logger.js | 26 ++++++------------- tests/e2e/utils/sleep.js | 4 +-- tests/e2e/utils/withFailTimeout.js | 6 ++--- tests/unit/GithubUtilsTest.js | 4 +-- tests/unit/PaymentUtilsTest.js | 3 +-- tests/unit/TranslateTest.js | 18 ++++++------- tests/unit/UrlTest.js | 2 +- tests/unit/ValidationUtilsTest.js | 3 +-- tests/unit/awaitStagingDeploysTest.js | 8 +++--- tests/unit/checkDeployBlockersTest.js | 8 +++--- tests/unit/createOrUpdateStagingDeployTest.js | 16 ++++++------ tests/unit/isStagingDeployLockedTest.js | 6 ++--- tests/unit/markPullRequestsAsDeployedTest.js | 6 ++--- tests/unit/nativeVersionUpdaterTest.js | 9 ++++--- tests/unit/postTestBuildComment.js | 10 +++---- tests/unit/versionUpdaterTest.js | 2 +- 41 files changed, 155 insertions(+), 189 deletions(-) diff --git a/tests/e2e/compare/compare.js b/tests/e2e/compare/compare.js index 3be7abc91188..b7d5c15c5f82 100644 --- a/tests/e2e/compare/compare.js +++ b/tests/e2e/compare/compare.js @@ -1,8 +1,8 @@ -const _ = require('underscore'); -const {computeProbability, computeZ} = require('./math'); -const {getStats} = require('../measure/math'); -const printToConsole = require('./output/console'); -const writeToMarkdown = require('./output/markdown'); +import _ from 'underscore'; +import getStats from '../measure/math'; +import math from './math'; +import printToConsole from './output/console'; +import writeToMarkdown from './output/markdown'; /* * base implementation from: https://github.com/callstack/reassure/blob/main/packages/reassure-compare/src/compare.ts @@ -36,8 +36,8 @@ function buildCompareEntry(name, compare, baseline) { const diff = compare.mean - baseline.mean; const relativeDurationDiff = diff / baseline.mean; - const z = computeZ(baseline.mean, baseline.stdev, compare.mean, compare.runs); - const prob = computeProbability(z); + const z = math.computeZ(baseline.mean, baseline.stdev, compare.mean, compare.runs); + const prob = math.computeProbability(z); const isDurationDiffOfSignificance = prob < PROBABILITY_CONSIDERED_SIGNIFICANCE && Math.abs(diff) >= DURATION_DIFF_THRESHOLD_SIGNIFICANCE; @@ -106,7 +106,7 @@ function compareResults(compareEntries, baselineEntries) { }; } -module.exports = (main, delta, outputFile, outputFormat = 'all') => { +export default (main, delta, outputFile, outputFormat = 'all') => { const outputData = compareResults(main, delta); if (outputFormat === 'console' || outputFormat === 'all') { diff --git a/tests/e2e/compare/math.js b/tests/e2e/compare/math.js index a7dbe1c95a86..a87c58c4dff3 100644 --- a/tests/e2e/compare/math.js +++ b/tests/e2e/compare/math.js @@ -89,7 +89,4 @@ const computeProbability = (z) => { return 0.2; }; -module.exports = { - computeZ, - computeProbability, -}; +export {computeZ, computeProbability}; diff --git a/tests/e2e/compare/output/console.js b/tests/e2e/compare/output/console.js index cfcd7019bbdd..61645baf38b8 100644 --- a/tests/e2e/compare/output/console.js +++ b/tests/e2e/compare/output/console.js @@ -1,14 +1,14 @@ -const {formatDurationDiffChange} = require('./format'); +import format from './format'; const printRegularLine = (entry) => { - console.debug(` - ${entry.name}: ${formatDurationDiffChange(entry)}`); + console.debug(` - ${entry.name}: ${format.formatDurationDiffChange(entry)}`); }; /** * Prints the result simply to console. * @param {Object} data */ -module.exports = (data) => { +export default (data) => { // No need to log errors or warnings as these were be logged on the fly console.debug(''); console.debug('❇️ Performance comparison results:'); diff --git a/tests/e2e/compare/output/format.js b/tests/e2e/compare/output/format.js index c31ac547b41d..18b49cf03028 100644 --- a/tests/e2e/compare/output/format.js +++ b/tests/e2e/compare/output/format.js @@ -82,12 +82,4 @@ const formatDurationDiffChange = (entry) => { return output; }; -module.exports = { - formatPercent, - formatPercentChange, - formatDuration, - formatDurationChange, - formatChange, - getDurationSymbols, - formatDurationDiffChange, -}; +export {formatPercent, formatPercentChange, formatDuration, formatDurationChange, formatChange, getDurationSymbols, formatDurationDiffChange}; diff --git a/tests/e2e/compare/output/markdown.js b/tests/e2e/compare/output/markdown.js index 2015d8de6cc3..1f9626678498 100644 --- a/tests/e2e/compare/output/markdown.js +++ b/tests/e2e/compare/output/markdown.js @@ -1,11 +1,10 @@ // From: https://raw.githubusercontent.com/callstack/reassure/main/packages/reassure-compare/src/output/markdown.ts - -const fs = require('node:fs/promises'); -const path = require('path'); -const _ = require('underscore'); -const markdownTable = require('./markdownTable'); -const {formatDuration, formatPercent, formatDurationDiffChange} = require('./format'); -const Logger = require('../../utils/logger'); +import fs from 'node:fs/promises'; +import path from 'path'; +import _ from 'underscore'; +import Logger from '../../utils/logger'; +import format from './format'; +import markdownTable from './markdownTable'; const tableHeader = ['Name', 'Duration']; @@ -17,8 +16,8 @@ const buildDurationDetails = (title, entry) => { return _.filter( [ `**${title}**`, - `Mean: ${formatDuration(entry.mean)}`, - `Stdev: ${formatDuration(entry.stdev)} (${formatPercent(relativeStdev)})`, + `Mean: ${format.formatDuration(entry.mean)}`, + `Stdev: ${format.formatDuration(entry.stdev)} (${format.formatPercent(relativeStdev)})`, entry.entries ? `Runs: ${entry.entries.join(' ')}` : '', ], Boolean, @@ -32,13 +31,13 @@ const buildDurationDetailsEntry = (entry) => const formatEntryDuration = (entry) => { if ('baseline' in entry && 'current' in entry) { - return formatDurationDiffChange(entry); + return format.formatDurationDiffChange(entry); } if ('baseline' in entry) { - return formatDuration(entry.baseline.mean); + return format.formatDuration(entry.baseline.mean); } if ('current' in entry) { - return formatDuration(entry.current.mean); + return format.formatDuration(entry.current.mean); } return ''; }; @@ -115,4 +114,4 @@ const writeToMarkdown = (filePath, data) => { }); }; -module.exports = writeToMarkdown; +export default writeToMarkdown; diff --git a/tests/e2e/compare/output/markdownTable.js b/tests/e2e/compare/output/markdownTable.js index 3b580bcfc116..198ae17daba5 100644 --- a/tests/e2e/compare/output/markdownTable.js +++ b/tests/e2e/compare/output/markdownTable.js @@ -365,4 +365,4 @@ function toAlignment(value) { : 0; } -module.exports = markdownTable; +export default markdownTable; diff --git a/tests/e2e/config.dev.js b/tests/e2e/config.dev.js index 894b4737c36f..8d12fb5ce007 100644 --- a/tests/e2e/config.dev.js +++ b/tests/e2e/config.dev.js @@ -1,7 +1,7 @@ const packageName = 'com.expensify.chat.dev'; const appPath = './android/app/build/outputs/apk/development/debug/app-development-debug.apk'; -module.exports = { +export default { MAIN_APP_PACKAGE: packageName, DELTA_APP_PACKAGE: packageName, MAIN_APP_PATH: appPath, diff --git a/tests/e2e/config.js b/tests/e2e/config.js index a7447a29c954..d51119e77430 100644 --- a/tests/e2e/config.js +++ b/tests/e2e/config.js @@ -17,7 +17,7 @@ const TEST_NAMES = { * npm run test:e2e -- --config ./path/to/your/config.js * ``` */ -module.exports = { +export default { MAIN_APP_PACKAGE: 'com.expensify.chat.e2e', DELTA_APP_PACKAGE: 'com.expensify.chat.e2edelta', diff --git a/tests/e2e/config.local.js b/tests/e2e/config.local.js index 45b946b91aeb..605b1ddd57d7 100644 --- a/tests/e2e/config.local.js +++ b/tests/e2e/config.local.js @@ -1,4 +1,4 @@ -module.exports = { +export default { MAIN_APP_PACKAGE: 'com.expensify.chat.e2e', DELTA_APP_PACKAGE: 'com.expensify.chat.e2edelta', MAIN_APP_PATH: './android/app/build/outputs/apk/e2e/release/app-e2e-release.apk', diff --git a/tests/e2e/measure/math.js b/tests/e2e/measure/math.js index 712e197d367b..14f75a7f980e 100644 --- a/tests/e2e/measure/math.js +++ b/tests/e2e/measure/math.js @@ -1,4 +1,4 @@ -const _ = require('underscore'); +import _ from 'underscore'; const filterOutliersViaIQR = (data) => { let q1; @@ -46,6 +46,5 @@ const getStats = (entries) => { }; }; -module.exports = { - getStats, -}; +// eslint-disable-next-line import/prefer-default-export +export default getStats; diff --git a/tests/e2e/measure/writeTestStats.js b/tests/e2e/measure/writeTestStats.js index c4074dbdd08f..6de9dcc79db4 100644 --- a/tests/e2e/measure/writeTestStats.js +++ b/tests/e2e/measure/writeTestStats.js @@ -1,6 +1,5 @@ -const fs = require('fs'); - -const {OUTPUT_FILE_CURRENT} = require('../config'); +import fs from 'fs'; +import config from '../config'; /** * Writes the results of `getStats` to the {@link OUTPUT_FILE_CURRENT} file. @@ -13,7 +12,7 @@ const {OUTPUT_FILE_CURRENT} = require('../config'); * @param {number} stats.runs - The number of times the test was run. * @param {string} [path] - The path to write to. Defaults to {@link OUTPUT_FILE_CURRENT}. */ -module.exports = (stats, path = OUTPUT_FILE_CURRENT) => { +export default (stats, path = config.OUTPUT_FILE_CURRENT) => { if (!stats.name || stats.mean == null || stats.stdev == null || !stats.entries || !stats.runs) { throw new Error(`Invalid stats object:\n${JSON.stringify(stats, null, 2)}\n\n`); } diff --git a/tests/e2e/merge.js b/tests/e2e/merge.js index 0ee476137315..d7c1b8699c7d 100644 --- a/tests/e2e/merge.js +++ b/tests/e2e/merge.js @@ -1,19 +1,19 @@ -const compare = require('./compare/compare'); -const {OUTPUT_DIR} = require('./config'); +import compare from './compare/compare'; +import CONFIG from './config'; const args = process.argv.slice(2); -let mainPath = `${OUTPUT_DIR}/main.json`; +let mainPath = `${CONFIG.OUTPUT_DIR}/main.json`; if (args.includes('--mainPath')) { mainPath = args[args.indexOf('--mainPath') + 1]; } -let deltaPath = `${OUTPUT_DIR}/delta.json`; +let deltaPath = `${CONFIG.OUTPUT_DIR}/delta.json`; if (args.includes('--deltaPath')) { deltaPath = args[args.indexOf('--deltaPath') + 1]; } -let outputPath = `${OUTPUT_DIR}/output.md`; +let outputPath = `${CONFIG.OUTPUT_DIR}/output.md`; if (args.includes('--outputPath')) { outputPath = args[args.indexOf('--outputPath') + 1]; } diff --git a/tests/e2e/nativeCommands/NativeCommandsAction.js b/tests/e2e/nativeCommands/NativeCommandsAction.js index f2aa4644f7ff..24ceefb5acb7 100644 --- a/tests/e2e/nativeCommands/NativeCommandsAction.js +++ b/tests/e2e/nativeCommands/NativeCommandsAction.js @@ -15,8 +15,4 @@ const makeBackspaceCommand = () => ({ actionName: NativeCommandsAction.backspace, }); -module.exports = { - NativeCommandsAction, - makeTypeTextCommand, - makeBackspaceCommand, -}; +export {NativeCommandsAction, makeTypeTextCommand, makeBackspaceCommand}; diff --git a/tests/e2e/nativeCommands/adbBackspace.js b/tests/e2e/nativeCommands/adbBackspace.js index 8f41364daed3..c0dcf65c240c 100644 --- a/tests/e2e/nativeCommands/adbBackspace.js +++ b/tests/e2e/nativeCommands/adbBackspace.js @@ -1,5 +1,5 @@ -const execAsync = require('../utils/execAsync'); -const Logger = require('../utils/logger'); +import execAsync from '../utils/execAsync'; +import Logger from '../utils/logger'; const adbBackspace = async () => { Logger.log(`🔙 Pressing backspace`); @@ -7,4 +7,4 @@ const adbBackspace = async () => { return true; }; -module.exports = adbBackspace; +export default adbBackspace; diff --git a/tests/e2e/nativeCommands/adbTypeText.js b/tests/e2e/nativeCommands/adbTypeText.js index cbaa9f4434a2..7a4c3b31b7fd 100644 --- a/tests/e2e/nativeCommands/adbTypeText.js +++ b/tests/e2e/nativeCommands/adbTypeText.js @@ -1,5 +1,5 @@ -const execAsync = require('../utils/execAsync'); -const Logger = require('../utils/logger'); +import execAsync from '../utils/execAsync'; +import Logger from '../utils/logger'; const adbTypeText = async (text) => { Logger.log(`📝 Typing text: ${text}`); @@ -7,4 +7,4 @@ const adbTypeText = async (text) => { return true; }; -module.exports = adbTypeText; +export default adbTypeText; diff --git a/tests/e2e/nativeCommands/index.js b/tests/e2e/nativeCommands/index.js index bb87c16a6f42..90dcb00bbcae 100644 --- a/tests/e2e/nativeCommands/index.js +++ b/tests/e2e/nativeCommands/index.js @@ -1,6 +1,7 @@ -const adbBackspace = require('./adbBackspace'); -const adbTypeText = require('./adbTypeText'); -const {NativeCommandsAction} = require('./NativeCommandsAction'); +import adbBackspace from './adbBackspace'; +import adbTypeText from './adbTypeText'; +// eslint-disable-next-line rulesdir/prefer-import-module-contents +import {NativeCommandsAction} from './NativeCommandsAction'; const executeFromPayload = (actionName, payload) => { switch (actionName) { @@ -15,8 +16,4 @@ const executeFromPayload = (actionName, payload) => { } }; -module.exports = { - NativeCommandsAction, - executeFromPayload, - adbTypeText, -}; +export {NativeCommandsAction, executeFromPayload, adbTypeText}; diff --git a/tests/e2e/server/index.js b/tests/e2e/server/index.js index c2365f259bb7..b90e96696525 100644 --- a/tests/e2e/server/index.js +++ b/tests/e2e/server/index.js @@ -1,10 +1,10 @@ -const {createServer} = require('http'); -const Routes = require('./routes'); -const Logger = require('../utils/logger'); -const {SERVER_PORT} = require('../config'); -const {executeFromPayload} = require('../nativeCommands'); +import {createServer} from 'http'; +import config from '../config'; +import * as nativeCommands from '../nativeCommands'; +import Logger from '../utils/logger'; +import Routes from './routes'; -const PORT = process.env.PORT || SERVER_PORT; +const PORT = process.env.PORT || config.SERVER_PORT; // Gets the request data as a string const getReqData = (req) => { @@ -130,7 +130,7 @@ const createServerInstance = () => { case Routes.testNativeCommand: { getPostJSONRequestData(req, res) .then((data) => - executeFromPayload(data.actionName, data.payload).then((status) => { + nativeCommands.executeFromPayload(data.actionName, data.payload).then((status) => { if (status) { res.end('ok'); return; @@ -196,4 +196,4 @@ const createServerInstance = () => { }; }; -module.exports = createServerInstance; +export default createServerInstance; diff --git a/tests/e2e/server/routes.js b/tests/e2e/server/routes.js index 0d23866ec808..ffab3b01d297 100644 --- a/tests/e2e/server/routes.js +++ b/tests/e2e/server/routes.js @@ -1,4 +1,4 @@ -module.exports = { +export default { // The app calls this endpoint to know which test to run testConfig: '/test_config', diff --git a/tests/e2e/testRunner.js b/tests/e2e/testRunner.js index 63c88d207581..f6e874c4f421 100644 --- a/tests/e2e/testRunner.js +++ b/tests/e2e/testRunner.js @@ -14,19 +14,19 @@ */ /* eslint-disable @lwc/lwc/no-async-await,no-restricted-syntax,no-await-in-loop */ -const fs = require('fs'); -const _ = require('underscore'); -const defaultConfig = require('./config'); -const Logger = require('./utils/logger'); -const execAsync = require('./utils/execAsync'); -const killApp = require('./utils/killApp'); -const launchApp = require('./utils/launchApp'); -const createServerInstance = require('./server'); -const installApp = require('./utils/installApp'); -const withFailTimeout = require('./utils/withFailTimeout'); -const reversePort = require('./utils/androidReversePort'); -const sleep = require('./utils/sleep'); -const compare = require('./compare/compare'); +import fs from 'fs'; +import _ from 'underscore'; +import compare from './compare/compare'; +import defaultConfig from './config'; +import createServerInstance from './server'; +import reversePort from './utils/androidReversePort'; +import execAsync from './utils/execAsync'; +import installApp from './utils/installApp'; +import killApp from './utils/killApp'; +import launchApp from './utils/launchApp'; +import Logger from './utils/logger'; +import sleep from './utils/sleep'; +import withFailTimeout from './utils/withFailTimeout'; // VARIABLE CONFIGURATION const args = process.argv.slice(2); diff --git a/tests/e2e/utils/androidReversePort.js b/tests/e2e/utils/androidReversePort.js index b644ca1538dd..55851daf59bf 100644 --- a/tests/e2e/utils/androidReversePort.js +++ b/tests/e2e/utils/androidReversePort.js @@ -1,6 +1,6 @@ -const {SERVER_PORT} = require('../config'); -const execAsync = require('./execAsync'); +import config from '../config'; +import execAsync from './execAsync'; -module.exports = function () { - return execAsync(`adb reverse tcp:${SERVER_PORT} tcp:${SERVER_PORT}`); -}; +export default function () { + return execAsync(`adb reverse tcp:${config.SERVER_PORT} tcp:${config.SERVER_PORT}`); +} diff --git a/tests/e2e/utils/execAsync.js b/tests/e2e/utils/execAsync.js index be80452c8acb..fad44082e40c 100644 --- a/tests/e2e/utils/execAsync.js +++ b/tests/e2e/utils/execAsync.js @@ -1,5 +1,5 @@ -const {exec} = require('child_process'); -const Logger = require('./logger'); +import {exec} from 'child_process'; +import Logger from './logger'; /** * Executes a command none-blocking by wrapping it in a promise. @@ -8,7 +8,7 @@ const Logger = require('./logger'); * @param {object} env environment variables * @returns {Promise} */ -module.exports = (command, env = {}) => { +export default (command, env = {}) => { let childProcess; const promise = new Promise((resolve, reject) => { const finalEnv = { diff --git a/tests/e2e/utils/getCurrentBranchName.js b/tests/e2e/utils/getCurrentBranchName.js index ca2f0cba97b0..55df11010214 100644 --- a/tests/e2e/utils/getCurrentBranchName.js +++ b/tests/e2e/utils/getCurrentBranchName.js @@ -1,4 +1,4 @@ -const {execSync} = require('child_process'); +import {execSync} from 'child_process'; const getCurrentBranchName = () => { const stdout = execSync('git rev-parse --abbrev-ref HEAD', { @@ -7,4 +7,4 @@ const getCurrentBranchName = () => { return stdout.trim(); }; -module.exports = getCurrentBranchName; +export default getCurrentBranchName; diff --git a/tests/e2e/utils/installApp.js b/tests/e2e/utils/installApp.js index 3741e459ea83..331ae5e99dee 100644 --- a/tests/e2e/utils/installApp.js +++ b/tests/e2e/utils/installApp.js @@ -1,5 +1,5 @@ -const execAsync = require('./execAsync'); -const Logger = require('./logger'); +import execAsync from './execAsync'; +import Logger from './logger'; /** * Installs the app on the currently connected device for the given platform. @@ -10,7 +10,7 @@ const Logger = require('./logger'); * @param {String} path * @returns {Promise} */ -module.exports = function (platform = 'android', packageName, path) { +export default function (platform = 'android', packageName, path) { if (platform !== 'android') { throw new Error(`installApp() missing implementation for platform: ${platform}`); } @@ -22,4 +22,4 @@ module.exports = function (platform = 'android', packageName, path) { Logger.warn('Failed to uninstall app:', e); }) .finally(() => execAsync(`adb install ${path}`)); -}; +} diff --git a/tests/e2e/utils/killApp.js b/tests/e2e/utils/killApp.js index bdef215bf752..32cc75d3ef8a 100644 --- a/tests/e2e/utils/killApp.js +++ b/tests/e2e/utils/killApp.js @@ -1,11 +1,11 @@ -const {APP_PACKAGE} = require('../config'); -const execAsync = require('./execAsync'); +import config from '../config'; +import execAsync from './execAsync'; -module.exports = function (platform = 'android', packageName = APP_PACKAGE) { +export default function (platform = 'android', packageName = config.APP_PACKAGE) { if (platform !== 'android') { throw new Error(`killApp() missing implementation for platform: ${platform}`); } // Use adb to kill the app return execAsync(`adb shell am force-stop ${packageName}`); -}; +} diff --git a/tests/e2e/utils/launchApp.js b/tests/e2e/utils/launchApp.js index f63e2e71cd8b..d5177794bbda 100644 --- a/tests/e2e/utils/launchApp.js +++ b/tests/e2e/utils/launchApp.js @@ -1,8 +1,8 @@ /* eslint-disable rulesdir/prefer-underscore-method */ -const {APP_PACKAGE, ACTIVITY_PATH} = require('../config'); -const execAsync = require('./execAsync'); +import config from '../config'; +import execAsync from './execAsync'; -module.exports = function (platform = 'android', packageName = APP_PACKAGE, activityPath = ACTIVITY_PATH, launchArgs = {}) { +export default function (platform = 'android', packageName = config.APP_PACKAGE, activityPath = config.ACTIVITY_PATH, launchArgs = {}) { if (platform !== 'android') { throw new Error(`launchApp() missing implementation for platform: ${platform}`); } @@ -12,4 +12,4 @@ module.exports = function (platform = 'android', packageName = APP_PACKAGE, acti .map((key) => `${typeof launchArgs[key] === 'boolean' ? '--ez' : '--es'} ${key} ${launchArgs[key]}`) .join(' '); return execAsync(`adb shell am start -n ${packageName}/${activityPath} ${launchArgsString}`); -}; +} diff --git a/tests/e2e/utils/logger.js b/tests/e2e/utils/logger.js index 7da1e8330bfc..aa8be5b84b26 100644 --- a/tests/e2e/utils/logger.js +++ b/tests/e2e/utils/logger.js @@ -1,6 +1,6 @@ -const fs = require('fs'); -const path = require('path'); -const {LOG_FILE} = require('../config'); +import fs from 'fs'; +import path from 'path'; +import CONST from '../config'; let isVerbose = true; const setLogLevelVerbose = (value) => { @@ -23,18 +23,18 @@ const log = (...args) => { } // Write to log file - if (!fs.existsSync(LOG_FILE)) { + if (!fs.existsSync(CONST.LOG_FILE)) { // Check that the directory exists - const logDir = path.dirname(LOG_FILE); + const logDir = path.dirname(CONST.LOG_FILE); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir); } - fs.writeFileSync(LOG_FILE, ''); + fs.writeFileSync(CONST.LOG_FILE, ''); } const time = new Date(); const timeStr = `${time.getHours()}:${time.getMinutes()}:${time.getSeconds()} ${time.getMilliseconds()}`; - fs.appendFileSync(LOG_FILE, `[${timeStr}] ${args.join(' ')}\n`); + fs.appendFileSync(CONST.LOG_FILE, `[${timeStr}] ${args.join(' ')}\n`); }; const info = (...args) => { @@ -99,14 +99,4 @@ const progressInfo = (textParam) => { }; }; -module.exports = { - log, - info, - warn, - note, - error, - success, - important, - progressInfo, - setLogLevelVerbose, -}; +export {log, info, warn, note, error, success, important, progressInfo, setLogLevelVerbose}; diff --git a/tests/e2e/utils/sleep.js b/tests/e2e/utils/sleep.js index 6cc4f3bb89d1..6d37ca3cd510 100644 --- a/tests/e2e/utils/sleep.js +++ b/tests/e2e/utils/sleep.js @@ -1,5 +1,5 @@ -module.exports = function sleep(ms) { +export default function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); -}; +} diff --git a/tests/e2e/utils/withFailTimeout.js b/tests/e2e/utils/withFailTimeout.js index 64c92966cdbc..3a314cd23562 100644 --- a/tests/e2e/utils/withFailTimeout.js +++ b/tests/e2e/utils/withFailTimeout.js @@ -1,6 +1,6 @@ -const {INTERACTION_TIMEOUT} = require('../config'); +import CONFIG from '../config'; -const TIMEOUT = process.env.INTERACTION_TIMEOUT || INTERACTION_TIMEOUT; +const TIMEOUT = process.env.INTERACTION_TIMEOUT || CONFIG.INTERACTION_TIMEOUT; const withFailTimeout = (promise, name) => new Promise((resolve, reject) => { @@ -20,4 +20,4 @@ const withFailTimeout = (promise, name) => }); }); -module.exports = withFailTimeout; +export default withFailTimeout; diff --git a/tests/unit/GithubUtilsTest.js b/tests/unit/GithubUtilsTest.js index b2b84dfd9d9f..ba4510794686 100644 --- a/tests/unit/GithubUtilsTest.js +++ b/tests/unit/GithubUtilsTest.js @@ -1,8 +1,8 @@ /** * @jest-environment node */ -const core = require('@actions/core'); -const GithubUtils = require('../../.github/libs/GithubUtils'); +import * as core from '@actions/core'; +import GithubUtils from '../../.github/libs/GithubUtils'; const mockGetInput = jest.fn(); const mockListIssues = jest.fn(); diff --git a/tests/unit/PaymentUtilsTest.js b/tests/unit/PaymentUtilsTest.js index d4524027353a..209e062b764e 100644 --- a/tests/unit/PaymentUtilsTest.js +++ b/tests/unit/PaymentUtilsTest.js @@ -1,6 +1,5 @@ import CONST from '../../src/CONST'; - -const paymentUtils = require('../../src/libs/PaymentUtils'); +import * as paymentUtils from '../../src/libs/PaymentUtils'; describe('PaymentUtils', () => { it('Test rounding wallet transfer instant fee', () => { diff --git a/tests/unit/TranslateTest.js b/tests/unit/TranslateTest.js index 45d4a12b61f5..d23fa52fc798 100644 --- a/tests/unit/TranslateTest.js +++ b/tests/unit/TranslateTest.js @@ -1,9 +1,9 @@ -const _ = require('underscore'); -const {error: AnnotationError} = require('@actions/core'); -const Localize = require('../../src/libs/Localize'); -const CONFIG = require('../../src/CONFIG'); -const translations = require('../../src/languages/translations'); -const CONST = require('../../src/CONST').default; +import {AnnotationError} from '@actions/core'; +import _ from 'underscore'; +import CONFIG from '../../src/CONFIG'; +import CONST from '../../src/CONST'; +import * as translations from '../../src/languages/translations'; +import * as Localize from '../../src/libs/Localize'; const originalTranslations = _.clone(translations); translations.default = { @@ -41,10 +41,10 @@ describe('translate', () => { }); test('Test when key is not found in default (Production Mode)', () => { - const ORIGINAL_IS_IN_PRODUCTION = CONFIG.default.IS_IN_PRODUCTION; - CONFIG.default.IS_IN_PRODUCTION = true; + const ORIGINAL_IS_IN_PRODUCTION = CONFIG.IS_IN_PRODUCTION; + CONFIG.IS_IN_PRODUCTION = true; expect(Localize.translate(CONST.LOCALES.ES_ES, 'testKey4')).toBe('testKey4'); - CONFIG.default.IS_IN_PRODUCTION = ORIGINAL_IS_IN_PRODUCTION; + CONFIG.IS_IN_PRODUCTION = ORIGINAL_IS_IN_PRODUCTION; }); it('Test when translation value is a function', () => { diff --git a/tests/unit/UrlTest.js b/tests/unit/UrlTest.js index 90ffb9b12d5b..74776ecf791d 100644 --- a/tests/unit/UrlTest.js +++ b/tests/unit/UrlTest.js @@ -1,4 +1,4 @@ -const Url = require('../../src/libs/Url'); +import * as Url from '../../src/libs/Url'; describe('Url', () => { describe('getPathFromURL()', () => { diff --git a/tests/unit/ValidationUtilsTest.js b/tests/unit/ValidationUtilsTest.js index 45de052f714d..43e389600d62 100644 --- a/tests/unit/ValidationUtilsTest.js +++ b/tests/unit/ValidationUtilsTest.js @@ -1,7 +1,6 @@ import {addDays, format, startOfDay, subYears} from 'date-fns'; import CONST from '../../src/CONST'; - -const ValidationUtils = require('../../src/libs/ValidationUtils'); +import * as ValidationUtils from '../../src/libs/ValidationUtils'; describe('ValidationUtils', () => { describe('isValidDate', () => { diff --git a/tests/unit/awaitStagingDeploysTest.js b/tests/unit/awaitStagingDeploysTest.js index 88a78d9260a7..8b8327e99047 100644 --- a/tests/unit/awaitStagingDeploysTest.js +++ b/tests/unit/awaitStagingDeploysTest.js @@ -1,10 +1,10 @@ /** * @jest-environment node */ -const core = require('@actions/core'); -const _ = require('underscore'); -const run = require('../../.github/actions/javascript/awaitStagingDeploys/awaitStagingDeploys'); -const GithubUtils = require('../../.github/libs/GithubUtils'); +import * as core from '@actions/core'; +import _ from 'underscore'; +import run from '../../.github/actions/javascript/awaitStagingDeploys/awaitStagingDeploys'; +import GithubUtils from '../../.github/libs/GithubUtils'; // Lower poll rate to speed up tests const TEST_POLL_RATE = 1; diff --git a/tests/unit/checkDeployBlockersTest.js b/tests/unit/checkDeployBlockersTest.js index dca8d5752767..354ab132f601 100644 --- a/tests/unit/checkDeployBlockersTest.js +++ b/tests/unit/checkDeployBlockersTest.js @@ -1,10 +1,10 @@ /** * @jest-environment node */ -const _ = require('underscore'); -const core = require('@actions/core'); -const GithubUtils = require('../../.github/libs/GithubUtils'); -const run = require('../../.github/actions/javascript/checkDeployBlockers/checkDeployBlockers'); +import * as core from '@actions/core'; +import _ from 'underscore'; +import run from '../../.github/actions/javascript/checkDeployBlockers/checkDeployBlockers'; +import GithubUtils from '../../.github/libs/GithubUtils'; // Static mock function for core.getInput const mockGetInput = jest.fn().mockImplementation((arg) => { diff --git a/tests/unit/createOrUpdateStagingDeployTest.js b/tests/unit/createOrUpdateStagingDeployTest.js index 15a795842667..75600f0d4fc8 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.js +++ b/tests/unit/createOrUpdateStagingDeployTest.js @@ -1,14 +1,14 @@ /** * @jest-environment node */ -const core = require('@actions/core'); -const fns = require('date-fns'); -const {vol} = require('memfs'); -const path = require('path'); -const CONST = require('../../.github/libs/CONST'); -const GitUtils = require('../../.github/libs/GitUtils').default; -const GithubUtils = require('../../.github/libs/GithubUtils'); -const run = require('../../.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy'); +import * as core from '@actions/core'; +import * as fns from 'date-fns'; +import {vol} from 'memfs'; +import path from 'path'; +import run from '../../.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy'; +import CONST from '../../.github/libs/CONST'; +import GithubUtils from '../../.github/libs/GithubUtils'; +import GitUtils from '../../.github/libs/GitUtils'; const PATH_TO_PACKAGE_JSON = path.resolve(__dirname, '../../package.json'); diff --git a/tests/unit/isStagingDeployLockedTest.js b/tests/unit/isStagingDeployLockedTest.js index 5a4e81414393..bddd3d851c00 100644 --- a/tests/unit/isStagingDeployLockedTest.js +++ b/tests/unit/isStagingDeployLockedTest.js @@ -1,6 +1,6 @@ -const core = require('@actions/core'); -const run = require('../../.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked'); -const GithubUtils = require('../../.github/libs/GithubUtils'); +import * as core from '@actions/core'; +import run from '../../.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked'; +import GithubUtils from '../../.github/libs/GithubUtils'; // Mock the entire GithubUtils module jest.mock('../../.github/libs/GithubUtils'); diff --git a/tests/unit/markPullRequestsAsDeployedTest.js b/tests/unit/markPullRequestsAsDeployedTest.js index 81da0a5c9371..a401a9f96a67 100644 --- a/tests/unit/markPullRequestsAsDeployedTest.js +++ b/tests/unit/markPullRequestsAsDeployedTest.js @@ -1,9 +1,9 @@ /** * @jest-environment node */ -const _ = require('underscore'); -const GitUtils = require('../../.github/libs/GitUtils').default; -const GithubUtils = require('../../.github/libs/GithubUtils'); +import _ from 'underscore'; +import GithubUtils from '../../.github/libs/GithubUtils'; +import GitUtils from '../../.github/libs/GitUtils'; let run; diff --git a/tests/unit/nativeVersionUpdaterTest.js b/tests/unit/nativeVersionUpdaterTest.js index 6e30073fbbdc..198583a3f4e1 100644 --- a/tests/unit/nativeVersionUpdaterTest.js +++ b/tests/unit/nativeVersionUpdaterTest.js @@ -1,7 +1,8 @@ -const fs = require('fs'); -const path = require('path'); -const {vol} = require('memfs'); -const {updateAndroidVersion, generateAndroidVersionCode} = require('../../.github/libs/nativeVersionUpdater'); +import fs from 'fs'; +import {vol} from 'memfs'; +import path from 'path'; +// eslint-disable-next-line rulesdir/prefer-import-module-contents +import {generateAndroidVersionCode, updateAndroidVersion} from '../../.github/libs/nativeVersionUpdater'; const BUILD_GRADLE_PATH = path.resolve(__dirname, '../../android/app/build.gradle'); diff --git a/tests/unit/postTestBuildComment.js b/tests/unit/postTestBuildComment.js index 50389808b77d..ff77ca190c08 100644 --- a/tests/unit/postTestBuildComment.js +++ b/tests/unit/postTestBuildComment.js @@ -1,7 +1,7 @@ -const {when} = require('jest-when'); - -const core = require('@actions/core'); -const GithubUtils = require('../../.github/libs/GithubUtils'); +import * as core from '@actions/core'; +import {when} from 'jest-when'; +import ghAction from '../../.github/actions/javascript/postTestBuildComment/postTestBuildComment'; +import GithubUtils from '../../.github/libs/GithubUtils'; const mockGetInput = jest.fn(); const mockCreateComment = jest.fn(); @@ -25,8 +25,6 @@ jest.mock('@actions/github', () => ({ }, })); -const ghAction = require('../../.github/actions/javascript/postTestBuildComment/postTestBuildComment'); - const androidLink = 'https://expensify.app/ANDROID_LINK'; const iOSLink = 'https://expensify.app/IOS_LINK'; const webLink = 'https://expensify.app/WEB_LINK'; diff --git a/tests/unit/versionUpdaterTest.js b/tests/unit/versionUpdaterTest.js index 2e88f8dde3f7..cb0b0754f930 100644 --- a/tests/unit/versionUpdaterTest.js +++ b/tests/unit/versionUpdaterTest.js @@ -1,4 +1,4 @@ -const versionUpdater = require('../../.github/libs/versionUpdater'); +import * as versionUpdater from '../../.github/libs/versionUpdater'; const VERSION = '2.3.9-80'; const VERSION_NUMBER = [2, 3, 9, 80]; From 8bc9ea9b66afc514176fdef950dfb3d09dc9705c Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 14 Feb 2024 18:24:41 +0100 Subject: [PATCH 112/225] fix a predicted report status --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7fca6614f1a1..e5cf350c70b3 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -873,7 +873,7 @@ function getMoneyRequestInformation( } : {}; - const optimisticNextStep = NextStepUtils.buildNextStep(iouReport, needsToBeManuallySubmitted ? CONST.REPORT.STATUS_NUM.OPEN : CONST.REPORT.STATUS_NUM.SUBMITTED); + const optimisticNextStep = NextStepUtils.buildNextStep(iouReport, CONST.REPORT.STATUS_NUM.OPEN); // STEP 5: Build Onyx Data const [optimisticData, successData, failureData] = buildOnyxDataForMoneyRequest( From c37aedae7439c9f5c9f419cda43bad760fb35f4a Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 15 Feb 2024 00:32:54 +0700 Subject: [PATCH 113/225] fix: delay when change default skin color --- src/components/EmojiPicker/EmojiSkinToneList.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/EmojiPicker/EmojiSkinToneList.js b/src/components/EmojiPicker/EmojiSkinToneList.js index da2559535895..d1cb541b7fb9 100644 --- a/src/components/EmojiPicker/EmojiSkinToneList.js +++ b/src/components/EmojiPicker/EmojiSkinToneList.js @@ -1,4 +1,4 @@ -import React, {useCallback, useState} from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import * as Emojis from '@assets/emojis'; @@ -27,11 +27,17 @@ function EmojiSkinToneList() { * @param {object} skinToneEmoji */ function updateSelectedSkinTone(skinToneEmoji) { - toggleIsSkinToneListVisible(); setHighlightedIndex(skinToneEmoji.skinTone); setPreferredSkinTone(skinToneEmoji.skinTone); } + useEffect(() => { + if (!isSkinToneListVisible) { + return; + } + toggleIsSkinToneListVisible(); + }, [preferredSkinTone]); + const currentSkinTone = getSkinToneEmojiFromIndex(preferredSkinTone); return ( From 28931163ed3690070bf3ebadf8da7ac0dea04ace Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 15 Feb 2024 00:47:17 +0700 Subject: [PATCH 114/225] fix lint --- src/components/EmojiPicker/EmojiSkinToneList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/EmojiPicker/EmojiSkinToneList.js b/src/components/EmojiPicker/EmojiSkinToneList.js index d1cb541b7fb9..62731928db23 100644 --- a/src/components/EmojiPicker/EmojiSkinToneList.js +++ b/src/components/EmojiPicker/EmojiSkinToneList.js @@ -36,6 +36,7 @@ function EmojiSkinToneList() { return; } toggleIsSkinToneListVisible(); + // eslint-disable-next-line react-hooks/exhaustive-deps -- only run when preferredSkinTone updates }, [preferredSkinTone]); const currentSkinTone = getSkinToneEmojiFromIndex(preferredSkinTone); From 98648cad03ce3bd86211f38c2723a9f9dc04da57 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 14 Feb 2024 20:03:45 +0100 Subject: [PATCH 115/225] Fix imports --- tests/e2e/compare/compare.js | 2 +- tests/e2e/compare/output/console.js | 2 +- tests/e2e/compare/output/markdown.js | 4 ++-- tests/e2e/nativeCommands/adbBackspace.js | 2 +- tests/e2e/nativeCommands/adbTypeText.js | 2 +- tests/e2e/server/index.js | 2 +- tests/e2e/testRunner.js | 13 +++++++------ tests/e2e/utils/execAsync.js | 2 +- tests/e2e/utils/installApp.js | 2 +- 9 files changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/e2e/compare/compare.js b/tests/e2e/compare/compare.js index b7d5c15c5f82..5ae8c8b74578 100644 --- a/tests/e2e/compare/compare.js +++ b/tests/e2e/compare/compare.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import getStats from '../measure/math'; -import math from './math'; +import * as math from './math'; import printToConsole from './output/console'; import writeToMarkdown from './output/markdown'; diff --git a/tests/e2e/compare/output/console.js b/tests/e2e/compare/output/console.js index 61645baf38b8..45d665bf8404 100644 --- a/tests/e2e/compare/output/console.js +++ b/tests/e2e/compare/output/console.js @@ -1,4 +1,4 @@ -import format from './format'; +import * as format from './format'; const printRegularLine = (entry) => { console.debug(` - ${entry.name}: ${format.formatDurationDiffChange(entry)}`); diff --git a/tests/e2e/compare/output/markdown.js b/tests/e2e/compare/output/markdown.js index 1f9626678498..119830a5bb2c 100644 --- a/tests/e2e/compare/output/markdown.js +++ b/tests/e2e/compare/output/markdown.js @@ -2,8 +2,8 @@ import fs from 'node:fs/promises'; import path from 'path'; import _ from 'underscore'; -import Logger from '../../utils/logger'; -import format from './format'; +import * as Logger from '../../utils/logger'; +import * as format from './format'; import markdownTable from './markdownTable'; const tableHeader = ['Name', 'Duration']; diff --git a/tests/e2e/nativeCommands/adbBackspace.js b/tests/e2e/nativeCommands/adbBackspace.js index c0dcf65c240c..7b01ed58080d 100644 --- a/tests/e2e/nativeCommands/adbBackspace.js +++ b/tests/e2e/nativeCommands/adbBackspace.js @@ -1,5 +1,5 @@ import execAsync from '../utils/execAsync'; -import Logger from '../utils/logger'; +import * as Logger from '../utils/logger'; const adbBackspace = async () => { Logger.log(`🔙 Pressing backspace`); diff --git a/tests/e2e/nativeCommands/adbTypeText.js b/tests/e2e/nativeCommands/adbTypeText.js index 7a4c3b31b7fd..b8c6cfd09c29 100644 --- a/tests/e2e/nativeCommands/adbTypeText.js +++ b/tests/e2e/nativeCommands/adbTypeText.js @@ -1,5 +1,5 @@ import execAsync from '../utils/execAsync'; -import Logger from '../utils/logger'; +import * as Logger from '../utils/logger'; const adbTypeText = async (text) => { Logger.log(`📝 Typing text: ${text}`); diff --git a/tests/e2e/server/index.js b/tests/e2e/server/index.js index b90e96696525..82152245d8e2 100644 --- a/tests/e2e/server/index.js +++ b/tests/e2e/server/index.js @@ -1,7 +1,7 @@ import {createServer} from 'http'; import config from '../config'; import * as nativeCommands from '../nativeCommands'; -import Logger from '../utils/logger'; +import * as Logger from '../utils/logger'; import Routes from './routes'; const PORT = process.env.PORT || config.SERVER_PORT; diff --git a/tests/e2e/testRunner.js b/tests/e2e/testRunner.js index f6e874c4f421..2b928ce3dc73 100644 --- a/tests/e2e/testRunner.js +++ b/tests/e2e/testRunner.js @@ -14,6 +14,7 @@ */ /* eslint-disable @lwc/lwc/no-async-await,no-restricted-syntax,no-await-in-loop */ +import {execSync} from 'child_process'; import fs from 'fs'; import _ from 'underscore'; import compare from './compare/compare'; @@ -24,7 +25,7 @@ import execAsync from './utils/execAsync'; import installApp from './utils/installApp'; import killApp from './utils/killApp'; import launchApp from './utils/launchApp'; -import Logger from './utils/logger'; +import * as Logger from './utils/logger'; import sleep from './utils/sleep'; import withFailTimeout from './utils/withFailTimeout'; @@ -413,13 +414,13 @@ const run = async () => { Logger.info('\n\nE2E test suite failed due to error:', e, '\nPrinting full logs:\n\n'); // Write logcat, meminfo, emulator info to file as well: - require('child_process').execSync(`adb logcat -d > ${config.OUTPUT_DIR}/logcat.txt`); - require('child_process').execSync(`adb shell "cat /proc/meminfo" > ${config.OUTPUT_DIR}/meminfo.txt`); - require('child_process').execSync(`adb shell "getprop" > ${config.OUTPUT_DIR}/emulator-properties.txt`); + execSync(`adb logcat -d > ${config.OUTPUT_DIR}/logcat.txt`); + execSync(`adb shell "cat /proc/meminfo" > ${config.OUTPUT_DIR}/meminfo.txt`); + execSync(`adb shell "getprop" > ${config.OUTPUT_DIR}/emulator-properties.txt`); - require('child_process').execSync(`cat ${config.LOG_FILE}`); + execSync(`cat ${config.LOG_FILE}`); try { - require('child_process').execSync(`cat ~/.android/avd/${process.env.AVD_NAME || 'test'}.avd/config.ini > ${config.OUTPUT_DIR}/emulator-config.ini`); + execSync(`cat ~/.android/avd/${process.env.AVD_NAME || 'test'}.avd/config.ini > ${config.OUTPUT_DIR}/emulator-config.ini`); } catch (ignoredError) { // the error is ignored, as the file might not exist if the test // run wasn't started with an emulator diff --git a/tests/e2e/utils/execAsync.js b/tests/e2e/utils/execAsync.js index fad44082e40c..9abc41105f7e 100644 --- a/tests/e2e/utils/execAsync.js +++ b/tests/e2e/utils/execAsync.js @@ -1,5 +1,5 @@ import {exec} from 'child_process'; -import Logger from './logger'; +import * as Logger from './logger'; /** * Executes a command none-blocking by wrapping it in a promise. diff --git a/tests/e2e/utils/installApp.js b/tests/e2e/utils/installApp.js index 331ae5e99dee..50fc8602762e 100644 --- a/tests/e2e/utils/installApp.js +++ b/tests/e2e/utils/installApp.js @@ -1,5 +1,5 @@ import execAsync from './execAsync'; -import Logger from './logger'; +import * as Logger from './logger'; /** * Installs the app on the currently connected device for the given platform. From f035ea08b399375e762160f45ee0aa947316b012 Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Thu, 15 Feb 2024 01:56:48 +0100 Subject: [PATCH 116/225] Update naming of animated component Co-authored-by: Tomek Zawadzki --- src/components/Composer/index.native.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index 166be722c794..6691c068eb3a 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {TextInput} from 'react-native'; import {StyleSheet} from 'react-native'; import type {AnimatedMarkdownTextInputRef} from '@components/RNMarkdownTextInput'; -import MarkdownTextInput from '@components/RNMarkdownTextInput'; +import RNMarkdownTextInput from '@components/RNMarkdownTextInput'; import useMarkdownStyle from '@hooks/useMarkdownStyle'; import useResetComposerFocus from '@hooks/useResetComposerFocus'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -68,7 +68,7 @@ function Composer( const composerStyle = useMemo(() => StyleSheet.flatten(style), [style]); return ( - Date: Thu, 15 Feb 2024 10:54:38 +0700 Subject: [PATCH 117/225] display deleted request in transaction thread report --- src/pages/home/report/ReportActionItem.js | 37 +++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 39a5fcaa4ee0..d2f3c4584fd0 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -627,6 +627,28 @@ function ReportActionItem(props) { if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) { const parentReportAction = props.parentReportActions[props.report.parentReportActionID]; if (ReportActionsUtils.isTransactionThread(parentReportAction)) { + const isReversedTransaction = ReportActionsUtils.isReversedTransaction(parentReportAction); + if (ReportActionsUtils.isDeletedParentAction(parentReportAction) || isReversedTransaction) { + return ( + + + + + + ${props.translate(isReversedTransaction ? 'parentReportAction.reversedTransaction' : 'parentReportAction.deletedRequest')}`} + /> + + + + + + ); + } return ( ${props.translate('parentReportAction.deletedTask')}`} /> - + ); } @@ -857,9 +879,10 @@ export default compose( }, }), )( - memo( - ReportActionItem, - (prevProps, nextProps) => + memo(ReportActionItem, (prevProps, nextProps) => { + const prevParentReportAction = prevProps.parentReportActions[prevProps.report.parentReportActionID]; + const nextParentReportAction = nextProps.parentReportActions[nextProps.report.parentReportActionID]; + return ( prevProps.displayAsGroup === nextProps.displayAsGroup && prevProps.draftMessage === nextProps.draftMessage && prevProps.isMostRecentIOUReportAction === nextProps.isMostRecentIOUReportAction && @@ -888,6 +911,8 @@ export default compose( prevProps.linkedReportActionID === nextProps.linkedReportActionID && _.isEqual(prevProps.policyReportFields, nextProps.policyReportFields) && _.isEqual(prevProps.report.reportFields, nextProps.report.reportFields) && - _.isEqual(prevProps.policy, nextProps.policy), - ), + _.isEqual(prevProps.policy, nextProps.policy) && + _.isEqual(prevParentReportAction, nextParentReportAction) + ); + }), ); From 97b59c265019503526e9270ee5b80192fc0e3e10 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 15 Feb 2024 11:03:37 +0700 Subject: [PATCH 118/225] remove divider line --- src/pages/home/report/ReportActionItem.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index d2f3c4584fd0..1fa2b0a70b19 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -630,7 +630,7 @@ function ReportActionItem(props) { const isReversedTransaction = ReportActionsUtils.isReversedTransaction(parentReportAction); if (ReportActionsUtils.isDeletedParentAction(parentReportAction) || isReversedTransaction) { return ( - + @@ -645,7 +645,6 @@ function ReportActionItem(props) { - ); } From 47737c48a94838f2db5bf50f3eab8b0a27ced3fe Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 15 Feb 2024 11:19:56 +0700 Subject: [PATCH 119/225] fix lint --- src/components/EmojiPicker/EmojiSkinToneList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/EmojiPicker/EmojiSkinToneList.js b/src/components/EmojiPicker/EmojiSkinToneList.js index 62731928db23..7f36d929025f 100644 --- a/src/components/EmojiPicker/EmojiSkinToneList.js +++ b/src/components/EmojiPicker/EmojiSkinToneList.js @@ -36,7 +36,7 @@ function EmojiSkinToneList() { return; } toggleIsSkinToneListVisible(); - // eslint-disable-next-line react-hooks/exhaustive-deps -- only run when preferredSkinTone updates + // eslint-disable-next-line react-hooks/exhaustive-deps -- only run when preferredSkinTone updates }, [preferredSkinTone]); const currentSkinTone = getSkinToneEmojiFromIndex(preferredSkinTone); From 785f2f28df1b84c2860f1d348212452320cd5a88 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 15 Feb 2024 14:17:40 +0700 Subject: [PATCH 120/225] show keyboard shortcuts page modal wherever you are in the app --- src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index 427ca2251e88..f79e275007d7 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -34,7 +34,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS, ], [SCREENS.SETTINGS.SECURITY]: [SCREENS.SETTINGS.TWO_FACTOR_AUTH, SCREENS.SETTINGS.CLOSE], - [SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS, SCREENS.KEYBOARD_SHORTCUTS, SCREENS.SETTINGS.TROUBLESHOOT], + [SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS, SCREENS.SETTINGS.TROUBLESHOOT], }; export default FULL_SCREEN_TO_RHP_MAPPING; From cfb47f6b9501f91b963a693f03648362462d3bf2 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Thu, 15 Feb 2024 09:11:26 +0100 Subject: [PATCH 121/225] C+ review fixes --- src/components/ValuePicker/ValueSelectorModal.tsx | 7 +++++-- src/components/ValuePicker/index.tsx | 3 ++- src/components/ValuePicker/types.ts | 12 +++++++++--- src/pages/workspace/WorkspaceNewRoomPage.js | 8 ++++---- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/components/ValuePicker/ValueSelectorModal.tsx b/src/components/ValuePicker/ValueSelectorModal.tsx index 539fa3c5d2ae..1e7c6088241d 100644 --- a/src/components/ValuePicker/ValueSelectorModal.tsx +++ b/src/components/ValuePicker/ValueSelectorModal.tsx @@ -10,7 +10,10 @@ import type {ValueSelectorModalProps} from './types'; function ValueSelectorModal({items = [], selectedItem, label = '', isVisible, onClose, onItemSelected, shouldShowTooltips = true}: ValueSelectorModalProps) { const styles = useThemeStyles(); - const sectionsData = useMemo(() => [{data: items.map((item) => ({...item, isSelected: item === selectedItem, keyForList: item.value ?? ''}))}], [items, selectedItem]); + const sections = useMemo( + () => [{data: items.map((item) => ({value: item.value, alternateText: item.description, text: item.label ?? '', isSelected: item === selectedItem, keyForList: item.value ?? ''}))}], + [items, selectedItem], + ); return ( onItemSelected?.(item)} initiallyFocusedOptionKey={selectedItem?.value} shouldStopPropagation diff --git a/src/components/ValuePicker/index.tsx b/src/components/ValuePicker/index.tsx index 4521fed332f5..cc8a0edf0b97 100644 --- a/src/components/ValuePicker/index.tsx +++ b/src/components/ValuePicker/index.tsx @@ -37,7 +37,8 @@ function ValuePicker({value, label, items, placeholder = '', errorText = '', onI void; + onItemSelected?: (item: ValuePickerListItem) => void; /** Function to call when the user closes the modal */ onClose?: () => void; @@ -54,4 +60,4 @@ type ValuePickerProps = { shouldShowTooltips?: boolean; }; -export type {ValuePickerItem, ValueSelectorModalProps, ValuePickerProps}; +export type {ValuePickerItem, ValueSelectorModalProps, ValuePickerProps, ValuePickerListItem}; diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index f6654966f77d..36f874e8919d 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -111,7 +111,7 @@ function WorkspaceNewRoomPage(props) { _.map( _.filter(PolicyUtils.getActivePolicies(props.policies), (policy) => policy.type !== CONST.POLICY.TYPE.PERSONAL), (policy) => ({ - text: policy.name, + label: policy.name, value: policy.id, }), ).sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase())), @@ -228,7 +228,7 @@ function WorkspaceNewRoomPage(props) { () => _.map(CONST.REPORT.WRITE_CAPABILITIES, (value) => ({ value, - text: translate(`writeCapabilityPage.writeCapability.${value}`), + label: translate(`writeCapabilityPage.writeCapability.${value}`), })), [translate], ); @@ -238,9 +238,9 @@ function WorkspaceNewRoomPage(props) { _.map( _.filter(_.values(CONST.REPORT.VISIBILITY), (visibilityOption) => visibilityOption !== CONST.REPORT.VISIBILITY.PUBLIC_ANNOUNCE), (visibilityOption) => ({ - text: translate(`newRoomPage.visibilityOptions.${visibilityOption}`), + label: translate(`newRoomPage.visibilityOptions.${visibilityOption}`), value: visibilityOption, - alternateText: translate(`newRoomPage.${visibilityOption}Description`), + description: translate(`newRoomPage.${visibilityOption}Description`), }), ), [translate], From 19c5c0d02397c404d168918dd502ae570343c428 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Thu, 15 Feb 2024 09:26:28 +0100 Subject: [PATCH 122/225] Fix failing typescript --- .../DatePicker/CalendarPicker/YearPickerModal.tsx | 6 +++--- src/components/DatePicker/CalendarPicker/index.tsx | 4 ++-- src/components/DatePicker/CalendarPicker/types.ts | 6 +++--- src/components/SelectionList/types.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx index 21c57f79f39c..8f2acc6fbd3c 100644 --- a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx +++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx @@ -6,14 +6,14 @@ import SelectionList from '@components/SelectionList'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -import type CalendarPickerRadioItem from './types'; +import type CalendarPickerListItem from './types'; type YearPickerModalProps = { /** Whether the modal is visible */ isVisible: boolean; /** The list of years to render */ - years: CalendarPickerRadioItem[]; + years: CalendarPickerListItem[]; /** Currently selected year */ currentYear?: number; @@ -72,7 +72,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear inputMode={CONST.INPUT_MODE.NUMERIC} headerMessage={headerMessage} sections={sections} - onSelectRow={(option: CalendarPickerRadioItem) => { + onSelectRow={(option) => { onYearChange?.(option.value); }} initiallyFocusedOptionKey={currentYear.toString()} diff --git a/src/components/DatePicker/CalendarPicker/index.tsx b/src/components/DatePicker/CalendarPicker/index.tsx index 60673f9a28d2..1b290aacd30d 100644 --- a/src/components/DatePicker/CalendarPicker/index.tsx +++ b/src/components/DatePicker/CalendarPicker/index.tsx @@ -13,7 +13,7 @@ import getButtonState from '@libs/getButtonState'; import CONST from '@src/CONST'; import ArrowIcon from './ArrowIcon'; import generateMonthMatrix from './generateMonthMatrix'; -import type CalendarPickerRadioItem from './types'; +import type CalendarPickerListItem from './types'; import YearPickerModal from './YearPickerModal'; type CalendarPickerProps = { @@ -59,7 +59,7 @@ function CalendarPicker({ const minYear = getYear(new Date(minDate)); const maxYear = getYear(new Date(maxDate)); - const [years, setYears] = useState( + const [years, setYears] = useState( Array.from({length: maxYear - minYear + 1}, (v, i) => i + minYear).map((year) => ({ text: year.toString(), value: year, diff --git a/src/components/DatePicker/CalendarPicker/types.ts b/src/components/DatePicker/CalendarPicker/types.ts index ce5e16d50535..3183b3a82b6f 100644 --- a/src/components/DatePicker/CalendarPicker/types.ts +++ b/src/components/DatePicker/CalendarPicker/types.ts @@ -1,8 +1,8 @@ -import type {RadioItem} from '@components/SelectionList/types'; +import type {ListItem} from '@components/SelectionList/types'; -type CalendarPickerRadioItem = RadioItem & { +type CalendarPickerListItem = ListItem & { /** The value representing a year in the CalendarPicker */ value: number; }; -export default CalendarPickerRadioItem; +export default CalendarPickerListItem; diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 75818746a22c..d5ad637dfc8d 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -49,7 +49,7 @@ type ListItem = { isDisabled?: boolean; /** User accountID */ - accountID?: number; + accountID?: number | null; /** User login */ login?: string; From 8ce65936483ccee0061f500083b924b5b1cf5733 Mon Sep 17 00:00:00 2001 From: Github Date: Thu, 15 Feb 2024 09:34:02 +0100 Subject: [PATCH 123/225] Fix workflow merging issue for performance tests --- .github/workflows/reassurePerformanceTests.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/reassurePerformanceTests.yml b/.github/workflows/reassurePerformanceTests.yml index ebe31f41f3d8..7a293561de18 100644 --- a/.github/workflows/reassurePerformanceTests.yml +++ b/.github/workflows/reassurePerformanceTests.yml @@ -13,6 +13,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup NodeJS uses: ./.github/actions/composite/setupNode @@ -22,6 +24,22 @@ jobs: git config --global user.email "test@test.com" git config --global user.name "Test" + - name: Get common ancestor commit + run: | + git fetch origin main + common_ancestor=$(git merge-base "${{ github.sha }}" origin/main) + echo "COMMIT_HASH=$common_ancestor" >> "$GITHUB_ENV" + + - name: Clean up deleted files + run: | + DELETED_FILES=$(git diff --name-only --diff-filter=D "$COMMIT_HASH" "${{ github.sha }}") + for file in $DELETED_FILES; do + if [ -n "$file" ]; then + rm -f "$file" + echo "Deleted file: $file" + fi + done + - name: Run performance testing script shell: bash run: | From cd3b7386ec28656f5aceeb23b088f95ce9a582c4 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Thu, 15 Feb 2024 16:32:53 +0530 Subject: [PATCH 124/225] fix: Chat - Font size is smaller in single backtick code block. Signed-off-by: Krishna Gupta --- src/styles/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 19f5b791a9af..3b923862ee86 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -179,10 +179,9 @@ const webViewStyles = (theme: ThemeColors) => pre: { ...baseCodeTagStyles(theme), - paddingTop: 12, - paddingBottom: 12, - paddingRight: 8, - paddingLeft: 8, + paddingVertical: 8, + paddingHorizontal: 12, + fontSize: 13, fontFamily: FontUtils.fontFamily.platform.MONOSPACE, marginTop: 0, marginBottom: 0, From c103561ae9f4a4522b1732bf9ea394e091ed1745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Thu, 15 Feb 2024 14:40:31 +0100 Subject: [PATCH 125/225] fix merge issues --- src/pages/home/sidebar/SidebarLinksData.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index 294f8e8b5112..828077097580 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -320,6 +320,11 @@ export default compose( key: ONYXKEYS.BETAS, initialValue: [], }, + allReportActions: { + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + selector: reportActionsSelector, + initialValue: {}, + }, policies: { key: ONYXKEYS.COLLECTION.POLICY, selector: policySelector, From f32c3a7dacca9a148d217a500ed849276235af01 Mon Sep 17 00:00:00 2001 From: filip-solecki <153545991+filip-solecki@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:08:43 +0100 Subject: [PATCH 126/225] Update .github/libs/GitUtils.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Błażej Kustra <46095609+blazejkustra@users.noreply.github.com> --- .github/libs/GitUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/libs/GitUtils.ts b/.github/libs/GitUtils.ts index 051a3f3dc40b..e7423f97a934 100644 --- a/.github/libs/GitUtils.ts +++ b/.github/libs/GitUtils.ts @@ -10,7 +10,7 @@ type CommitType = { }; /** - * When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) + * @param [shallowExcludeTag] When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) */ function fetchTag(tag: string, shallowExcludeTag = '') { let shouldRetry = true; From 51d248fbb7e525a9881797b685cd3d4358e61873 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Thu, 15 Feb 2024 15:22:59 +0100 Subject: [PATCH 127/225] Fix gh-actions --- .github/actions/javascript/createOrUpdateStagingDeploy/index.js | 2 +- .github/actions/javascript/getDeployPullRequestList/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index c09c1b8621f3..541a0b9683d0 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -16719,7 +16719,7 @@ const CONST = __importStar(__nccwpck_require__(4097)); const sanitizeStringForJSONParse_1 = __importDefault(__nccwpck_require__(9338)); const VERSION_UPDATER = __importStar(__nccwpck_require__(8007)); /** - * When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) + * @param [shallowExcludeTag] When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) */ function fetchTag(tag, shallowExcludeTag = '') { let shouldRetry = true; diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 837dfd82adf1..e1d50f61da1f 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -13951,7 +13951,7 @@ const CONST = __importStar(__nccwpck_require__(4097)); const sanitizeStringForJSONParse_1 = __importDefault(__nccwpck_require__(9338)); const VERSION_UPDATER = __importStar(__nccwpck_require__(8007)); /** - * When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) + * @param [shallowExcludeTag] When fetching the given tag, exclude all history reachable by the shallowExcludeTag (used to make fetch much faster) */ function fetchTag(tag, shallowExcludeTag = '') { let shouldRetry = true; From 9a9e7c0e917527c42be75415af6cfd82986728c2 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 15 Feb 2024 20:03:01 +0530 Subject: [PATCH 128/225] Fixed state reset on going back from amount step --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 8c94bb32ffa9..caf8a1c4fe95 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -417,9 +417,8 @@ function IOURequestStepConfirmation({ }, ]} /> - {isLoading ? ( - - ) : ( + {isLoading && } + - )} + )} From d021a164adeacc0625ee36ce2a8eaece3d130851 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 15 Feb 2024 22:08:23 +0700 Subject: [PATCH 129/225] fix logic get year --- src/CONST.ts | 2 +- src/components/DatePicker/CalendarPicker/YearPickerModal.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 5c99c5877559..bfbe51e1f755 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -123,7 +123,7 @@ const CONST = { }, DATE_BIRTH: { - MIN_AGE: 5, + MIN_AGE: 0, MIN_AGE_FOR_PAYMENT: 18, MAX_AGE: 150, }, diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx index 21c57f79f39c..cf1c5fd7ab93 100644 --- a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx +++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx @@ -33,7 +33,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear const yearsList = searchText === '' ? years : years.filter((year) => year.text.includes(searchText)); return { headerMessage: !yearsList.length ? translate('common.noResultsFound') : '', - sections: [{data: yearsList, indexOffset: 0}], + sections: [{data: yearsList.reverse(), indexOffset: 0}], }; }, [years, searchText, translate]); @@ -78,6 +78,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear initiallyFocusedOptionKey={currentYear.toString()} showScrollIndicator shouldStopPropagation + shouldUseDynamicMaxToRenderPerBatch /> From 547d4eb6b250100b48a9f62b8599e4d37d99c2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Thu, 15 Feb 2024 16:22:28 +0100 Subject: [PATCH 130/225] setup collator --- src/libs/LocaleCompare.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/libs/LocaleCompare.ts diff --git a/src/libs/LocaleCompare.ts b/src/libs/LocaleCompare.ts new file mode 100644 index 000000000000..5142c5b43d9a --- /dev/null +++ b/src/libs/LocaleCompare.ts @@ -0,0 +1,21 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const DEFAULT_LOCALE = 'en'; + +const COLLATOR_OPTIONS: Intl.CollatorOptions = {usage: 'sort', sensitivity: 'base'}; + +let collator = new Intl.Collator(DEFAULT_LOCALE, COLLATOR_OPTIONS); + +Onyx.connect({ + key: ONYXKEYS.NVP_PREFERRED_LOCALE, + callback: (locale) => { + collator = new Intl.Collator(locale ?? DEFAULT_LOCALE, COLLATOR_OPTIONS); + }, +}); + +function localeCompare(a: string, b: string) { + return collator.compare(a, b); +} + +export default localeCompare; From 2c430dd4d97afcbf36a7981d146ab2fa18ffe65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Thu, 15 Feb 2024 16:26:18 +0100 Subject: [PATCH 131/225] replace localeCompare with collator in getOrderedReportIDs --- src/libs/SidebarUtils.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 2347f5b9f5c5..695ca8a570a2 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -13,6 +13,7 @@ import type {ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import * as CollectionUtils from './CollectionUtils'; +import localeCompare from './localeCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as OptionsListUtils from './OptionsListUtils'; @@ -200,20 +201,20 @@ function getOrderedReportIDs( }); // Sort each group of reports accordingly - pinnedAndGBRReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0)); - draftReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0)); + pinnedAndGBRReports.sort((a, b) => (a?.displayName && b?.displayName ? localeCompare(a.displayName, b.displayName) : 0)); + draftReports.sort((a, b) => (a?.displayName && b?.displayName ? localeCompare(a.displayName, b.displayName) : 0)); if (isInDefaultMode) { nonArchivedReports.sort((a, b) => { const compareDates = a?.lastVisibleActionCreated && b?.lastVisibleActionCreated ? compareStringDates(b.lastVisibleActionCreated, a.lastVisibleActionCreated) : 0; - const compareDisplayNames = a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0; + const compareDisplayNames = a?.displayName && b?.displayName ? localeCompare(a.displayName, b.displayName) : 0; return compareDates || compareDisplayNames; }); // For archived reports ensure that most recent reports are at the top by reversing the order archivedReports.sort((a, b) => (a?.lastVisibleActionCreated && b?.lastVisibleActionCreated ? compareStringDates(b.lastVisibleActionCreated, a.lastVisibleActionCreated) : 0)); } else { - nonArchivedReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0)); - archivedReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0)); + nonArchivedReports.sort((a, b) => (a?.displayName && b?.displayName ? localeCompare(a.displayName, b.displayName) : 0)); + archivedReports.sort((a, b) => (a?.displayName && b?.displayName ? localeCompare(a.displayName, b.displayName) : 0)); } // Now that we have all the reports grouped and sorted, they must be flattened into an array and only return the reportID. From b0831796244d18e39a77978ecca6d9e64d4ffeea Mon Sep 17 00:00:00 2001 From: Vadym Date: Thu, 15 Feb 2024 16:32:18 +0100 Subject: [PATCH 132/225] chore: adds white space for better readability --- src/pages/home/report/ReactionList/BaseReactionList.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/home/report/ReactionList/BaseReactionList.tsx b/src/pages/home/report/ReactionList/BaseReactionList.tsx index 09471faf0b42..d1b0d8c4ca84 100755 --- a/src/pages/home/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/home/report/ReactionList/BaseReactionList.tsx @@ -43,6 +43,7 @@ const getItemLayout = (data: ArrayLike | null | undefined, inde function BaseReactionList({hasUserReacted = false, users, isVisible = false, emojiCodes, emojiCount, emojiName, onClose}: BaseReactionListProps) { const {isSmallScreenWidth} = useWindowDimensions(); const {hoveredComponentBG, reactionListContainer, reactionListContainerFixedWidth, pv2} = useThemeStyles(); + if (!isVisible) { return null; } From c431f9e7f77bfbadcc1fecf4d15a794798998bd9 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 15 Feb 2024 22:49:52 +0700 Subject: [PATCH 133/225] fix selected category is not highlighted --- src/components/CategoryPicker/index.js | 2 +- .../OptionsSelector/BaseOptionsSelector.js | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index a957e31a9de4..a8ff663cebf2 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -84,7 +84,7 @@ CategoryPicker.defaultProps = defaultProps; export default withOnyx({ policyCategories: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${"062D53707053B5B9"}`, }, policyRecentlyUsedCategories: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`, diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 26016bd51ec0..dc3c34b78c0d 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -2,7 +2,7 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {Component} from 'react'; import {ScrollView, View} from 'react-native'; -import _ from 'underscore'; +import _, {find, findIndex} from 'underscore'; import ArrowKeyFocusManager from '@components/ArrowKeyFocusManager'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; @@ -84,6 +84,7 @@ class BaseOptionsSelector extends Component { const allOptions = this.flattenSections(); const sections = this.sliceSections(); const focusedIndex = this.getInitiallyFocusedIndex(allOptions); + this.focusedOption = allOptions[focusedIndex]; this.state = { sections, @@ -146,6 +147,10 @@ class BaseOptionsSelector extends Component { }); } + if (prevState.focusedIndex !== this.state.focusedIndex) { + this.focusedOption = this.state.allOptions[this.state.focusedIndex]; + } + if (_.isEqual(this.props.sections, prevProps.sections)) { return; } @@ -162,13 +167,14 @@ class BaseOptionsSelector extends Component { } const newFocusedIndex = this.props.selectedOptions.length; const isNewFocusedIndex = newFocusedIndex !== this.state.focusedIndex; - + const prevFocusedOption = find(newOptions, (option) => this.focusedOption && option.keyForList === this.focusedOption.keyForList); + const prevFocusedOptionIndex = prevFocusedOption ? findIndex(newOptions, (option) => this.focusedOption && option.keyForList === this.focusedOption.keyForList) : undefined; // eslint-disable-next-line react/no-did-update-set-state this.setState( { sections: newSections, allOptions: newOptions, - focusedIndex: _.isNumber(this.props.focusedIndex) ? this.props.focusedIndex : newFocusedIndex, + focusedIndex: prevFocusedOptionIndex || _.isNumber(this.props.focusedIndex) ? this.props.focusedIndex : newFocusedIndex, }, () => { // If we just toggled an option on a multi-selection page or cleared the search input, scroll to top From 10df32a647abe71ae14ea842b0a07d5c54a4109e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 15 Feb 2024 23:39:44 +0700 Subject: [PATCH 134/225] fix logic issue --- src/components/OptionsSelector/BaseOptionsSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index dc3c34b78c0d..d4913b4827f7 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -174,7 +174,7 @@ class BaseOptionsSelector extends Component { { sections: newSections, allOptions: newOptions, - focusedIndex: prevFocusedOptionIndex || _.isNumber(this.props.focusedIndex) ? this.props.focusedIndex : newFocusedIndex, + focusedIndex: prevFocusedOptionIndex || (_.isNumber(this.props.focusedIndex) ? this.props.focusedIndex : newFocusedIndex), }, () => { // If we just toggled an option on a multi-selection page or cleared the search input, scroll to top From a16983be88a93ea4087b45f774a5c9a4d3a296df Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 15 Feb 2024 23:45:10 +0700 Subject: [PATCH 135/225] fix remove hardcode --- src/components/CategoryPicker/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index a8ff663cebf2..a957e31a9de4 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -84,7 +84,7 @@ CategoryPicker.defaultProps = defaultProps; export default withOnyx({ policyCategories: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${"062D53707053B5B9"}`, + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, }, policyRecentlyUsedCategories: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`, From b74612f46c88b1b91ad5938e40863feffe50e946 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Thu, 15 Feb 2024 20:05:09 +0100 Subject: [PATCH 136/225] fix Login - Expensify logo is too close to the top of the screen --- src/pages/signin/SignInPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index 3e2d6c7082de..f122f28a5a25 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -133,7 +133,7 @@ function SignInPageInner({credentials, account, activeClients, preferredLocale}) const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate, formatPhoneNumber} = useLocalize(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {shouldUseNarrowLayout, isInModal} = useResponsiveLayout(); const safeAreaInsets = useSafeAreaInsets(); const signInPageLayoutRef = useRef(); const loginFormRef = useRef(); @@ -251,7 +251,7 @@ function SignInPageInner({credentials, account, activeClients, preferredLocale}) // Bottom SafeAreaView is removed so that login screen svg displays correctly on mobile. // The SVG should flow under the Home Indicator on iOS. Date: Thu, 15 Feb 2024 22:18:49 +0100 Subject: [PATCH 137/225] rename import --- tests/e2e/utils/logger.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/e2e/utils/logger.js b/tests/e2e/utils/logger.js index aa8be5b84b26..5a7046d8c8b0 100644 --- a/tests/e2e/utils/logger.js +++ b/tests/e2e/utils/logger.js @@ -1,6 +1,6 @@ import fs from 'fs'; import path from 'path'; -import CONST from '../config'; +import CONFIG from '../config'; let isVerbose = true; const setLogLevelVerbose = (value) => { @@ -23,18 +23,18 @@ const log = (...args) => { } // Write to log file - if (!fs.existsSync(CONST.LOG_FILE)) { + if (!fs.existsSync(CONFIG.LOG_FILE)) { // Check that the directory exists - const logDir = path.dirname(CONST.LOG_FILE); + const logDir = path.dirname(CONFIG.LOG_FILE); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir); } - fs.writeFileSync(CONST.LOG_FILE, ''); + fs.writeFileSync(CONFIG.LOG_FILE, ''); } const time = new Date(); const timeStr = `${time.getHours()}:${time.getMinutes()}:${time.getSeconds()} ${time.getMilliseconds()}`; - fs.appendFileSync(CONST.LOG_FILE, `[${timeStr}] ${args.join(' ')}\n`); + fs.appendFileSync(CONFIG.LOG_FILE, `[${timeStr}] ${args.join(' ')}\n`); }; const info = (...args) => { From b9bbf24c278c4d89b0a4ce96836bb00fc150d896 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 16 Feb 2024 14:52:16 +0700 Subject: [PATCH 138/225] resolve conflict --- src/pages/home/report/ReportActionItemMessage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemMessage.tsx b/src/pages/home/report/ReportActionItemMessage.tsx index ac3238002906..ad571eb66f10 100644 --- a/src/pages/home/report/ReportActionItemMessage.tsx +++ b/src/pages/home/report/ReportActionItemMessage.tsx @@ -58,7 +58,7 @@ function ReportActionItemMessage({action, displayAsGroup, reportID, style, isHid const originalMessage = action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? action.originalMessage : null; const iouReportID = originalMessage?.IOUReportID; if (iouReportID) { - iouMessage = ReportUtils.getIOUReportActionDisplayMessage(action); + iouMessage = ReportUtils.getReportPreviewMessage(ReportUtils.getReport(iouReportID), action, false, false, null, false, true); } } From 00411541811b620e5e6db9148c3956a8618da3df Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 16 Feb 2024 10:09:02 +0100 Subject: [PATCH 139/225] comments --- src/CONST.ts | 2 +- src/components/ReportActionItem/MoneyRequestPreview.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index d57c685f7d24..fae319325f13 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1090,7 +1090,7 @@ const CONST = { FIRST_RESPONDER: Number(Config?.EXPENSIFY_ACCOUNT_ID_FIRST_RESPONDER ?? 9375152), HELP: Number(Config?.EXPENSIFY_ACCOUNT_ID_HELP ?? -1), INTEGRATION_TESTING_CREDS: Number(Config?.EXPENSIFY_ACCOUNT_ID_INTEGRATION_TESTING_CREDS ?? -1), - NOTIFICATIONS: Number(11665625), + NOTIFICATIONS: Number(Config?.EXPENSIFY_ACCOUNT_ID_NOTIFICATIONS ?? 11665625), PAYROLL: Number(Config?.EXPENSIFY_ACCOUNT_ID_PAYROLL ?? 9679724), QA: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA ?? 3126513), QA_TRAVIS: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA_TRAVIS ?? 8595733), diff --git a/src/components/ReportActionItem/MoneyRequestPreview.tsx b/src/components/ReportActionItem/MoneyRequestPreview.tsx index 7395a8d1504e..7577c6b547e4 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview.tsx @@ -132,6 +132,7 @@ function MoneyRequestPreview({ if (isEmptyObject(iouReport) && !isBillSplit) { return null; } + const sessionAccountID = session?.accountID; const managerID = iouReport?.managerID ?? -1; const ownerAccountID = iouReport?.ownerAccountID ?? -1; From e145012dc17a52f7924c0b4e578cdb88bc60390a Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 16 Feb 2024 16:15:58 +0700 Subject: [PATCH 140/225] remove getIOUReportActionDisplayMessage function --- src/libs/ReportUtils.ts | 57 ------------------------ tests/perf-test/ReportUtils.perf-test.ts | 19 -------- 2 files changed, 76 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4594cae3ec06..38ddfe379d9e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4603,62 +4603,6 @@ function getVisibleMemberIDs(report: OnyxEntry): number[] { return visibleChatMemberAccountIDs; } -/** - * Return iou report action display message - */ -function getIOUReportActionDisplayMessage(reportAction: OnyxEntry): string { - if (reportAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.IOU) { - return ''; - } - const originalMessage = reportAction.originalMessage; - const {IOUReportID} = originalMessage; - const iouReport = getReport(IOUReportID); - let translationKey: TranslationPaths; - if (originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.PAY) { - // The `REPORT_ACTION_TYPE.PAY` action type is used for both fulfilling existing requests and sending money. To - // differentiate between these two scenarios, we check if the `originalMessage` contains the `IOUDetails` - // property. If it does, it indicates that this is a 'Send money' action. - const {amount, currency} = originalMessage.IOUDetails ?? originalMessage; - const formattedAmount = CurrencyUtils.convertToDisplayString(Math.abs(amount), currency) ?? ''; - const payerName = isExpenseReport(iouReport) ? getPolicyName(iouReport) : getDisplayNameForParticipant(iouReport?.managerID, true); - - switch (originalMessage.paymentType) { - case CONST.IOU.PAYMENT_TYPE.ELSEWHERE: - translationKey = 'iou.paidElsewhereWithAmount'; - break; - case CONST.IOU.PAYMENT_TYPE.EXPENSIFY: - case CONST.IOU.PAYMENT_TYPE.VBBA: - translationKey = 'iou.paidWithExpensifyWithAmount'; - break; - default: - translationKey = 'iou.payerPaidAmount'; - break; - } - return Localize.translateLocal(translationKey, {amount: formattedAmount, payer: payerName ?? ''}); - } - - const transaction = TransactionUtils.getTransaction(originalMessage.IOUTransactionID ?? ''); - const transactionDetails = getTransactionDetails(!isEmptyObject(transaction) ? transaction : null); - const formattedAmount = CurrencyUtils.convertToDisplayString(transactionDetails?.amount ?? 0, transactionDetails?.currency); - const isRequestSettled = isSettled(originalMessage.IOUReportID); - const isApproved = isReportApproved(iouReport); - if (isRequestSettled) { - return Localize.translateLocal('iou.payerSettled', { - amount: formattedAmount, - }); - } - if (isApproved) { - return Localize.translateLocal('iou.approvedAmount', { - amount: formattedAmount, - }); - } - translationKey = ReportActionsUtils.isSplitBillAction(reportAction) ? 'iou.didSplitAmount' : 'iou.requestedAmount'; - return Localize.translateLocal(translationKey, { - formattedAmount, - comment: transactionDetails?.comment ?? '', - }); -} - /** * Checks if a report is a group chat. * @@ -5088,7 +5032,6 @@ export { hasOnlyTransactionsWithPendingRoutes, hasNonReimbursableTransactions, hasMissingSmartscanFields, - getIOUReportActionDisplayMessage, isWaitingForAssigneeToCompleteTask, isGroupChat, isDraftExpenseReport, diff --git a/tests/perf-test/ReportUtils.perf-test.ts b/tests/perf-test/ReportUtils.perf-test.ts index ae3429bb9c01..ee3c54608436 100644 --- a/tests/perf-test/ReportUtils.perf-test.ts +++ b/tests/perf-test/ReportUtils.perf-test.ts @@ -184,23 +184,4 @@ describe('ReportUtils', () => { await waitForBatchedUpdates(); await measureFunction(() => ReportUtils.getTransactionDetails(transaction, 'yyyy-MM-dd')); }); - - test('[ReportUtils] getIOUReportActionDisplayMessage on 1k policies', async () => { - const reportAction = { - ...createRandomReportAction(1), - actionName: CONST.REPORT.ACTIONS.TYPE.IOU, - originalMessage: { - IOUReportID: '1', - IOUTransactionID: '1', - amount: 100, - participantAccountID: 1, - currency: CONST.CURRENCY.USD, - type: CONST.IOU.REPORT_ACTION_TYPE.PAY, - paymentType: CONST.IOU.PAYMENT_TYPE.EXPENSIFY, - }, - }; - - await waitForBatchedUpdates(); - await measureFunction(() => ReportUtils.getIOUReportActionDisplayMessage(reportAction)); - }); }); From d98405545474651cbb95545f3eb8cc200298f9b0 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 16 Feb 2024 18:10:04 +0800 Subject: [PATCH 141/225] use popover anchor tooltip --- src/components/Reactions/ReportActionItemEmojiReactions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.tsx b/src/components/Reactions/ReportActionItemEmojiReactions.tsx index 69779dc316e1..ac05432a0555 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.tsx +++ b/src/components/Reactions/ReportActionItemEmojiReactions.tsx @@ -4,7 +4,7 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {Emoji} from '@assets/emojis/types'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import Tooltip from '@components/Tooltip'; +import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import useThemeStyles from '@hooks/useThemeStyles'; From 729abf651db9a3640849ae30956504d7f4dac6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Fri, 16 Feb 2024 12:24:28 +0100 Subject: [PATCH 142/225] feat: display members(admin) and profile only for collect group policy --- src/libs/PolicyUtils.ts | 6 ++++++ src/pages/workspace/WorkspaceInitialPage.tsx | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index a8c0508e30b6..f24a10070cd1 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -108,6 +108,10 @@ function isExpensifyGuideTeam(email: string): boolean { */ const isPolicyAdmin = (policy: OnyxEntry | EmptyObject): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; +const isCollectPolicy = (policy: OnyxEntry | EmptyObject): boolean => policy?.type === CONST.POLICY.TYPE.TEAM; + +const isFreePolicy = (policy: OnyxEntry | EmptyObject): boolean => policy?.type === CONST.POLICY.TYPE.FREE; + const isPolicyMember = (policyID: string, policies: OnyxCollection): boolean => Object.values(policies ?? {}).some((policy) => policy?.id === policyID); /** @@ -258,6 +262,8 @@ export { isExpensifyTeam, isExpensifyGuideTeam, isInstantSubmitEnabled, + isCollectPolicy, + isFreePolicy, isPolicyAdmin, isSubmitAndClose, getMemberAccountIDsForWorkspace, diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 07049a91bee9..3a75c000b7db 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -98,8 +98,10 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r const hasGeneralSettingsError = !isEmptyObject(policy?.errorFields?.generalSettings ?? {}) || !isEmptyObject(policy?.errorFields?.avatar ?? {}); const shouldShowProtectedItems = PolicyUtils.isPolicyAdmin(policy); + const isCollectPolicy = PolicyUtils.isCollectPolicy(policy); + const isFreePolicy = PolicyUtils.isFreePolicy(policy); - const protectedMenuItems: WorkspaceMenuItem[] = [ + const protectedFreePolicyMenuItems: WorkspaceMenuItem[] = [ { translationKey: 'workspace.common.card', icon: Expensicons.ExpensifyCard, @@ -148,6 +150,16 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r }, ]; + const protectedCollectPolicyMenuItems: WorkspaceMenuItem[] = [ + { + translationKey: 'workspace.common.members', + icon: Expensicons.Users, + action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_MEMBERS.getRoute(policyID)))), + brickRoadIndicator: hasMembersError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + routeName: SCREENS.WORKSPACE.MEMBERS, + }, + ]; + const menuItems: WorkspaceMenuItem[] = [ { translationKey: 'workspace.common.profile', @@ -156,7 +168,8 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r brickRoadIndicator: hasGeneralSettingsError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, routeName: SCREENS.WORKSPACE.PROFILE, }, - ...(shouldShowProtectedItems ? protectedMenuItems : []), + ...(isCollectPolicy && shouldShowProtectedItems ? protectedCollectPolicyMenuItems : []), + ...(isFreePolicy && shouldShowProtectedItems ? protectedFreePolicyMenuItems : []), ]; const prevPolicy = usePrevious(policy); From b1817afc3f3373d71f0fcfccba3b2f1e538baafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Fri, 16 Feb 2024 15:44:38 +0100 Subject: [PATCH 143/225] fix a typo --- src/libs/SidebarUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 695ca8a570a2..838770ea9255 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -13,7 +13,7 @@ import type {ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import * as CollectionUtils from './CollectionUtils'; -import localeCompare from './localeCompare'; +import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as OptionsListUtils from './OptionsListUtils'; From 2b9224c026559fc2e63f7930e214b655faefdf54 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 16 Feb 2024 21:51:22 +0700 Subject: [PATCH 144/225] fix: Distance request preview in the main chat is grayed out offline when created online --- src/libs/actions/Report.ts | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4a791dd41b31..9134f0a89e61 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -571,19 +571,6 @@ function openReport( ]; const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - pendingFields: { - createChat: null, - }, - errorFields: { - createChat: null, - }, - isOptimisticReport: false, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, @@ -647,11 +634,26 @@ function openReport( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction}, }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: {[optimisticCreatedAction.reportActionID]: {pendingAction: null}}, - }); + successData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: {[optimisticCreatedAction.reportActionID]: {pendingAction: null}}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + pendingFields: { + createChat: null, + }, + errorFields: { + createChat: null, + }, + isOptimisticReport: false, + }, + }, + ); // Add optimistic personal details for new participants const optimisticPersonalDetails: OnyxCollection = {}; From 2908c4fdab4d6809bd97fb2f9531be0e22063c5e Mon Sep 17 00:00:00 2001 From: Nathalie Kuoch Date: Fri, 16 Feb 2024 13:57:23 +0100 Subject: [PATCH 145/225] Support vbbas per workspace + refactor --- .../AcceptACHContractForBankAccount.ts | 2 +- .../AddPersonalBankAccountParams.ts | 4 +- .../ConnectBankAccountManuallyParams.ts | 9 ---- .../parameters/ConnectBankAccountParams.ts | 13 +++++ .../ConnectBankAccountWithPlaidParams.ts | 12 ----- .../OpenReimbursementAccountPageParams.ts | 3 +- .../API/parameters/OpenWorkspaceViewParams.ts | 5 ++ ...ateBeneficialOwnersForBankAccountParams.ts | 4 +- ...eCompanyInformationForBankAccountParams.ts | 7 +-- ...PersonalInformationForBankAccountParams.ts | 2 +- ...lidateBankAccountWithTransactionsParams.ts | 1 + .../VerifyIdentityForBankAccountParams.ts | 2 +- src/libs/API/parameters/index.ts | 4 +- src/libs/API/types.ts | 6 +-- src/libs/actions/BankAccounts.ts | 54 ++++++++++--------- .../resetFreePlanBankAccount.js | 4 +- .../ReimbursementAccount/BankAccountStep.js | 7 ++- .../BankInfo/BankInfo.tsx | 19 +++++-- .../BeneficialOwnersStep.tsx | 15 ++++-- .../BusinessInfo/BusinessInfo.tsx | 19 ++++--- .../CompleteVerification.tsx | 17 +++--- .../components/BankAccountValidationForm.tsx | 4 +- .../PersonalInfo/PersonalInfo.tsx | 5 +- .../ReimbursementAccountDraftPropTypes.js | 2 +- .../VerifyIdentity/VerifyIdentity.tsx | 5 +- .../workspace/WorkspacePageWithSections.tsx | 12 ++--- .../WorkspaceResetBankAccountModal.js | 2 +- src/types/form/ReimbursementAccountForm.ts | 2 +- src/types/onyx/PlaidBankAccount.ts | 2 +- 29 files changed, 137 insertions(+), 106 deletions(-) delete mode 100644 src/libs/API/parameters/ConnectBankAccountManuallyParams.ts create mode 100644 src/libs/API/parameters/ConnectBankAccountParams.ts delete mode 100644 src/libs/API/parameters/ConnectBankAccountWithPlaidParams.ts create mode 100644 src/libs/API/parameters/OpenWorkspaceViewParams.ts diff --git a/src/libs/API/parameters/AcceptACHContractForBankAccount.ts b/src/libs/API/parameters/AcceptACHContractForBankAccount.ts index 6cf2735beabc..11ea73bd3a34 100644 --- a/src/libs/API/parameters/AcceptACHContractForBankAccount.ts +++ b/src/libs/API/parameters/AcceptACHContractForBankAccount.ts @@ -1,5 +1,5 @@ import type {ACHContractStepProps} from '@src/types/form/ReimbursementAccountForm'; -type AcceptACHContractForBankAccount = ACHContractStepProps & {bankAccountID: number; canUseNewVbbaFlow?: boolean}; +type AcceptACHContractForBankAccount = ACHContractStepProps & {bankAccountID: number; policyID: string}; export default AcceptACHContractForBankAccount; diff --git a/src/libs/API/parameters/AddPersonalBankAccountParams.ts b/src/libs/API/parameters/AddPersonalBankAccountParams.ts index 1fa8fc0eb48d..d3caf65517df 100644 --- a/src/libs/API/parameters/AddPersonalBankAccountParams.ts +++ b/src/libs/API/parameters/AddPersonalBankAccountParams.ts @@ -1,8 +1,8 @@ type AddPersonalBankAccountParams = { - addressName: string; + addressName?: string; routingNumber: string; accountNumber: string; - isSavings: boolean; + isSavings?: boolean; setupType: string; bank?: string; plaidAccountID: string; diff --git a/src/libs/API/parameters/ConnectBankAccountManuallyParams.ts b/src/libs/API/parameters/ConnectBankAccountManuallyParams.ts deleted file mode 100644 index 17a72588a1e2..000000000000 --- a/src/libs/API/parameters/ConnectBankAccountManuallyParams.ts +++ /dev/null @@ -1,9 +0,0 @@ -type ConnectBankAccountManuallyParams = { - bankAccountID: number; - accountNumber?: string; - routingNumber?: string; - plaidMask?: string; - canUseNewVbbaFlow?: boolean; - policyID?: string; -}; -export default ConnectBankAccountManuallyParams; diff --git a/src/libs/API/parameters/ConnectBankAccountParams.ts b/src/libs/API/parameters/ConnectBankAccountParams.ts new file mode 100644 index 000000000000..b4a4f1d71150 --- /dev/null +++ b/src/libs/API/parameters/ConnectBankAccountParams.ts @@ -0,0 +1,13 @@ +type ConnectBankAccountParams = { + bankAccountID: number; + routingNumber: string; + accountNumber: string; + bank?: string; + plaidAccountID?: string; + plaidAccessToken?: string; + plaidMask?: string; + isSavings?: boolean; + policyID?: string; +}; + +export default ConnectBankAccountParams; diff --git a/src/libs/API/parameters/ConnectBankAccountWithPlaidParams.ts b/src/libs/API/parameters/ConnectBankAccountWithPlaidParams.ts deleted file mode 100644 index e41a3192420e..000000000000 --- a/src/libs/API/parameters/ConnectBankAccountWithPlaidParams.ts +++ /dev/null @@ -1,12 +0,0 @@ -type ConnectBankAccountWithPlaidParams = { - bankAccountID: number; - routingNumber: string; - accountNumber: string; - bank?: string; - plaidAccountID: string; - plaidAccessToken: string; - canUseNewVbbaFlow?: boolean; - policyID?: string; -}; - -export default ConnectBankAccountWithPlaidParams; diff --git a/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts b/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts index 5cd4bff2b94b..8e4ae5208a2e 100644 --- a/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts +++ b/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts @@ -7,8 +7,7 @@ type OpenReimbursementAccountPageParams = { stepToOpen: ReimbursementAccountStep; subStep: ReimbursementAccountSubStep; localCurrentStep: ReimbursementAccountStep; - policyID?: string; - canUseNewVbbaFlow?: boolean; + policyID: string; }; export default OpenReimbursementAccountPageParams; diff --git a/src/libs/API/parameters/OpenWorkspaceViewParams.ts b/src/libs/API/parameters/OpenWorkspaceViewParams.ts new file mode 100644 index 000000000000..3dc9da6e1947 --- /dev/null +++ b/src/libs/API/parameters/OpenWorkspaceViewParams.ts @@ -0,0 +1,5 @@ +type OpenWorkspaceViewParams = { + policyID: string; +}; + +export default OpenWorkspaceViewParams; diff --git a/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts b/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts index 4d4e1af87e3b..b9686eed61af 100644 --- a/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts +++ b/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts @@ -1,5 +1,5 @@ -import type {ACHContractStepProps} from '@src/types/form/ReimbursementAccountForm'; +import type {BeneficialOwnersStepProps} from '@src/types/form/ReimbursementAccountForm'; -type UpdateBeneficialOwnersForBankAccountParams = ACHContractStepProps & {bankAccountID: number; canUseNewVbbaFlow?: boolean}; +type UpdateBeneficialOwnersForBankAccountParams = BeneficialOwnersStepProps & {bankAccountID: number; policyID: string}; export default UpdateBeneficialOwnersForBankAccountParams; diff --git a/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts b/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts index 324c7070bbe2..14eadf19cee1 100644 --- a/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts +++ b/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts @@ -1,8 +1,5 @@ -import type {BankAccountStepProps, CompanyStepProps, ReimbursementAccountProps} from '@src/types/form/ReimbursementAccountForm'; +import type {CompanyStepProps} from '@src/types/form/ReimbursementAccountForm'; -type BankAccountCompanyInformation = BankAccountStepProps & CompanyStepProps & ReimbursementAccountProps; - -type UpdateCompanyInformationForBankAccountParams = BankAccountCompanyInformation & {bankAccountID: number; canUseNewVbbaFlow?: boolean}; +type UpdateCompanyInformationForBankAccountParams = CompanyStepProps & {bankAccountID: number; policyID: string}; export default UpdateCompanyInformationForBankAccountParams; -export type {BankAccountCompanyInformation}; diff --git a/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts b/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts index b9c2ce65405b..7b27649bed96 100644 --- a/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts +++ b/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts @@ -1,5 +1,5 @@ import type {RequestorStepProps} from '@src/types/form/ReimbursementAccountForm'; -type UpdatePersonalInformationForBankAccountParams = RequestorStepProps & {bankAccountID: number; canUseNewVbbaFlow: boolean}; +type UpdatePersonalInformationForBankAccountParams = RequestorStepProps & {bankAccountID: number; policyID: string}; export default UpdatePersonalInformationForBankAccountParams; diff --git a/src/libs/API/parameters/ValidateBankAccountWithTransactionsParams.ts b/src/libs/API/parameters/ValidateBankAccountWithTransactionsParams.ts index 546889b7a68e..dde8e6141a9d 100644 --- a/src/libs/API/parameters/ValidateBankAccountWithTransactionsParams.ts +++ b/src/libs/API/parameters/ValidateBankAccountWithTransactionsParams.ts @@ -1,6 +1,7 @@ type ValidateBankAccountWithTransactionsParams = { bankAccountID: number; validateCode: string; + policyID: string; }; export default ValidateBankAccountWithTransactionsParams; diff --git a/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts b/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts index 2104977e04d5..6ef6b3712439 100644 --- a/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts +++ b/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts @@ -1,6 +1,6 @@ type VerifyIdentityForBankAccountParams = { bankAccountID: number; onfidoData: string; - canUseNewVbbaFlow?: boolean; + policyID: string; }; export default VerifyIdentityForBankAccountParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index ada3b84e6cf4..4dd4ee3994fc 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -9,8 +9,7 @@ export type {default as BeginAppleSignInParams} from './BeginAppleSignInParams'; export type {default as BeginGoogleSignInParams} from './BeginGoogleSignInParams'; export type {default as BeginSignInParams} from './BeginSignInParams'; export type {default as CloseAccountParams} from './CloseAccountParams'; -export type {default as ConnectBankAccountManuallyParams} from './ConnectBankAccountManuallyParams'; -export type {default as ConnectBankAccountWithPlaidParams} from './ConnectBankAccountWithPlaidParams'; +export type {default as ConnectBankAccountParams} from './ConnectBankAccountParams'; export type {default as DeleteContactMethodParams} from './DeleteContactMethodParams'; export type {default as DeletePaymentBankAccountParams} from './DeletePaymentBankAccountParams'; export type {default as DeletePaymentCardParams} from './DeletePaymentCardParams'; @@ -109,6 +108,7 @@ export type {default as UpdateWorkspaceAvatarParams} from './UpdateWorkspaceAvat export type {default as AddMembersToWorkspaceParams} from './AddMembersToWorkspaceParams'; export type {default as DeleteMembersFromWorkspaceParams} from './DeleteMembersFromWorkspaceParams'; export type {default as OpenWorkspaceParams} from './OpenWorkspaceParams'; +export type {default as OpenWorkspaceViewParams} from './OpenWorkspaceViewParams'; export type {default as OpenWorkspaceReimburseViewParams} from './OpenWorkspaceReimburseViewParams'; export type {default as OpenWorkspaceInvitePageParams} from './OpenWorkspaceInvitePageParams'; export type {default as OpenWorkspaceMembersPageParams} from './OpenWorkspaceMembersPageParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 97e0fdd064c5..39ad71e6e9d6 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -160,7 +160,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.VALIDATE_BANK_ACCOUNT_WITH_TRANSACTIONS]: Parameters.ValidateBankAccountWithTransactionsParams; [WRITE_COMMANDS.UPDATE_COMPANY_INFORMATION_FOR_BANK_ACCOUNT]: Parameters.UpdateCompanyInformationForBankAccountParams; [WRITE_COMMANDS.UPDATE_BENEFICIAL_OWNERS_FOR_BANK_ACCOUNT]: UpdateBeneficialOwnersForBankAccountParams; - [WRITE_COMMANDS.CONNECT_BANK_ACCOUNT_MANUALLY]: Parameters.ConnectBankAccountManuallyParams; + [WRITE_COMMANDS.CONNECT_BANK_ACCOUNT_MANUALLY]: Parameters.ConnectBankAccountParams; [WRITE_COMMANDS.VERIFY_IDENTITY_FOR_BANK_ACCOUNT]: Parameters.VerifyIdentityForBankAccountParams; [WRITE_COMMANDS.BANK_ACCOUNT_HANDLE_PLAID_ERROR]: Parameters.BankAccountHandlePlaidErrorParams; [WRITE_COMMANDS.REPORT_VIRTUAL_EXPENSIFY_CARD_FRAUD]: Parameters.ReportVirtualExpensifyCardFraudParams; @@ -213,7 +213,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.TWO_FACTOR_AUTH_VALIDATE]: Parameters.ValidateTwoFactorAuthParams; [WRITE_COMMANDS.ADD_COMMENT]: Parameters.AddCommentOrAttachementParams; [WRITE_COMMANDS.ADD_ATTACHMENT]: Parameters.AddCommentOrAttachementParams; - [WRITE_COMMANDS.CONNECT_BANK_ACCOUNT_WITH_PLAID]: Parameters.ConnectBankAccountWithPlaidParams; + [WRITE_COMMANDS.CONNECT_BANK_ACCOUNT_WITH_PLAID]: Parameters.ConnectBankAccountParams; [WRITE_COMMANDS.ADD_PERSONAL_BANK_ACCOUNT]: Parameters.AddPersonalBankAccountParams; [WRITE_COMMANDS.OPT_IN_TO_PUSH_NOTIFICATIONS]: Parameters.OptInOutToPushNotificationsParams; [WRITE_COMMANDS.OPT_OUT_OF_PUSH_NOTIFICATIONS]: Parameters.OptInOutToPushNotificationsParams; @@ -325,7 +325,7 @@ type ReadCommand = ValueOf; type ReadCommandParameters = { [READ_COMMANDS.OPEN_APP]: Parameters.OpenAppParams; [READ_COMMANDS.OPEN_REIMBURSEMENT_ACCOUNT_PAGE]: Parameters.OpenReimbursementAccountPageParams; - [READ_COMMANDS.OPEN_WORKSPACE_VIEW]: EmptyObject; + [READ_COMMANDS.OPEN_WORKSPACE_VIEW]: Parameters.OpenWorkspaceViewParams; [READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN]: EmptyObject; [READ_COMMANDS.OPEN_PAYMENTS_PAGE]: EmptyObject; [READ_COMMANDS.OPEN_PERSONAL_DETAILS]: EmptyObject; diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index c5f68317bf18..36563860fb70 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -3,8 +3,7 @@ import * as API from '@libs/API'; import type { AddPersonalBankAccountParams, BankAccountHandlePlaidErrorParams, - ConnectBankAccountManuallyParams, - ConnectBankAccountWithPlaidParams, + ConnectBankAccountParams, DeletePaymentBankAccountParams, OpenReimbursementAccountPageParams, ValidateBankAccountWithTransactionsParams, @@ -152,14 +151,15 @@ function addBusinessWebsiteForDraft(websiteUrl: string) { * Submit Bank Account step with Plaid data so php can perform some checks. */ function connectBankAccountWithPlaid(bankAccountID: number, selectedPlaidBankAccount: PlaidBankAccount, policyID: string) { - const parameters: ConnectBankAccountWithPlaidParams = { + const parameters: ConnectBankAccountParams = { bankAccountID, routingNumber: selectedPlaidBankAccount.routingNumber, accountNumber: selectedPlaidBankAccount.accountNumber, bank: selectedPlaidBankAccount.bankName, plaidAccountID: selectedPlaidBankAccount.plaidAccountID, plaidAccessToken: selectedPlaidBankAccount.plaidAccessToken, - canUseNewVbbaFlow: true, + plaidMask: selectedPlaidBankAccount.mask, + isSavings: selectedPlaidBankAccount.isSavings, policyID, }; @@ -254,22 +254,23 @@ function deletePaymentBankAccount(bankAccountID: number) { * @param bankAccountID - ID for bank account * @param params - User personal data */ -function updatePersonalInformationForBankAccount(bankAccountID: number, params: RequestorStepProps) { +function updatePersonalInformationForBankAccount(bankAccountID: number, params: RequestorStepProps, policyID: string) { API.write( WRITE_COMMANDS.UPDATE_PERSONAL_INFORMATION_FOR_BANK_ACCOUNT, { ...params, bankAccountID, - canUseNewVbbaFlow: true, + policyID, }, getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.REQUESTOR), ); } -function validateBankAccount(bankAccountID: number, validateCode: string) { +function validateBankAccount(bankAccountID: number, validateCode: string, policyID: string) { const parameters: ValidateBankAccountWithTransactionsParams = { bankAccountID, validateCode, + policyID, }; const onyxData: OnyxData = { @@ -353,7 +354,6 @@ function openReimbursementAccountPage(stepToOpen: ReimbursementAccountStep, subS subStep, localCurrentStep, policyID, - canUseNewVbbaFlow: true, }; return API.read(READ_COMMANDS.OPEN_REIMBURSEMENT_ACCOUNT_PAGE, parameters, onyxData); @@ -363,13 +363,13 @@ function openReimbursementAccountPage(stepToOpen: ReimbursementAccountStep, subS * Updates the bank account in the database with the company step data * @param params - Business step form data */ -function updateCompanyInformationForBankAccount(bankAccountID: number, params: CompanyStepProps) { +function updateCompanyInformationForBankAccount(bankAccountID: number, params: CompanyStepProps, policyID: string) { API.write( WRITE_COMMANDS.UPDATE_COMPANY_INFORMATION_FOR_BANK_ACCOUNT, { ...params, bankAccountID, - canUseNewVbbaFlow: true, + policyID, }, getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.COMPANY), ); @@ -379,13 +379,13 @@ function updateCompanyInformationForBankAccount(bankAccountID: number, params: C * Add beneficial owners for the bank account and verify the accuracy of the information provided * @param params - Beneficial Owners step form params */ -function updateBeneficialOwnersForBankAccount(bankAccountID: number, params: BeneficialOwnersStepProps) { +function updateBeneficialOwnersForBankAccount(bankAccountID: number, params: BeneficialOwnersStepProps, policyID: string) { API.write( WRITE_COMMANDS.UPDATE_BENEFICIAL_OWNERS_FOR_BANK_ACCOUNT, { ...params, bankAccountID, - canUseNewVbbaFlow: true, + policyID, }, getVBBADataForOnyx(), ); @@ -395,13 +395,13 @@ function updateBeneficialOwnersForBankAccount(bankAccountID: number, params: Ben * Accept the ACH terms and conditions and verify the accuracy of the information provided * @param params - Verification step form params */ -function acceptACHContractForBankAccount(bankAccountID: number, params: ACHContractStepProps) { +function acceptACHContractForBankAccount(bankAccountID: number, params: ACHContractStepProps, policyID: string) { API.write( WRITE_COMMANDS.ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT, { ...params, bankAccountID, - canUseNewVbbaFlow: true, + policyID, }, getVBBADataForOnyx(), ); @@ -409,15 +409,17 @@ function acceptACHContractForBankAccount(bankAccountID: number, params: ACHContr /** * Create the bank account with manually entered data. - * @param plaidMask - scheme for Plaid account number */ -function connectBankAccountManually(bankAccountID: number, accountNumber?: string, routingNumber?: string, plaidMask?: string, policyID?: string) { - const parameters: ConnectBankAccountManuallyParams = { +function connectBankAccountManually(bankAccountID: number, bankAccount: PlaidBankAccount, policyID: string) { + const parameters: ConnectBankAccountParams = { bankAccountID, - accountNumber, - routingNumber, - plaidMask, - canUseNewVbbaFlow: true, + routingNumber: bankAccount.routingNumber, + accountNumber: bankAccount.accountNumber, + bank: bankAccount.bankName, + plaidAccountID: bankAccount.plaidAccountID, + plaidAccessToken: bankAccount.plaidAccessToken, + plaidMask: bankAccount.mask, + isSavings: bankAccount.isSavings, policyID, }; @@ -427,20 +429,22 @@ function connectBankAccountManually(bankAccountID: number, accountNumber?: strin /** * Verify the user's identity via Onfido */ -function verifyIdentityForBankAccount(bankAccountID: number, onfidoData: Record) { +function verifyIdentityForBankAccount(bankAccountID: number, onfidoData: Record, policyID: string) { const parameters: VerifyIdentityForBankAccountParams = { bankAccountID, onfidoData: JSON.stringify(onfidoData), - canUseNewVbbaFlow: true, + policyID, }; API.write(WRITE_COMMANDS.VERIFY_IDENTITY_FOR_BANK_ACCOUNT, parameters, getVBBADataForOnyx()); } -function openWorkspaceView() { +function openWorkspaceView(policyID: string) { API.read( READ_COMMANDS.OPEN_WORKSPACE_VIEW, - {}, + { + policyID, + }, { optimisticData: [ { diff --git a/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js b/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js index 962800fb2e55..2cc085e32f5e 100644 --- a/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js +++ b/src/libs/actions/ReimbursementAccount/resetFreePlanBankAccount.js @@ -9,8 +9,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; * Reset user's reimbursement account. This will delete the bank account. * @param {Number} bankAccountID * @param {Object} session + * @param {String} policyID */ -function resetFreePlanBankAccount(bankAccountID, session) { +function resetFreePlanBankAccount(bankAccountID, session, policyID) { if (!bankAccountID) { throw new Error('Missing bankAccountID when attempting to reset free plan bank account'); } @@ -23,6 +24,7 @@ function resetFreePlanBankAccount(bankAccountID, session) { { bankAccountID, ownerEmail: session.email, + policyID, }, { optimisticData: [ diff --git a/src/pages/ReimbursementAccount/BankAccountStep.js b/src/pages/ReimbursementAccount/BankAccountStep.js index 278036430dbf..75ae02587486 100644 --- a/src/pages/ReimbursementAccount/BankAccountStep.js +++ b/src/pages/ReimbursementAccount/BankAccountStep.js @@ -97,7 +97,12 @@ function BankAccountStep(props) { }; if (subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID || subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL) { - return ; + return ( + + ); } return ( diff --git a/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx b/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx index a0b91ffcdb0e..bb352acd4732 100644 --- a/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx +++ b/src/pages/ReimbursementAccount/BankInfo/BankInfo.tsx @@ -31,6 +31,8 @@ type BankInfoOnyxProps = { /** The draft values of the bank account being setup */ reimbursementAccountDraft: OnyxEntry; + + policyID: string; }; type BankInfoProps = BankInfoOnyxProps & { @@ -43,7 +45,7 @@ const manualSubsteps: Array> = [Manual, Confir const plaidSubsteps: Array> = [Plaid, Confirmation]; const receivedRedirectURI = getPlaidOAuthReceivedRedirectURI(); -function BankInfo({reimbursementAccount, reimbursementAccountDraft, plaidLinkToken, onBackButtonPress}: BankInfoProps) { +function BankInfo({reimbursementAccount, reimbursementAccountDraft, plaidLinkToken, onBackButtonPress, policyID}: BankInfoProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -57,15 +59,20 @@ function BankInfo({reimbursementAccount, reimbursementAccountDraft, plaidLinkTok setupType = CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID; } - const policyID = reimbursementAccount?.achData?.policyID ?? ''; const bankAccountID = Number(reimbursementAccount?.achData?.bankAccountID ?? '0'); const submit = useCallback(() => { if (setupType === CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL) { BankAccounts.connectBankAccountManually( bankAccountID, - values[BANK_INFO_STEP_KEYS.ACCOUNT_NUMBER], - values[BANK_INFO_STEP_KEYS.ROUTING_NUMBER], - values[BANK_INFO_STEP_KEYS.PLAID_MASK], + { + [BANK_INFO_STEP_KEYS.ROUTING_NUMBER]: values[BANK_INFO_STEP_KEYS.ROUTING_NUMBER] ?? '', + [BANK_INFO_STEP_KEYS.ACCOUNT_NUMBER]: values[BANK_INFO_STEP_KEYS.ACCOUNT_NUMBER] ?? '', + [BANK_INFO_STEP_KEYS.BANK_NAME]: values[BANK_INFO_STEP_KEYS.BANK_NAME] ?? '', + [BANK_INFO_STEP_KEYS.PLAID_ACCOUNT_ID]: values[BANK_INFO_STEP_KEYS.PLAID_ACCOUNT_ID] ?? '', + [BANK_INFO_STEP_KEYS.PLAID_ACCESS_TOKEN]: values[BANK_INFO_STEP_KEYS.PLAID_ACCESS_TOKEN] ?? '', + [BANK_INFO_STEP_KEYS.PLAID_MASK]: values[BANK_INFO_STEP_KEYS.PLAID_MASK] ?? '', + [BANK_INFO_STEP_KEYS.IS_SAVINGS]: values[BANK_INFO_STEP_KEYS.IS_SAVINGS] ?? false, + }, policyID, ); } else if (setupType === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID) { @@ -77,6 +84,8 @@ function BankInfo({reimbursementAccount, reimbursementAccountDraft, plaidLinkTok [BANK_INFO_STEP_KEYS.BANK_NAME]: values[BANK_INFO_STEP_KEYS.BANK_NAME] ?? '', [BANK_INFO_STEP_KEYS.PLAID_ACCOUNT_ID]: values[BANK_INFO_STEP_KEYS.PLAID_ACCOUNT_ID] ?? '', [BANK_INFO_STEP_KEYS.PLAID_ACCESS_TOKEN]: values[BANK_INFO_STEP_KEYS.PLAID_ACCESS_TOKEN] ?? '', + [BANK_INFO_STEP_KEYS.PLAID_MASK]: values[BANK_INFO_STEP_KEYS.PLAID_MASK] ?? '', + [BANK_INFO_STEP_KEYS.IS_SAVINGS]: values[BANK_INFO_STEP_KEYS.IS_SAVINGS] ?? false, }, policyID, ); diff --git a/src/pages/ReimbursementAccount/BeneficialOwnersStep.tsx b/src/pages/ReimbursementAccount/BeneficialOwnersStep.tsx index 406033b10b03..10b05e58deb7 100644 --- a/src/pages/ReimbursementAccount/BeneficialOwnersStep.tsx +++ b/src/pages/ReimbursementAccount/BeneficialOwnersStep.tsx @@ -47,6 +47,7 @@ function BeneficialOwnersStep({reimbursementAccount, reimbursementAccountDraft, const {translate} = useLocalize(); const styles = useThemeStyles(); const companyName = reimbursementAccount?.achData?.companyName ?? ''; + const policyID = reimbursementAccount?.achData?.policyID ?? ''; const defaultValues = { ownsMoreThan25Percent: reimbursementAccount?.achData?.ownsMoreThan25Percent ?? reimbursementAccountDraft?.ownsMoreThan25Percent ?? false, hasOtherBeneficialOwners: reimbursementAccount?.achData?.hasOtherBeneficialOwners ?? reimbursementAccountDraft?.hasOtherBeneficialOwners ?? false, @@ -76,11 +77,15 @@ function BeneficialOwnersStep({reimbursementAccount, reimbursementAccountDraft, ), ); - BankAccounts.updateBeneficialOwnersForBankAccount(Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), { - ownsMoreThan25Percent: isUserUBO, - beneficialOwners: JSON.stringify(beneficialOwners), - beneficialOwnerKeys, - }); + BankAccounts.updateBeneficialOwnersForBankAccount( + Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), + { + ownsMoreThan25Percent: isUserUBO, + beneficialOwners: JSON.stringify(beneficialOwners), + beneficialOwnerKeys, + }, + policyID, + ); }; const addBeneficialOwner = (beneficialOwnerID: string) => { diff --git a/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx b/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx index 21f90c414b5d..f63cf72f8a4f 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx @@ -68,16 +68,21 @@ function BusinessInfo({reimbursementAccount, reimbursementAccountDraft, onBackBu [reimbursementAccount, reimbursementAccountDraft], ); + const policyID = reimbursementAccount?.achData?.policyID ?? ''; const values = useMemo(() => getSubstepValues(BUSINESS_INFO_STEP_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); const submit = useCallback(() => { - BankAccounts.updateCompanyInformationForBankAccount(Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), { - ...values, - ...getBankAccountFields(['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']), - companyTaxID: values.companyTaxID?.replace(CONST.REGEX.NON_NUMERIC, ''), - companyPhone: parsePhoneNumber(values.companyPhone ?? '', {regionCode: CONST.COUNTRY.US}).number?.significant, - }); - }, [reimbursementAccount, values, getBankAccountFields]); + BankAccounts.updateCompanyInformationForBankAccount( + Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), + { + ...values, + ...getBankAccountFields(['routingNumber', 'accountNumber', 'bankName', 'plaidAccountID', 'plaidAccessToken', 'isSavings']), + companyTaxID: values.companyTaxID?.replace(CONST.REGEX.NON_NUMERIC, ''), + companyPhone: parsePhoneNumber(values.companyPhone ?? '', {regionCode: CONST.COUNTRY.US}).number?.significant, + }, + policyID, + ); + }, [reimbursementAccount, values, getBankAccountFields, policyID]); const startFrom = useMemo(() => getInitialSubstepForBusinessInfo(values), [values]); diff --git a/src/pages/ReimbursementAccount/CompleteVerification/CompleteVerification.tsx b/src/pages/ReimbursementAccount/CompleteVerification/CompleteVerification.tsx index 20d4663dcf8d..1c440a52644c 100644 --- a/src/pages/ReimbursementAccount/CompleteVerification/CompleteVerification.tsx +++ b/src/pages/ReimbursementAccount/CompleteVerification/CompleteVerification.tsx @@ -40,14 +40,19 @@ function CompleteVerification({reimbursementAccount, reimbursementAccountDraft, const styles = useThemeStyles(); const values = useMemo(() => getSubstepValues(COMPLETE_VERIFICATION_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); + const policyID = reimbursementAccount?.achData?.policyID ?? ''; const submit = useCallback(() => { - BankAccounts.acceptACHContractForBankAccount(Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), { - isAuthorizedToUseBankAccount: values.isAuthorizedToUseBankAccount, - certifyTrueInformation: values.certifyTrueInformation, - acceptTermsAndConditions: values.acceptTermsAndConditions, - }); - }, [reimbursementAccount, values]); + BankAccounts.acceptACHContractForBankAccount( + Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), + { + isAuthorizedToUseBankAccount: values.isAuthorizedToUseBankAccount, + certifyTrueInformation: values.certifyTrueInformation, + acceptTermsAndConditions: values.acceptTermsAndConditions, + }, + policyID, + ); + }, [reimbursementAccount, values, policyID]); const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo, goToTheLastStep} = useSubStep({bodyContent, startFrom: 0, onFinished: submit}); diff --git a/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx b/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx index d921343c9557..0e8192b60416 100644 --- a/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx +++ b/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx @@ -89,10 +89,10 @@ function BankAccountValidationForm({requiresTwoFactorAuth, reimbursementAccount, // Send valid amounts to BankAccountAPI::validateBankAccount in Web-Expensify const bankAccountID = Number(reimbursementAccount?.achData?.bankAccountID ?? '0'); if (bankAccountID) { - BankAccounts.validateBankAccount(bankAccountID, validateCode); + BankAccounts.validateBankAccount(bankAccountID, validateCode, policyID); } }, - [reimbursementAccount], + [reimbursementAccount, policyID], ); return ( getSubstepValues(PERSONAL_INFO_STEP_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); const bankAccountID = Number(reimbursementAccount?.achData?.bankAccountID ?? '0'); const submit = useCallback(() => { - BankAccounts.updatePersonalInformationForBankAccount(bankAccountID, {...values}); - }, [values, bankAccountID]); + BankAccounts.updatePersonalInformationForBankAccount(bankAccountID, {...values}, policyID); + }, [values, bankAccountID, policyID]); const startFrom = useMemo(() => getInitialSubstepForPersonalInfo(values), [values]); const {componentToRender: SubStep, isEditing, screenIndex, nextScreen, prevScreen, moveTo, goToTheLastStep} = useSubStep({bodyContent, startFrom, onFinished: submit}); diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js b/src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js index c46ae5a65020..97bd1c508775 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes.js @@ -8,7 +8,7 @@ export default PropTypes.shape({ routingNumber: PropTypes.string, acceptTerms: PropTypes.bool, plaidAccountID: PropTypes.string, - plaidMask: PropTypes.string, + mask: PropTypes.string, /** Props needed for CompanyStep */ companyName: PropTypes.string, diff --git a/src/pages/ReimbursementAccount/VerifyIdentity/VerifyIdentity.tsx b/src/pages/ReimbursementAccount/VerifyIdentity/VerifyIdentity.tsx index 44563afd587c..d17166365a39 100644 --- a/src/pages/ReimbursementAccount/VerifyIdentity/VerifyIdentity.tsx +++ b/src/pages/ReimbursementAccount/VerifyIdentity/VerifyIdentity.tsx @@ -38,12 +38,13 @@ function VerifyIdentity({reimbursementAccount, onBackButtonPress, onfidoApplican const styles = useThemeStyles(); const {translate} = useLocalize(); + const policyID = reimbursementAccount?.achData?.policyID ?? ''; const handleOnfidoSuccess = useCallback( (onfidoData: Record) => { - BankAccounts.verifyIdentityForBankAccount(Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), {...onfidoData, applicantID: onfidoApplicantID}); + BankAccounts.verifyIdentityForBankAccount(Number(reimbursementAccount?.achData?.bankAccountID ?? '0'), {...onfidoData, applicantID: onfidoApplicantID}, policyID); BankAccounts.updateReimbursementAccountDraft({isOnfidoSetupComplete: true}); }, - [reimbursementAccount, onfidoApplicantID], + [reimbursementAccount, onfidoApplicantID, policyID], ); const handleOnfidoError = () => { diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 9e01a82b3188..4a77adac7b37 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -79,12 +79,12 @@ type WorkspacePageWithSectionsProps = WithPolicyAndFullscreenLoadingProps & icon?: IconAsset; }; -function fetchData(skipVBBACal?: boolean) { +function fetchData(policyID: string, skipVBBACal?: boolean) { if (skipVBBACal) { return; } - BankAccounts.openWorkspaceView(); + BankAccounts.openWorkspaceView(policyID); } function WorkspacePageWithSections({ @@ -107,12 +107,12 @@ function WorkspacePageWithSections({ icon, }: WorkspacePageWithSectionsProps) { const styles = useThemeStyles(); - useNetwork({onReconnect: () => fetchData(shouldSkipVBBACall)}); + const policyID = route.params?.policyID ?? ''; + useNetwork({onReconnect: () => fetchData(policyID, shouldSkipVBBACall)}); const isLoading = reimbursementAccount?.isLoading ?? true; const achState = reimbursementAccount?.achData?.state ?? ''; const isUsingECard = user?.isUsingExpensifyCard ?? false; - const policyID = route.params?.policyID ?? ''; const hasVBA = achState === BankAccount.STATE.OPEN; const content = children(hasVBA, policyID, isUsingECard); const {isSmallScreenWidth} = useWindowDimensions(); @@ -131,8 +131,8 @@ function WorkspacePageWithSections({ }, []); useEffect(() => { - fetchData(shouldSkipVBBACall); - }, [shouldSkipVBBACall]); + fetchData(policyID, shouldSkipVBBACall); + }, [policyID, shouldSkipVBBACall]); const shouldShow = useMemo(() => { // If the policy object doesn't exist or contains only error data, we shouldn't display it. diff --git a/src/pages/workspace/WorkspaceResetBankAccountModal.js b/src/pages/workspace/WorkspaceResetBankAccountModal.js index f4ad662ebb75..f98077a546ca 100644 --- a/src/pages/workspace/WorkspaceResetBankAccountModal.js +++ b/src/pages/workspace/WorkspaceResetBankAccountModal.js @@ -48,7 +48,7 @@ function WorkspaceResetBankAccountModal({reimbursementAccount, session}) { } danger onCancel={BankAccounts.cancelResetFreePlanBankAccount} - onConfirm={() => BankAccounts.resetFreePlanBankAccount(bankAccountID, session)} + onConfirm={() => BankAccounts.resetFreePlanBankAccount(bankAccountID, session, achData.policyID)} shouldShowCancelButton isVisible /> diff --git a/src/types/form/ReimbursementAccountForm.ts b/src/types/form/ReimbursementAccountForm.ts index ff45795992c4..7bc1c52e8025 100644 --- a/src/types/form/ReimbursementAccountForm.ts +++ b/src/types/form/ReimbursementAccountForm.ts @@ -4,7 +4,7 @@ const INPUT_IDS = { BANK_INFO_STEP: { ROUTING_NUMBER: 'routingNumber', ACCOUNT_NUMBER: 'accountNumber', - PLAID_MASK: 'plaidMask', + PLAID_MASK: 'mask', IS_SAVINGS: 'isSavings', BANK_NAME: 'bankName', PLAID_ACCOUNT_ID: 'plaidAccountID', diff --git a/src/types/onyx/PlaidBankAccount.ts b/src/types/onyx/PlaidBankAccount.ts index f0aa5f8540e1..7620c4aee367 100644 --- a/src/types/onyx/PlaidBankAccount.ts +++ b/src/types/onyx/PlaidBankAccount.ts @@ -15,7 +15,7 @@ type PlaidBankAccount = { routingNumber: string; /** Last 4 digits of the account number */ - mask?: string; + mask: string; /** Plaid access token, used to then retrieve Assets and Balances */ plaidAccessToken: string; From b65abec1151c51eec36830555125f72028a030b9 Mon Sep 17 00:00:00 2001 From: Nathalie Kuoch Date: Fri, 16 Feb 2024 20:41:49 +0100 Subject: [PATCH 146/225] Restore canUseNewVbbaFlow while BE is not deployed --- .../API/parameters/AcceptACHContractForBankAccount.ts | 2 +- src/libs/API/parameters/ConnectBankAccountParams.ts | 1 + .../API/parameters/OpenReimbursementAccountPageParams.ts | 1 + .../UpdateBeneficialOwnersForBankAccountParams.ts | 2 +- .../UpdateCompanyInformationForBankAccountParams.ts | 2 +- .../UpdatePersonalInformationForBankAccountParams.ts | 2 +- .../API/parameters/VerifyIdentityForBankAccountParams.ts | 1 + src/libs/actions/BankAccounts.ts | 8 ++++++++ 8 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libs/API/parameters/AcceptACHContractForBankAccount.ts b/src/libs/API/parameters/AcceptACHContractForBankAccount.ts index 11ea73bd3a34..de4ce4e86857 100644 --- a/src/libs/API/parameters/AcceptACHContractForBankAccount.ts +++ b/src/libs/API/parameters/AcceptACHContractForBankAccount.ts @@ -1,5 +1,5 @@ import type {ACHContractStepProps} from '@src/types/form/ReimbursementAccountForm'; -type AcceptACHContractForBankAccount = ACHContractStepProps & {bankAccountID: number; policyID: string}; +type AcceptACHContractForBankAccount = ACHContractStepProps & {bankAccountID: number; policyID: string; canUseNewVbbaFlow?: boolean}; export default AcceptACHContractForBankAccount; diff --git a/src/libs/API/parameters/ConnectBankAccountParams.ts b/src/libs/API/parameters/ConnectBankAccountParams.ts index b4a4f1d71150..fb0e3422d08c 100644 --- a/src/libs/API/parameters/ConnectBankAccountParams.ts +++ b/src/libs/API/parameters/ConnectBankAccountParams.ts @@ -8,6 +8,7 @@ type ConnectBankAccountParams = { plaidMask?: string; isSavings?: boolean; policyID?: string; + canUseNewVbbaFlow?: boolean; }; export default ConnectBankAccountParams; diff --git a/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts b/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts index 8e4ae5208a2e..31eb443ce80e 100644 --- a/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts +++ b/src/libs/API/parameters/OpenReimbursementAccountPageParams.ts @@ -7,6 +7,7 @@ type OpenReimbursementAccountPageParams = { stepToOpen: ReimbursementAccountStep; subStep: ReimbursementAccountSubStep; localCurrentStep: ReimbursementAccountStep; + canUseNewVbbaFlow?: boolean; policyID: string; }; diff --git a/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts b/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts index b9686eed61af..f5cc3f664d12 100644 --- a/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts +++ b/src/libs/API/parameters/UpdateBeneficialOwnersForBankAccountParams.ts @@ -1,5 +1,5 @@ import type {BeneficialOwnersStepProps} from '@src/types/form/ReimbursementAccountForm'; -type UpdateBeneficialOwnersForBankAccountParams = BeneficialOwnersStepProps & {bankAccountID: number; policyID: string}; +type UpdateBeneficialOwnersForBankAccountParams = BeneficialOwnersStepProps & {bankAccountID: number; policyID: string; canUseNewVbbaFlow?: boolean}; export default UpdateBeneficialOwnersForBankAccountParams; diff --git a/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts b/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts index 14eadf19cee1..21ca49839aec 100644 --- a/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts +++ b/src/libs/API/parameters/UpdateCompanyInformationForBankAccountParams.ts @@ -1,5 +1,5 @@ import type {CompanyStepProps} from '@src/types/form/ReimbursementAccountForm'; -type UpdateCompanyInformationForBankAccountParams = CompanyStepProps & {bankAccountID: number; policyID: string}; +type UpdateCompanyInformationForBankAccountParams = CompanyStepProps & {bankAccountID: number; policyID: string; canUseNewVbbaFlow?: boolean}; export default UpdateCompanyInformationForBankAccountParams; diff --git a/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts b/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts index 7b27649bed96..c1a29ddd9cec 100644 --- a/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts +++ b/src/libs/API/parameters/UpdatePersonalInformationForBankAccountParams.ts @@ -1,5 +1,5 @@ import type {RequestorStepProps} from '@src/types/form/ReimbursementAccountForm'; -type UpdatePersonalInformationForBankAccountParams = RequestorStepProps & {bankAccountID: number; policyID: string}; +type UpdatePersonalInformationForBankAccountParams = RequestorStepProps & {bankAccountID: number; policyID: string; canUseNewVbbaFlow: boolean}; export default UpdatePersonalInformationForBankAccountParams; diff --git a/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts b/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts index 6ef6b3712439..c11aec9be239 100644 --- a/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts +++ b/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts @@ -2,5 +2,6 @@ type VerifyIdentityForBankAccountParams = { bankAccountID: number; onfidoData: string; policyID: string; + canUseNewVbbaFlow?: boolean; }; export default VerifyIdentityForBankAccountParams; diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index 36563860fb70..30dd03b6e780 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -160,6 +160,7 @@ function connectBankAccountWithPlaid(bankAccountID: number, selectedPlaidBankAcc plaidAccessToken: selectedPlaidBankAccount.plaidAccessToken, plaidMask: selectedPlaidBankAccount.mask, isSavings: selectedPlaidBankAccount.isSavings, + canUseNewVbbaFlow: true, policyID, }; @@ -261,6 +262,7 @@ function updatePersonalInformationForBankAccount(bankAccountID: number, params: ...params, bankAccountID, policyID, + canUseNewVbbaFlow: true, }, getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.REQUESTOR), ); @@ -354,6 +356,7 @@ function openReimbursementAccountPage(stepToOpen: ReimbursementAccountStep, subS subStep, localCurrentStep, policyID, + canUseNewVbbaFlow: true, }; return API.read(READ_COMMANDS.OPEN_REIMBURSEMENT_ACCOUNT_PAGE, parameters, onyxData); @@ -370,6 +373,7 @@ function updateCompanyInformationForBankAccount(bankAccountID: number, params: C ...params, bankAccountID, policyID, + canUseNewVbbaFlow: true, }, getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.COMPANY), ); @@ -386,6 +390,7 @@ function updateBeneficialOwnersForBankAccount(bankAccountID: number, params: Ben ...params, bankAccountID, policyID, + canUseNewVbbaFlow: true, }, getVBBADataForOnyx(), ); @@ -402,6 +407,7 @@ function acceptACHContractForBankAccount(bankAccountID: number, params: ACHContr ...params, bankAccountID, policyID, + canUseNewVbbaFlow: true, }, getVBBADataForOnyx(), ); @@ -420,6 +426,7 @@ function connectBankAccountManually(bankAccountID: number, bankAccount: PlaidBan plaidAccessToken: bankAccount.plaidAccessToken, plaidMask: bankAccount.mask, isSavings: bankAccount.isSavings, + canUseNewVbbaFlow: true, policyID, }; @@ -434,6 +441,7 @@ function verifyIdentityForBankAccount(bankAccountID: number, onfidoData: Record< bankAccountID, onfidoData: JSON.stringify(onfidoData), policyID, + canUseNewVbbaFlow: true, }; API.write(WRITE_COMMANDS.VERIFY_IDENTITY_FOR_BANK_ACCOUNT, parameters, getVBBADataForOnyx()); From 860043fd155e489b47d66d5bb9b8a119ff6eadf3 Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Fri, 16 Feb 2024 23:26:08 +0100 Subject: [PATCH 147/225] Applying suggestion Co-authored-by: Akinwale Ariwodola --- src/libs/ReportUtils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 88b75f0e0af3..afbd003841e7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1453,7 +1453,10 @@ function getIconsForParticipants(participants: number[], personalDetails: OnyxCo function getWorkspaceIcon(report: OnyxEntry, policy: OnyxEntry = null): Icon { const workspaceName = getPolicyName(report, false, policy); const rootParentReport = getRootParentReport(report); - const policyExpenseChatAvatarSource = + const hasCustomAvatar = (isEmptyObject(rootParentReport) || !isDefaultRoom(rootParentReport)) && allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar; + const policyExpenseChatAvatarSource = hasCustomAvatar ? + allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar : + getDefaultWorkspaceAvatar(workspaceName); (isEmptyObject(rootParentReport) || !isDefaultRoom(rootParentReport)) && allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatar : getDefaultWorkspaceAvatar(workspaceName); From 7455306b01624d80855e87a9290cd82323a124fc Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sat, 17 Feb 2024 11:37:04 +0100 Subject: [PATCH 148/225] Set tag, category, and billable when editing a split bill --- src/libs/actions/IOU.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 3482dd0e30ad..b5c9ae45a6de 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2384,6 +2384,10 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA updatedTransaction.modifiedMerchant, {...updatedTransaction.receipt, state: CONST.IOU.RECEIPT_STATE.OPEN}, updatedTransaction.filename, + undefined, + updatedTransaction.category, + updatedTransaction.tag, + updatedTransaction.billable, ); const oneOnOneCreatedActionForChat = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); From 6856016d366fa811d888800c848a76bd2b4fab2c Mon Sep 17 00:00:00 2001 From: Ishpaul Singh <104348397+ishpaul777@users.noreply.github.com> Date: Sat, 17 Feb 2024 18:31:44 +0530 Subject: [PATCH 149/225] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7ad11c1c93e..72736b3fedb7 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ For an M1 Mac, read this [SO](https://stackoverflow.com/questions/64901180/how-t * To run a on a **Development Simulator**: `npm run ios` * Changes applied to Javascript will be applied automatically, any changes to native code will require a recompile -If you want to run the app on an actual physical iOS device, please follow the instructions [here](https://github.com/Expensify/App/blob/docs/how-to-build-app-on-physcial-device/contributingGuides/HOW_TO_BUILD_APP_ON_PHYSICAL_IOS_DEVICE.md). +If you want to run the app on an actual physical iOS device, please follow the instructions [here](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_BUILD_APP_ON_PHYSICAL_IOS_DEVICE.md). ## Running the Android app 🤖 * Before installing Android dependencies, you need to obtain a token from Mapbox to download their SDKs. Please run `npm run configure-mapbox` and follow the instructions. If you already did this step for iOS, there is no need to repeat this step. From 6fb17e73d6871843342f0dc05152a7976c70868e Mon Sep 17 00:00:00 2001 From: someone-here Date: Sat, 17 Feb 2024 20:21:41 +0530 Subject: [PATCH 150/225] Request location only when necessary in MapView --- src/components/MapView/MapView.tsx | 36 +++++++++++++--------- src/components/MapView/MapView.website.tsx | 36 +++++++++++++--------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index a3178f642852..bbf727484bf9 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -32,6 +32,20 @@ const MapView = forwardRef( const [userInteractedWithMap, setUserInteractedWithMap] = useState(false); const hasAskedForLocationPermission = useRef(false); + // Determines if map can be panned to user's detected + // location without bothering the user. It will return + // false if user has already started dragging the map or + // if there are one or more waypoints present. + const shouldPanMapToCurrentPosition = useCallback(() => !userInteractedWithMap && (!waypoints || waypoints.length === 0), [userInteractedWithMap, waypoints]); + + const setCurrentPositionToInitialState = useCallback(() => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (cachedUserLocation || !initialState) { + return; + } + setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); + }, [initialState, setCurrentPosition, cachedUserLocation]); + useFocusEffect( useCallback(() => { if (isOffline) { @@ -42,6 +56,11 @@ const MapView = forwardRef( return; } + if (!shouldPanMapToCurrentPosition()) { + setCurrentPositionToInitialState(); + return; + } + hasAskedForLocationPermission.current = true; getCurrentPosition( (params) => { @@ -49,24 +68,11 @@ const MapView = forwardRef( setCurrentPosition(currentCoords); setUserLocation(currentCoords); }, - () => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (cachedUserLocation || !initialState) { - return; - } - - setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); - }, + setCurrentPositionToInitialState, ); - }, [cachedUserLocation, initialState, isOffline]), + }, [cachedUserLocation, initialState, isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), ); - // Determines if map can be panned to user's detected - // location without bothering the user. It will return - // false if user has already started dragging the map or - // if there are one or more waypoints present. - const shouldPanMapToCurrentPosition = useCallback(() => !userInteractedWithMap && (!waypoints || waypoints.length === 0), [userInteractedWithMap, waypoints]); - useEffect(() => { if (!currentPosition || !cameraRef.current) { return; diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 289f7d0d62a8..fa4ffd9a28e8 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -55,6 +55,20 @@ const MapView = forwardRef( const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []); const hasAskedForLocationPermission = useRef(false); + // Determines if map can be panned to user's detected + // location without bothering the user. It will return + // false if user has already started dragging the map or + // if there are one or more waypoints present. + const shouldPanMapToCurrentPosition = useCallback(() => !userInteractedWithMap && (!waypoints || waypoints.length === 0), [userInteractedWithMap, waypoints]); + + const setCurrentPositionToInitialState = useCallback(() => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (cachedUserLocation || !initialState) { + return; + } + setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); + }, [initialState, setCurrentPosition, cachedUserLocation]); + useFocusEffect( useCallback(() => { if (isOffline) { @@ -65,6 +79,11 @@ const MapView = forwardRef( return; } + if (!shouldPanMapToCurrentPosition()) { + setCurrentPositionToInitialState(); + return; + } + hasAskedForLocationPermission.current = true; getCurrentPosition( (params) => { @@ -72,24 +91,11 @@ const MapView = forwardRef( setCurrentPosition(currentCoords); setUserLocation(currentCoords); }, - () => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (cachedUserLocation || !initialState) { - return; - } - - setCurrentPosition({longitude: initialState.location[0], latitude: initialState.location[1]}); - }, + setCurrentPositionToInitialState, ); - }, [cachedUserLocation, initialState, isOffline]), + }, [cachedUserLocation, initialState, isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), ); - // Determines if map can be panned to user's detected - // location without bothering the user. It will return - // false if user has already started dragging the map or - // if there are one or more waypoints present. - const shouldPanMapToCurrentPosition = useCallback(() => !userInteractedWithMap && (!waypoints || waypoints.length === 0), [userInteractedWithMap, waypoints]); - useEffect(() => { if (!currentPosition || !mapRef) { return; From d1725f4f127ca1fcc7da1ea28dd4f4a1c70a2fc3 Mon Sep 17 00:00:00 2001 From: someone-here Date: Sat, 17 Feb 2024 20:35:45 +0530 Subject: [PATCH 151/225] Fix lint --- src/components/MapView/MapView.tsx | 2 +- src/components/MapView/MapView.website.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index bbf727484bf9..eff316c4213f 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -70,7 +70,7 @@ const MapView = forwardRef( }, setCurrentPositionToInitialState, ); - }, [cachedUserLocation, initialState, isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), + }, [isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), ); useEffect(() => { diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index fa4ffd9a28e8..1175f0ad72f8 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -93,7 +93,7 @@ const MapView = forwardRef( }, setCurrentPositionToInitialState, ); - }, [cachedUserLocation, initialState, isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), + }, [isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), ); useEffect(() => { From ed824786d97ef0b46fe4ef9e2e4028e51687d1c0 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sat, 17 Feb 2024 18:06:31 +0200 Subject: [PATCH 152/225] Hide the double offline indicator in bank steps --- .../BankInfo/substeps/Confirmation.tsx | 87 +++++----- .../ConfirmationUBO.tsx | 151 +++++++++-------- .../substeps/CompanyOwnersListUBO.tsx | 83 +++++---- .../PersonalInfo/substeps/Confirmation.tsx | 157 +++++++++--------- 4 files changed, 233 insertions(+), 245 deletions(-) diff --git a/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx b/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx index f2070e38b942..4105a5f03f36 100644 --- a/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx +++ b/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import DotIndicatorMessage from '@components/DotIndicatorMessage'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import type {SubStepProps} from '@hooks/useSubStep/types'; @@ -46,57 +45,55 @@ function Confirmation({reimbursementAccount, reimbursementAccountDraft, onNext, }; return ( - - - {translate('bankAccount.letsDoubleCheck')} - {translate('bankAccount.thisBankAccount')} - {setupType === CONST.BANK_ACCOUNT.SUBSTEP.MANUAL && ( - - - - - - )} - {setupType === CONST.BANK_ACCOUNT.SUBSTEP.PLAID && ( + {translate('bankAccount.letsDoubleCheck')} + {translate('bankAccount.thisBankAccount')} + {setupType === CONST.BANK_ACCOUNT.SUBSTEP.MANUAL && ( + - )} - - {error && error.length > 0 && ( - - )} -