From 6749e0abc0d041cfe0c4349c36a5982045cf1ae6 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 7 Dec 2023 22:39:15 +0700 Subject: [PATCH 001/213] fix: pdf not cached --- src/components/PDFView/index.native.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index b022823d215a..417106618c6d 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -11,6 +11,7 @@ import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeSt import withWindowDimensions from '@components/withWindowDimensions'; import compose from '@libs/compose'; import * as StyleUtils from '@styles/StyleUtils'; +import * as CachedPDFPaths from '@userActions/CachedPDFPaths'; import CONST from '@src/CONST'; import PDFPasswordForm from './PDFPasswordForm'; import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; @@ -118,7 +119,9 @@ class PDFView extends Component { * After the PDF is successfully loaded hide PDFPasswordForm and the loading * indicator. */ - finishPDFLoad() { + finishPDFLoad(_, path) { + console.log(path) + // CachedPDFPaths.save(path); this.setState({ shouldRequestPassword: false, shouldShowLoadingIndicator: false, @@ -155,7 +158,7 @@ class PDFView extends Component { fitPolicy={0} trustAllCerts={false} renderActivityIndicator={() => } - source={{uri: this.props.sourceURL}} + source={{uri: this.props.sourceURL, cache: true, expiration: 864000}} style={pdfStyles} onError={this.handleFailureToLoadPDF} password={this.state.password} From 0624e89a3f80065d08f2c271e78b505f779520e1 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 7 Dec 2023 22:40:08 +0700 Subject: [PATCH 002/213] fix lint --- src/components/PDFView/index.native.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index 417106618c6d..4f09906ad6fe 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -11,7 +11,6 @@ import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeSt import withWindowDimensions from '@components/withWindowDimensions'; import compose from '@libs/compose'; import * as StyleUtils from '@styles/StyleUtils'; -import * as CachedPDFPaths from '@userActions/CachedPDFPaths'; import CONST from '@src/CONST'; import PDFPasswordForm from './PDFPasswordForm'; import {defaultProps, propTypes as pdfViewPropTypes} from './pdfViewPropTypes'; @@ -120,8 +119,6 @@ class PDFView extends Component { * indicator. */ finishPDFLoad(_, path) { - console.log(path) - // CachedPDFPaths.save(path); this.setState({ shouldRequestPassword: false, shouldShowLoadingIndicator: false, From 552592cd2ab1b75e54f10e4450dec427fe6dee02 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 7 Dec 2023 22:56:24 +0700 Subject: [PATCH 003/213] fix lint --- src/components/PDFView/index.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index 4f09906ad6fe..69a47ce82d9c 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -118,7 +118,7 @@ class PDFView extends Component { * After the PDF is successfully loaded hide PDFPasswordForm and the loading * indicator. */ - finishPDFLoad(_, path) { + finishPDFLoad() { this.setState({ shouldRequestPassword: false, shouldShowLoadingIndicator: false, From 2a4d712ab8651d58ba1c2e12c63d59a1e9178c54 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 22 Dec 2023 16:08:45 +0800 Subject: [PATCH 004/213] update ioutype params when selecting a split participant --- src/libs/actions/IOU.js | 15 ++++++++++++ ...yForRefactorRequestParticipantsSelector.js | 23 ++++++++++-------- .../step/IOURequestStepParticipants.js | 24 ++++++++++++++++--- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index a9d04654f8bd..1c8f30d75498 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -23,6 +23,7 @@ import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -169,6 +170,19 @@ function clearMoneyRequest(transactionID) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null); } +/** + * @param {Object[]} routes + * @param {String} newIouType + */ +function updateMoneyRequestTypeParams(routes, newIouType) { + routes.forEach((route) => { + if (route.name !== SCREENS.MONEY_REQUEST.CREATE && route.name !== SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS) { + return; + } + Navigation.setParams({iouType: newIouType}, route.key); + }); +} + /** * @param {String} transactionID * @param {Number} amount @@ -3481,6 +3495,7 @@ export { resetMoneyRequestTag, resetMoneyRequestTag_temporaryForRefactor, clearMoneyRequest, + updateMoneyRequestTypeParams, setMoneyRequestAmount_temporaryForRefactor, setMoneyRequestBillable_temporaryForRefactor, setMoneyRequestCategory_temporaryForRefactor, diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 8d7d5cfceb77..6952f2e3cfa4 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -163,13 +163,16 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ * @param {Object} option */ const addSingleParticipant = (option) => { - onParticipantsAdded([ - { - ..._.pick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText'), - selected: true, - }, - ]); - onFinish(); + onParticipantsAdded( + [ + { + ..._.pick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText'), + selected: true, + }, + ], + false, + ); + onFinish(false); }; /** @@ -208,7 +211,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ ]; } - onParticipantsAdded(newSelectedOptions); + onParticipantsAdded(newSelectedOptions, newSelectedOptions.length !== 0); }, [participants, onParticipantsAdded], ); @@ -276,7 +279,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ return; } - onFinish(); + onFinish(true); }, [shouldShowSplitBillErrorMessage, onFinish]); const footerContent = ( @@ -315,7 +318,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ boldStyle shouldShowConfirmButton={shouldShowSplitBillErrorMessage && isAllowedToSplit} confirmButtonText={translate('iou.addToSplit')} - onConfirmSelection={onFinish} + onConfirmSelection={() => onFinish(true)} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} textInputAlert={isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''} safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index ec670b828146..38ca52730512 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -1,3 +1,4 @@ +import {useNavigationState} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef} from 'react'; import transactionPropTypes from '@components/transactionPropTypes'; @@ -36,13 +37,16 @@ function IOURequestStepParticipants({ transaction: {participants = []}, }) { const {translate} = useLocalize(); + const routes = useNavigationState((state) => state.routes); const optionsSelectorRef = useRef(); const selectedReportID = useRef(reportID); const numberOfParticipants = useRef(participants.length); const iouRequestType = TransactionUtils.getRequestType(transaction); - const headerTitle = translate(TransactionUtils.getHeaderTitleTranslationKey(transaction)); + const isSplitRequest = iouType === CONST.IOU.TYPE.SPLIT; + const headerTitle = isSplitRequest ? translate('iou.split') : translate(TransactionUtils.getHeaderTitleTranslationKey(transaction)); const receiptFilename = lodashGet(transaction, 'filename'); const receiptPath = lodashGet(transaction, 'receipt.source'); + const newIouType = useRef(null); // When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow. // This is because until the request is saved, the receipt file is only stored in the browsers memory as a blob:// and if the browser is refreshed, then @@ -51,8 +55,22 @@ function IOURequestStepParticipants({ IOUUtils.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, () => {}, iouRequestType, iouType, transactionID, reportID); }, [receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID]); + useEffect(() => { + if (!newIouType.current) { + return; + } + IOU.updateMoneyRequestTypeParams(routes, newIouType.current); + newIouType.current = null; + }, [routes, participants]); + const addParticipant = useCallback( - (val) => { + (val, isSplit) => { + if (isSplit && iouType !== CONST.IOU.TYPE.SPLIT) { + newIouType.current = CONST.IOU.TYPE.SPLIT; + } else if (!isSplit && iouType === CONST.IOU.TYPE.SPLIT) { + newIouType.current = CONST.IOU.TYPE.REQUEST; + } + IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); numberOfParticipants.current = val.length; @@ -66,7 +84,7 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. selectedReportID.current = lodashGet(val, '[0].reportID', reportID); }, - [reportID, transactionID], + [reportID, transactionID, iouType], ); const goToNextStep = useCallback(() => { From 35d1ad8fb9e69da6aa5af7e459fd2a00a9aa02da Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 22 Dec 2023 16:09:39 +0800 Subject: [PATCH 005/213] allow to split with 1 person --- .../request/step/IOURequestStepParticipants.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 38ca52730512..fa9887635d37 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -87,12 +87,15 @@ function IOURequestStepParticipants({ [reportID, transactionID, iouType], ); - const goToNextStep = useCallback(() => { - const nextStepIOUType = numberOfParticipants.current === 1 ? iouType : CONST.IOU.TYPE.SPLIT; - IOU.resetMoneyRequestTag_temporaryForRefactor(transactionID); - IOU.resetMoneyRequestCategory_temporaryForRefactor(transactionID); - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(nextStepIOUType, transactionID, selectedReportID.current || reportID)); - }, [iouType, transactionID, reportID]); + const goToNextStep = useCallback( + (isSplit) => { + const nextStepIOUType = !isSplit && iouType !== CONST.IOU.TYPE.REQUEST ? CONST.IOU.TYPE.REQUEST : iouType; + IOU.resetMoneyRequestTag_temporaryForRefactor(transactionID); + IOU.resetMoneyRequestCategory_temporaryForRefactor(transactionID); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(nextStepIOUType, transactionID, selectedReportID.current || reportID)); + }, + [iouType, transactionID, reportID], + ); const navigateBack = useCallback(() => { IOUUtils.navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID); From 91c0b691ec326d8115a97fce1d0069b982b1210f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 22 Dec 2023 16:09:57 +0800 Subject: [PATCH 006/213] don't include selected participant if not split --- src/pages/iou/request/step/IOURequestStepParticipants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index fa9887635d37..17c0fc4e4918 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -112,7 +112,7 @@ function IOURequestStepParticipants({ > (optionsSelectorRef.current = el)} - participants={participants} + participants={isSplitRequest ? participants : []} onParticipantsAdded={addParticipant} onFinish={goToNextStep} iouType={iouType} From c4232f34d6e16d037ad8ffb574aa9be29c76dd9e Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 22 Dec 2023 16:10:12 +0800 Subject: [PATCH 007/213] reset ioutype if tab changed --- src/pages/iou/request/IOURequestStartPage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.js index 6572e154ee14..b5c2348cf72c 100644 --- a/src/pages/iou/request/IOURequestStartPage.js +++ b/src/pages/iou/request/IOURequestStartPage.js @@ -1,3 +1,4 @@ +import {useNavigationState} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; @@ -61,6 +62,7 @@ function IOURequestStartPage({ }) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const routes = useNavigationState((state) => state.routes); const [isDraggingOver, setIsDraggingOver] = useState(false); const tabTitles = { [CONST.IOU.TYPE.REQUEST]: translate('iou.requestMoney'), @@ -103,10 +105,13 @@ function IOURequestStartPage({ if (newIouType === previousIOURequestType) { return; } + if (iouType === CONST.IOU.TYPE.SPLIT) { + IOU.updateMoneyRequestTypeParams(routes, CONST.IOU.TYPE.REQUEST); + } IOU.startMoneyRequest_temporaryForRefactor(reportID, isFromGlobalCreate, newIouType); transactionRequestType.current = newIouType; }, - [previousIOURequestType, reportID, isFromGlobalCreate], + [previousIOURequestType, reportID, isFromGlobalCreate, iouType, routes], ); if (!transaction.transactionID) { From b34854f35fdce47cb9ec05e944eae232e4c91d9e Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 1 Jan 2024 22:31:01 +0800 Subject: [PATCH 008/213] add more comment --- src/pages/iou/request/step/IOURequestStepParticipants.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 17c0fc4e4918..8907cb4945e9 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -65,9 +65,13 @@ function IOURequestStepParticipants({ const addParticipant = useCallback( (val, isSplit) => { + // It's only possible to switch between REQUEST and SPLIT. + // We want to update the IOU type only if it's not updated yet to prevent unnecessary updates. if (isSplit && iouType !== CONST.IOU.TYPE.SPLIT) { newIouType.current = CONST.IOU.TYPE.SPLIT; } else if (!isSplit && iouType === CONST.IOU.TYPE.SPLIT) { + // Non-split can be either REQUEST or SEND. Instead of checking whether + // the current IOU type is not a REQUEST (true for SEND), we check whether the current IOU type is a SPLIT. newIouType.current = CONST.IOU.TYPE.REQUEST; } From 756c002b276c7e9d3662e6a3786ed02079166326 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 1 Jan 2024 22:44:38 +0800 Subject: [PATCH 009/213] prettier --- src/pages/iou/request/step/IOURequestStepParticipants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 8907cb4945e9..865695b9e38a 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -70,7 +70,7 @@ function IOURequestStepParticipants({ if (isSplit && iouType !== CONST.IOU.TYPE.SPLIT) { newIouType.current = CONST.IOU.TYPE.SPLIT; } else if (!isSplit && iouType === CONST.IOU.TYPE.SPLIT) { - // Non-split can be either REQUEST or SEND. Instead of checking whether + // Non-split can be either REQUEST or SEND. Instead of checking whether // the current IOU type is not a REQUEST (true for SEND), we check whether the current IOU type is a SPLIT. newIouType.current = CONST.IOU.TYPE.REQUEST; } From 15fb5de877950cd62ef5937bc0dd922cfb44c02c Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 2 Jan 2024 18:59:26 +0800 Subject: [PATCH 010/213] remove unnecessary logic --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index cb6225b641fc..0ba57ba012d4 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -104,11 +104,6 @@ function IOURequestStepConfirmation({ // If there is not a report attached to the IOU with a reportID, then the participants were manually selected and the user needs taken // back to the participants step if (!transaction.participantsAutoAssigned) { - // When going back to the participants step, if the iou is a "request" (not a split), then the participants need to be cleared from the - // transaction so that the participant can be selected again. - if (iouType === CONST.IOU.TYPE.REQUEST) { - IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, []); - } Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID)); return; } From 9f93e082ee945889492ee9b8c2a964f663134582 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 2 Jan 2024 19:12:32 +0800 Subject: [PATCH 011/213] add more comment --- src/pages/iou/request/step/IOURequestStepParticipants.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 865695b9e38a..495853206dc0 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -59,6 +59,8 @@ function IOURequestStepParticipants({ if (!newIouType.current) { return; } + // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before + // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. IOU.updateMoneyRequestTypeParams(routes, newIouType.current); newIouType.current = null; }, [routes, participants]); From 2c34c91b7a413f5add1dec3e21822ec32715ee65 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 4 Jan 2024 16:31:54 +0800 Subject: [PATCH 012/213] update iou type for all money request page --- src/libs/actions/IOU.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index a641f0d061d4..59bf2ddbdc0e 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -174,12 +174,14 @@ function clearMoneyRequest(transactionID) { } /** + * Update money request-related pages IOU type params + * * @param {Object[]} routes * @param {String} newIouType */ function updateMoneyRequestTypeParams(routes, newIouType) { routes.forEach((route) => { - if (route.name !== SCREENS.MONEY_REQUEST.CREATE && route.name !== SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS) { + if (!route.name.startsWith('Money_Request_')) { return; } Navigation.setParams({iouType: newIouType}, route.key); From ccbc27eea6b5daa9bb6f11cb28affde1327e6d9e Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 4 Jan 2024 16:39:56 +0800 Subject: [PATCH 013/213] remove unused import --- src/libs/actions/IOU.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 59bf2ddbdc0e..661c49379763 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -25,7 +25,6 @@ import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; import * as Policy from './Policy'; import * as Report from './Report'; From cef767ab13aad2c2c04fee5db681915e70c59976 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 9 Jan 2024 13:49:16 +0100 Subject: [PATCH 014/213] ref: started migratiing MoneyRequestConfirmationList --- ...st.js => MoneyRequestConfirmationList.tsx} | 195 ++++++++---------- 1 file changed, 91 insertions(+), 104 deletions(-) rename src/components/{MoneyRequestConfirmationList.js => MoneyRequestConfirmationList.tsx} (88%) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.tsx similarity index 88% rename from src/components/MoneyRequestConfirmationList.js rename to src/components/MoneyRequestConfirmationList.tsx index 13dce9337673..5d0c12944d78 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -4,8 +4,8 @@ import {isEmpty} from 'lodash'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useReducer, useState} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {StyleProp, View, ViewStyle} from 'react-native'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; @@ -28,6 +28,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import * as OnyxTypes from '@src/types/onyx'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import categoryPropTypes from './categoryPropTypes'; import ConfirmedRoute from './ConfirmedRoute'; @@ -43,135 +44,115 @@ import tagPropTypes from './tagPropTypes'; import taxPropTypes from './taxPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; +import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; -const propTypes = { - /** Callback to inform parent modal of success */ - onConfirm: PropTypes.func, - - /** Callback to parent modal to send money */ - onSendMoney: PropTypes.func, - - /** Callback to inform a participant is selected */ - onSelectParticipant: PropTypes.func, - - /** Should we request a single or multiple participant selection from user */ - hasMultipleParticipants: PropTypes.bool.isRequired, - - /** IOU amount */ - iouAmount: PropTypes.number.isRequired, - - /** IOU comment */ - iouComment: PropTypes.string, - - /** IOU currency */ - iouCurrencyCode: PropTypes.string, - - /** IOU type */ - iouType: PropTypes.string, - - /** IOU date */ - iouCreated: PropTypes.string, - - /** IOU merchant */ - iouMerchant: PropTypes.string, - - /** IOU Category */ - iouCategory: PropTypes.string, +type MoneyRequestConfirmationListOnyxProps = { + iou?: OnyxTypes.IOU; + policyTaxRates?: any; + session: OnyxTypes.Session; + transaction?: OnyxTypes.Transaction; + mileageRate?: { + unit?: typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES | typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS; + rate?: number; + currency?: string; + }; + policyCategories?: any; + policyTags?: any; + policy?: OnyxEntry; +}; +type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & + WithCurrentUserPersonalDetailsProps & { + /** Callback to inform parent modal of success */ + onConfirm?: () => void; - /** IOU Tag */ - iouTag: PropTypes.string, + /** Callback to parent modal to send money */ + onSendMoney?: () => void; - /** IOU isBillable */ - iouIsBillable: PropTypes.bool, + /** Callback to inform a participant is selected */ + onSelectParticipant?: () => void; - /** Callback to toggle the billable state */ - onToggleBillable: PropTypes.func, + /** Should we request a single or multiple participant selection from user */ + hasMultipleParticipants: boolean; - /** Selected participants from MoneyRequestModal with login / accountID */ - selectedParticipants: PropTypes.arrayOf(optionPropTypes).isRequired, + /** IOU amount */ + iouAmount: number; - /** Payee of the money request with login */ - payeePersonalDetails: optionPropTypes, + /** IOU comment */ + iouComment?: string; - /** Can the participants be modified or not */ - canModifyParticipants: PropTypes.bool, + /** IOU currency */ + iouCurrencyCode?: string; - /** Should the list be read only, and not editable? */ - isReadOnly: PropTypes.bool, + /** IOU type */ + iouType?: string; - /** Depending on expense report or personal IOU report, respective bank account route */ - bankAccountRoute: PropTypes.string, + /** IOU date */ + iouCreated?: string; - ...withCurrentUserPersonalDetailsPropTypes, + /** IOU merchant */ + iouMerchant?: string; - /** Current user session */ - session: PropTypes.shape({ - email: PropTypes.string.isRequired, - }), + /** IOU Category */ + iouCategory?: string; - /** The policyID of the request */ - policyID: PropTypes.string, + /** IOU Tag */ + iouTag?: string; - /** The reportID of the request */ - reportID: PropTypes.string, + /** IOU isBillable */ + iouIsBillable?: boolean; - /** File path of the receipt */ - receiptPath: PropTypes.string, + /** Callback to toggle the billable state */ + onToggleBillable?: () => void; - /** File name of the receipt */ - receiptFilename: PropTypes.string, + /** Selected participants from MoneyRequestModal with login / accountID */ + selectedParticipants: OptionType[]; - /** List styles for OptionsSelector */ - listStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), + /** Payee of the money request with login */ + payeePersonalDetails?: OptionType; - /** ID of the transaction that represents the money request */ - transactionID: PropTypes.string, + /** Can the participants be modified or not */ + canModifyParticipants?: boolean; - /** Transaction that represents the money request */ - transaction: transactionPropTypes, + /** Should the list be read only, and not editable? */ + isReadOnly?: boolean; - /** Unit and rate used for if the money request is a distance request */ - mileageRate: PropTypes.shape({ - /** Unit used to represent distance */ - unit: PropTypes.oneOf([CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS]), + /** Depending on expense report or personal IOU report, respective bank account route */ + bankAccountRoute?: string; - /** Rate used to calculate the distance request amount */ - rate: PropTypes.number, + /** The policyID of the request */ + policyID?: string; - /** The currency of the rate */ - currency: PropTypes.string, - }), + /** The reportID of the request */ + reportID?: string; - /** Whether the money request is a distance request */ - isDistanceRequest: PropTypes.bool, + /** File path of the receipt */ + receiptPath?: string; - /** Whether the money request is a scan request */ - isScanRequest: PropTypes.bool, + /** File name of the receipt */ + receiptFilename?: string; - /** Whether we're editing a split bill */ - isEditingSplitBill: PropTypes.bool, + /** List styles for OptionsSelector */ + listStyles?: StyleProp; - /** Whether we should show the amount, date, and merchant fields. */ - shouldShowSmartScanFields: PropTypes.bool, + /** ID of the transaction that represents the money request */ + transactionID?: string; - /** A flag for verifying that the current report is a sub-report of a workspace chat */ - isPolicyExpenseChat: PropTypes.bool, + /** Whether the money request is a distance request */ + isDistanceRequest?: boolean; - /* Onyx Props */ - /** Collection of categories attached to a policy */ - policyCategories: PropTypes.objectOf(categoryPropTypes), + /** Whether the money request is a scan request */ + isScanRequest?: boolean; - /** Collection of tags attached to a policy */ - policyTags: tagPropTypes, + /** Whether we're editing a split bill */ + isEditingSplitBill?: boolean; - /* Onyx Props */ - /** Collection of tax rates attached to a policy */ - policyTaxRates: taxPropTypes, + /** Whether we should show the amount, date, and merchant fields. */ + shouldShowSmartScanFields?: boolean; - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, -}; + /** A flag for verifying that the current report is a sub-report of a workspace chat */ + isPolicyExpenseChat?: boolean; + }; const defaultProps = { onConfirm: () => {}, @@ -208,12 +189,18 @@ const defaultProps = { policyTaxRates: {}, }; -function MoneyRequestConfirmationList(props) { +function MoneyRequestConfirmationList({ + onConfirm = () => {}, + onSendMoney = () => {}, + onSelectParticipant = () => {}, + iouType = CONST.IOU.TYPE.REQUEST, + iouCategory = '', + iouTag = '', + iouIsBillable = false, + onToggleBillable = () => {}, +}: MoneyRequestConfirmationListProps) { const theme = useTheme(); const styles = useThemeStyles(); - // Destructure functions from props to pass it as a dependecy to useCallback/useMemo hooks. - // Prop functions pass props itself as a "this" value to the function which means they change every time props change. - const {onSendMoney, onConfirm, onSelectParticipant} = props; const {translate, toLocaleDigit} = useLocalize(); const transaction = props.transaction; const {canUseViolations} = usePermissions(); From 0cfed1e596bfa3c2be19418b545645ff751f3443 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 10 Jan 2024 00:25:42 +0800 Subject: [PATCH 015/213] returns the onyx promise --- src/libs/actions/IOU.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f92b91dff539..bc3b9f490f34 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -270,9 +270,10 @@ function setMoneyRequestBillable_temporaryForRefactor(transactionID, billable) { /** * @param {String} transactionID * @param {Object[]} participants + * @returns {Promise} */ function setMoneyRequestParticipants_temporaryForRefactor(transactionID, participants) { - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); + return Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); } /** From 7320bf70b5f3f0121f35cba641b9179cd6ccc6e9 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 10 Jan 2024 00:26:12 +0800 Subject: [PATCH 016/213] use a promise approach to update the route params --- .../step/IOURequestStepParticipants.js | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 495853206dc0..894cc3ae0367 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -46,7 +46,6 @@ function IOURequestStepParticipants({ const headerTitle = isSplitRequest ? translate('iou.split') : translate(TransactionUtils.getHeaderTitleTranslationKey(transaction)); const receiptFilename = lodashGet(transaction, 'filename'); const receiptPath = lodashGet(transaction, 'receipt.source'); - const newIouType = useRef(null); // When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow. // This is because until the request is saved, the receipt file is only stored in the browsers memory as a blob:// and if the browser is refreshed, then @@ -55,29 +54,26 @@ function IOURequestStepParticipants({ IOUUtils.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, () => {}, iouRequestType, iouType, transactionID, reportID); }, [receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID]); - useEffect(() => { - if (!newIouType.current) { - return; - } - // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before - // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. - IOU.updateMoneyRequestTypeParams(routes, newIouType.current); - newIouType.current = null; - }, [routes, participants]); - const addParticipant = useCallback( (val, isSplit) => { + let newIouType; // It's only possible to switch between REQUEST and SPLIT. // We want to update the IOU type only if it's not updated yet to prevent unnecessary updates. if (isSplit && iouType !== CONST.IOU.TYPE.SPLIT) { - newIouType.current = CONST.IOU.TYPE.SPLIT; + newIouType = CONST.IOU.TYPE.SPLIT; } else if (!isSplit && iouType === CONST.IOU.TYPE.SPLIT) { // Non-split can be either REQUEST or SEND. Instead of checking whether // the current IOU type is not a REQUEST (true for SEND), we check whether the current IOU type is a SPLIT. - newIouType.current = CONST.IOU.TYPE.REQUEST; + newIouType = CONST.IOU.TYPE.REQUEST; } - IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); + IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val).then(() => { + if (newIouType) { + // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before + // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. + IOU.updateMoneyRequestTypeParams(routes, newIouType); + } + }); numberOfParticipants.current = val.length; // When multiple participants are selected, the reportID is generated at the end of the confirmation step. @@ -90,7 +86,7 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. selectedReportID.current = lodashGet(val, '[0].reportID', reportID); }, - [reportID, transactionID, iouType], + [reportID, transactionID, iouType, routes], ); const goToNextStep = useCallback( From 6875ca835e773017f9a4b195841433de1ef72033 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 10 Jan 2024 00:29:24 +0800 Subject: [PATCH 017/213] lint --- src/pages/iou/request/step/IOURequestStepParticipants.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 894cc3ae0367..aa8a3dac84e6 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -68,11 +68,12 @@ function IOURequestStepParticipants({ } IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val).then(() => { - if (newIouType) { - // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before - // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. - IOU.updateMoneyRequestTypeParams(routes, newIouType); + if (!newIouType) { + return; } + // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before + // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. + IOU.updateMoneyRequestTypeParams(routes, newIouType); }); numberOfParticipants.current = val.length; From d76e0dfcb8c726158f817641b432d93c4e53d330 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 9 Jan 2024 18:49:30 +0100 Subject: [PATCH 018/213] ref: keep migrating --- .../MoneyRequestConfirmationList.tsx | 522 +++++++++--------- .../TextInput/BaseTextInput/types.ts | 2 +- src/libs/ReportUtils.ts | 2 +- src/types/onyx/Policy.ts | 13 +- 4 files changed, 270 insertions(+), 269 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 5d0c12944d78..87c6db42c3b5 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -1,12 +1,12 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; import {isEmpty} from 'lodash'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useReducer, useState} from 'react'; -import {StyleProp, View, ViewStyle} from 'react-native'; -import {OnyxEntry, withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {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 {ValueOf} from 'type-fest'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; @@ -22,55 +22,49 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import type {Participant} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import * as OnyxTypes from '@src/types/onyx'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {MileageRate} from '@src/types/onyx/Policy'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; -import categoryPropTypes from './categoryPropTypes'; import ConfirmedRoute from './ConfirmedRoute'; import FormHelpMessage from './FormHelpMessage'; import Image from './Image'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; -import optionPropTypes from './optionPropTypes'; import OptionsSelector from './OptionsSelector'; import SettlementButton from './SettlementButton'; import ShowMoreButton from './ShowMoreButton'; import Switch from './Switch'; -import tagPropTypes from './tagPropTypes'; -import taxPropTypes from './taxPropTypes'; import Text from './Text'; -import transactionPropTypes from './transactionPropTypes'; import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersonalDetails'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; type MoneyRequestConfirmationListOnyxProps = { - iou?: OnyxTypes.IOU; - policyTaxRates?: any; + iou: OnyxEntry; + policyTaxRates: any; session: OnyxTypes.Session; - transaction?: OnyxTypes.Transaction; - mileageRate?: { - unit?: typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES | typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS; - rate?: number; - currency?: string; - }; - policyCategories?: any; - policyTags?: any; + transaction: OnyxTypes.Transaction; + mileageRate: OnyxEntry; + policyCategories: OnyxEntry; + policyTags: OnyxEntry; policy?: OnyxEntry; }; + type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & WithCurrentUserPersonalDetailsProps & { /** Callback to inform parent modal of success */ - onConfirm?: () => void; + onConfirm?: (selectedParticipants: Participant[]) => void; /** Callback to parent modal to send money */ - onSendMoney?: () => void; + onSendMoney?: (paymentMethod: ValueOf) => void; /** Callback to inform a participant is selected */ - onSelectParticipant?: () => void; + onSelectParticipant?: (option: Participant) => void; /** Should we request a single or multiple participant selection from user */ hasMultipleParticipants: boolean; @@ -85,7 +79,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & iouCurrencyCode?: string; /** IOU type */ - iouType?: string; + iouType?: ValueOf; /** IOU date */ iouCreated?: string; @@ -106,10 +100,10 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & onToggleBillable?: () => void; /** Selected participants from MoneyRequestModal with login / accountID */ - selectedParticipants: OptionType[]; + selectedParticipants: Participant[]; /** Payee of the money request with login */ - payeePersonalDetails?: OptionType; + payeePersonalDetails?: OnyxEntry; /** Can the participants be modified or not */ canModifyParticipants?: boolean; @@ -152,42 +146,11 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & /** A flag for verifying that the current report is a sub-report of a workspace chat */ isPolicyExpenseChat?: boolean; - }; -const defaultProps = { - onConfirm: () => {}, - onSendMoney: () => {}, - onSelectParticipant: () => {}, - iouType: CONST.IOU.TYPE.REQUEST, - iouCategory: '', - iouTag: '', - iouIsBillable: false, - onToggleBillable: () => {}, - payeePersonalDetails: null, - canModifyParticipants: false, - isReadOnly: false, - bankAccountRoute: '', - session: { - email: null, - }, - policyID: '', - reportID: '', - ...withCurrentUserPersonalDetailsDefaultProps, - receiptPath: '', - receiptFilename: '', - listStyles: [], - policyCategories: {}, - policyTags: {}, - transactionID: '', - transaction: {}, - mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'}, - isDistanceRequest: false, - isScanRequest: false, - shouldShowSmartScanFields: true, - isPolicyExpenseChat: false, - iou: iouDefaultProps, - policyTaxRates: {}, -}; + hasSmartScanFailed?: boolean; + + reportActionID?: string; + }; function MoneyRequestConfirmationList({ onConfirm = () => {}, @@ -198,81 +161,114 @@ function MoneyRequestConfirmationList({ iouTag = '', iouIsBillable = false, onToggleBillable = () => {}, + payeePersonalDetails = null, + canModifyParticipants = false, + isReadOnly = false, + bankAccountRoute = '', + policyID = '', + reportID = '', + receiptPath = '', + receiptFilename = '', + transactionID = '', + mileageRate = {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'}, + isDistanceRequest = false, + isScanRequest = false, + shouldShowSmartScanFields = true, + isPolicyExpenseChat = false, + transaction, + iouAmount, + policyTags, + policyCategories, + policy, + policyTaxRates, + iouCurrencyCode, + isEditingSplitBill, + hasSmartScanFailed, + iouMerchant, + currentUserPersonalDetails, + hasMultipleParticipants, + selectedParticipants, + session, + iou, + reportActionID, + iouCreated, + listStyles, + iouComment, }: MoneyRequestConfirmationListProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); - const transaction = props.transaction; const {canUseViolations} = usePermissions(); - const isTypeRequest = props.iouType === CONST.IOU.TYPE.REQUEST; - const isSplitBill = props.iouType === CONST.IOU.TYPE.SPLIT; - const isTypeSend = props.iouType === CONST.IOU.TYPE.SEND; + const isTypeRequest = iouType === CONST.IOU.TYPE.REQUEST; + const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; + const isTypeSend = iouType === CONST.IOU.TYPE.SEND; - const isSplitWithScan = isSplitBill && props.isScanRequest; + const isSplitWithScan = isSplitBill && isScanRequest; - const {unit, rate, currency} = props.mileageRate; - const distance = lodashGet(transaction, 'routes.route0.distance', 0); - const shouldCalculateDistanceAmount = props.isDistanceRequest && props.iouAmount === 0; + const distance = transaction?.routes?.route0.distance ?? 0; + const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = props.isPolicyExpenseChat && (props.iouCategory || OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories))); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); // Do not hide fields in case of send money request - const shouldShowAllFields = props.isDistanceRequest || shouldExpandFields || !props.shouldShowSmartScanFields || isTypeSend || props.isEditingSplitBill; + const shouldShowAllFields = isDistanceRequest || shouldExpandFields || !shouldShowSmartScanFields || isTypeSend || isEditingSplitBill; // In Send Money and Split Bill with Scan flow, we don't allow the Merchant or Date to be edited. For distance requests, don't show the merchant as there's already another "Distance" menu item const shouldShowDate = shouldShowAllFields && !isTypeSend && !isSplitWithScan; - const shouldShowMerchant = shouldShowAllFields && !isTypeSend && !props.isDistanceRequest && !isSplitWithScan; + const shouldShowMerchant = shouldShowAllFields && !isTypeSend && !isDistanceRequest && !isSplitWithScan; // Fetches the first tag list of the policy - const policyTag = PolicyUtils.getTag(props.policyTags); - const policyTagList = lodashGet(policyTag, 'tags', {}); - const policyTagListName = lodashGet(policyTag, 'name', translate('common.tag')); + const policyTag = PolicyUtils.getTag(policyTags); + const policyTagList = policyTag?.tags ?? {}; + const policyTagListName = policyTag?.name ?? translate('common.tag'); // A flag for showing the tags field - const shouldShowTags = props.isPolicyExpenseChat && (props.iouTag || OptionsListUtils.hasEnabledOptions(_.values(policyTagList))); + const shouldShowTags = isPolicyExpenseChat && (iouTag || OptionsListUtils.hasEnabledOptions(Object.values(policyTagList))); // A flag for showing tax fields - tax rate and tax amount - const shouldShowTax = props.isPolicyExpenseChat && props.policy.isTaxTrackingEnabled; + const shouldShowTax = isPolicyExpenseChat && policy?.isTaxTrackingEnabled; // A flag for showing the billable field - const shouldShowBillable = !lodashGet(props.policy, 'disabledFields.defaultBillable', true); + const shouldShowBillable = !policy?.disabledFields?.defaultBillable ?? true; const hasRoute = TransactionUtils.hasRoute(transaction); - const isDistanceRequestWithoutRoute = props.isDistanceRequest && !hasRoute; + const isDistanceRequestWithoutRoute = isDistanceRequest && !hasRoute; const formattedAmount = isDistanceRequestWithoutRoute ? translate('common.tbd') : CurrencyUtils.convertToDisplayString( - shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount, - props.isDistanceRequest ? currency : props.iouCurrencyCode, + shouldCalculateDistanceAmount + ? DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate?.rate) + : iouAmount, + isDistanceRequest ? mileageRate?.currency : iouCurrencyCode, ); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(props.transaction.taxAmount, props.iouCurrencyCode); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); - const defaultTaxKey = props.policyTaxRates.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${props.policyTaxRates.taxes[defaultTaxKey].name} (${props.policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - const taxRateTitle = (props.transaction.taxRate && props.transaction.taxRate.text) || defaultTaxName; + const defaultTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; + const taxRateTitle = transaction.taxRate?.text || defaultTaxName; const isFocused = useIsFocused(); - const [formError, setFormError] = useState(''); + const [formError, setFormError] = useState(null); const [didConfirm, setDidConfirm] = useState(false); const [didConfirmSplit, setDidConfirmSplit] = useState(false); const shouldDisplayFieldError = useMemo(() => { - if (!props.isEditingSplitBill) { + if (!isEditingSplitBill) { return false; } - return (props.hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); - }, [props.isEditingSplitBill, props.hasSmartScanFailed, transaction, didConfirmSplit]); + return (!!hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); + }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); - const isMerchantEmpty = !props.iouMerchant || props.iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; - const shouldDisplayMerchantError = props.isPolicyExpenseChat && !props.isScanRequest && isMerchantEmpty; + const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + const shouldDisplayMerchantError = isPolicyExpenseChat && !isScanRequest && isMerchantEmpty; useEffect(() => { - if (shouldDisplayFieldError && props.hasSmartScanFailed) { + if (shouldDisplayFieldError && hasSmartScanFailed) { setFormError('iou.receiptScanningFailed'); return; } @@ -281,83 +277,80 @@ function MoneyRequestConfirmationList({ return; } // reset the form error whenever the screen gains or loses focus - setFormError(''); - }, [isFocused, transaction, shouldDisplayFieldError, props.hasSmartScanFailed, didConfirmSplit]); + setFormError(null); + }, [isFocused, transaction, shouldDisplayFieldError, hasSmartScanFailed, didConfirmSplit]); useEffect(() => { if (!shouldCalculateDistanceAmount) { return; } - const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate.rate); IOU.setMoneyRequestAmount(amount); - }, [shouldCalculateDistanceAmount, distance, rate, unit]); + }, [shouldCalculateDistanceAmount, distance, mileageRate?.rate, mileageRate?.unit]); /** * Returns the participants with amount - * @param {Array} participants - * @returns {Array} */ const getParticipantsWithAmount = useCallback( - (participantsList) => { - const iouAmount = IOUUtils.calculateAmount(participantsList.length, props.iouAmount, props.iouCurrencyCode); + (participantsList: Participant[]) => { + const calculatedIouAmount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode ?? ''); return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( participantsList, - props.iouAmount > 0 ? CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode) : '', + calculatedIouAmount > 0 ? CurrencyUtils.convertToDisplayString(calculatedIouAmount, iouCurrencyCode) : '', ); }, - [props.iouAmount, props.iouCurrencyCode], + [iouAmount, iouCurrencyCode], ); // If completing a split bill fails, set didConfirm to false to allow the user to edit the fields again - if (props.isEditingSplitBill && didConfirm) { + if (isEditingSplitBill && didConfirm) { setDidConfirm(false); } const splitOrRequestOptions = useMemo(() => { let text; - if (isSplitBill && props.iouAmount === 0) { + if (isSplitBill && iouAmount === 0) { text = translate('iou.split'); - } else if ((props.receiptPath && isTypeRequest) || isDistanceRequestWithoutRoute) { + } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithoutRoute) { text = translate('iou.request'); - if (props.iouAmount !== 0) { - text = translate('iou.requestAmount', {amount: formattedAmount}); + if (iouAmount !== 0) { + text = translate('iou.requestAmount', {amount: Number(formattedAmount)}); } } else { const translationKey = isSplitBill ? 'iou.splitAmount' : 'iou.requestAmount'; - text = translate(translationKey, {amount: formattedAmount}); + text = translate(translationKey, {amount: Number(formattedAmount)}); } return [ { text: text[0].toUpperCase() + text.slice(1), - value: props.iouType, + value: iouType, }, ]; - }, [isSplitBill, isTypeRequest, props.iouType, props.iouAmount, props.receiptPath, formattedAmount, isDistanceRequestWithoutRoute, translate]); + }, [isSplitBill, isTypeRequest, iouType, iouAmount, receiptPath, formattedAmount, isDistanceRequestWithoutRoute, translate]); - const selectedParticipants = useMemo(() => _.filter(props.selectedParticipants, (participant) => participant.selected), [props.selectedParticipants]); - const payeePersonalDetails = useMemo(() => props.payeePersonalDetails || props.currentUserPersonalDetails, [props.payeePersonalDetails, props.currentUserPersonalDetails]); - const canModifyParticipants = !props.isReadOnly && props.canModifyParticipants && props.hasMultipleParticipants; - const shouldDisablePaidBySection = canModifyParticipants; + const selectedParticipantsMemo = useMemo(() => selectedParticipants.filter((participant) => participant.selected), [selectedParticipants]); + const payeePersonalDetailsMemo = useMemo(() => payeePersonalDetails ?? currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); + const canModifyParticipantsValue = !isReadOnly && canModifyParticipants && hasMultipleParticipants; const optionSelectorSections = useMemo(() => { const sections = []; - const unselectedParticipants = _.filter(props.selectedParticipants, (participant) => !participant.selected); - if (props.hasMultipleParticipants) { - const formattedSelectedParticipants = getParticipantsWithAmount(selectedParticipants); - let formattedParticipantsList = _.union(formattedSelectedParticipants, unselectedParticipants); + const unselectedParticipants = selectedParticipants.filter((participant) => !participant.selected); + if (hasMultipleParticipants) { + const formattedSelectedParticipants = getParticipantsWithAmount(selectedParticipantsMemo); + let formattedParticipantsList = [...new Set([...formattedSelectedParticipants, ...unselectedParticipants])]; - if (!canModifyParticipants) { - formattedParticipantsList = _.map(formattedParticipantsList, (participant) => ({ + if (!canModifyParticipantsValue) { + formattedParticipantsList = formattedParticipantsList.map((participant) => ({ ...participant, isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID), })); } - const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, props.iouAmount, props.iouCurrencyCode, true); + const myIOUAmount = IOUUtils.calculateAmount(selectedParticipantsMemo.length, iouAmount, iouCurrencyCode, true); const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( - payeePersonalDetails, - props.iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, props.iouCurrencyCode) : '', + payeePersonalDetailsMemo, + iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', ); sections.push( @@ -366,7 +359,7 @@ function MoneyRequestConfirmationList({ data: [formattedPayeeOption], shouldShow: true, indexOffset: 0, - isDisabled: shouldDisablePaidBySection, + isDisabled: canModifyParticipantsValue, }, { title: translate('moneyRequestConfirmationList.splitWith'), @@ -376,7 +369,7 @@ function MoneyRequestConfirmationList({ }, ); } else { - const formattedSelectedParticipants = _.map(props.selectedParticipants, (participant) => ({ + const formattedSelectedParticipants = selectedParticipants.map((participant) => ({ ...participant, isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID), })); @@ -389,50 +382,56 @@ function MoneyRequestConfirmationList({ } return sections; }, [ - props.selectedParticipants, - props.hasMultipleParticipants, - props.iouAmount, - props.iouCurrencyCode, - getParticipantsWithAmount, selectedParticipants, - payeePersonalDetails, + hasMultipleParticipants, + iouAmount, + iouCurrencyCode, + getParticipantsWithAmount, + payeePersonalDetailsMemo, translate, - shouldDisablePaidBySection, - canModifyParticipants, + canModifyParticipantsValue, + selectedParticipantsMemo, ]); const selectedOptions = useMemo(() => { - if (!props.hasMultipleParticipants) { + if (!hasMultipleParticipants) { return []; } - return [...selectedParticipants, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails)]; - }, [selectedParticipants, props.hasMultipleParticipants, payeePersonalDetails]); + return [...selectedParticipantsMemo, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetailsMemo)]; + }, [selectedParticipantsMemo, hasMultipleParticipants, payeePersonalDetailsMemo]); useEffect(() => { - if (!props.isDistanceRequest) { + if (!isDistanceRequest) { return; } - const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate, toLocaleDigit); - IOU.setMoneyRequestMerchant_temporaryForRefactor(props.transactionID, distanceMerchant); - }, [hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, props.isDistanceRequest, props.transactionID]); + const distanceMerchant = DistanceRequestUtils.getDistanceMerchant( + hasRoute, + distance, + mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + mileageRate?.rate ?? 0, + mileageRate?.currency ?? 'USD', + translate, + toLocaleDigit, + ); + IOU.setMoneyRequestMerchant_temporaryForRefactor(transactionID, distanceMerchant); + }, [hasRoute, distance, mileageRate?.unit, mileageRate?.rate, mileageRate?.currency, translate, toLocaleDigit, isDistanceRequest, transactionID]); /** * @param {Object} option */ const selectParticipant = useCallback( - (option) => { + (option: Participant) => { // Return early if selected option is currently logged in user. - if (option.accountID === props.session.accountID) { + if (option.accountID === session?.accountID) { return; } onSelectParticipant(option); }, - [props.session.accountID, onSelectParticipant], + [session.accountID, onSelectParticipant], ); /** * Navigate to report details or profile of selected user - * @param {Object} option */ const navigateToReportOrUserDetail = (option) => { if (option.accountID) { @@ -444,19 +443,16 @@ function MoneyRequestConfirmationList({ } }; - /** - * @param {String} paymentMethod - */ const confirm = useCallback( - (paymentMethod) => { - if (_.isEmpty(selectedParticipants)) { + (paymentMethod: ValueOf) => { + if (selectedParticipantsMemo.length === 0) { return; } - if (props.iouCategory && props.iouCategory.length > CONST.API_TRANSACTION_CATEGORY_MAX_LENGTH) { + if (iouCategory && iouCategory.length > CONST.API_TRANSACTION_CATEGORY_MAX_LENGTH) { setFormError('iou.error.invalidCategoryLength'); return; } - if (props.iouType === CONST.IOU.TYPE.SEND) { + if (iouType === CONST.IOU.TYPE.SEND) { if (!paymentMethod) { return; } @@ -467,44 +463,44 @@ function MoneyRequestConfirmationList({ onSendMoney(paymentMethod); } else { // validate the amount for distance requests - const decimals = CurrencyUtils.getCurrencyDecimals(props.iouCurrencyCode); - if (props.isDistanceRequest && !isDistanceRequestWithoutRoute && !MoneyRequestUtils.validateAmount(String(props.iouAmount), decimals)) { + const decimals = CurrencyUtils.getCurrencyDecimals(iouCurrencyCode); + if (isDistanceRequest && !isDistanceRequestWithoutRoute && !MoneyRequestUtils.validateAmount(String(iouAmount), decimals)) { setFormError('common.error.invalidAmount'); return; } - if (props.isEditingSplitBill && TransactionUtils.areRequiredFieldsEmpty(transaction)) { + if (isEditingSplitBill && TransactionUtils.areRequiredFieldsEmpty(transaction)) { setDidConfirmSplit(true); setFormError('iou.error.genericSmartscanFailureMessage'); return; } setDidConfirm(true); - onConfirm(selectedParticipants); + onConfirm(selectedParticipantsMemo); } }, [ - selectedParticipants, + selectedParticipantsMemo, + iouCategory, + iouType, onSendMoney, - onConfirm, - props.isEditingSplitBill, - props.iouType, - props.isDistanceRequest, - props.iouCategory, + iouCurrencyCode, + isDistanceRequest, isDistanceRequestWithoutRoute, - props.iouCurrencyCode, - props.iouAmount, + iouAmount, + isEditingSplitBill, transaction, + onConfirm, ], ); const footerContent = useMemo(() => { - if (props.isReadOnly) { + if (isReadOnly) { return; } - const shouldShowSettlementButton = props.iouType === CONST.IOU.TYPE.SEND; - const shouldDisableButton = selectedParticipants.length === 0 || shouldDisplayMerchantError; + const shouldShowSettlementButton = iouType === CONST.IOU.TYPE.SEND; + const shouldDisableButton = selectedParticipantsMemo.length === 0 || shouldDisplayMerchantError; const button = shouldShowSettlementButton ? ( confirm(value)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} @@ -539,7 +536,7 @@ function MoneyRequestConfirmationList({ return ( <> - {!_.isEmpty(formError) && ( + {formError && ( ); }, [ - props.isReadOnly, - props.iouType, - props.bankAccountRoute, - props.iouCurrencyCode, - props.policyID, - selectedParticipants.length, + isReadOnly, + iouType, + selectedParticipantsMemo.length, shouldDisplayMerchantError, confirm, + bankAccountRoute, + iouCurrencyCode, + policyID, splitOrRequestOptions, formError, styles.ph1, @@ -565,81 +562,80 @@ function MoneyRequestConfirmationList({ translate, ]); - const {image: receiptImage, thumbnail: receiptThumbnail} = - props.receiptPath && props.receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction, props.receiptPath, props.receiptFilename) : {}; + const receiptData = ReceiptUtils.getThumbnailAndImageURIs(transaction, receiptPath, receiptFilename); return ( - {props.isDistanceRequest && ( + {isDistanceRequest && ( - + )} - {(receiptImage || receiptThumbnail) && ( + {(receiptData.image || receiptData.thumbnail) && ( )} - {props.shouldShowSmartScanFields && ( + {shouldShowSmartScanFields && ( { - if (props.isDistanceRequest) { + if (isDistanceRequest) { return; } - if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.AMOUNT)); + if (isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.AMOUNT)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_AMOUNT.getRoute(props.iouType, props.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_AMOUNT.getRoute(iouType, reportID)); }} style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? translate('common.error.enterAmount') : ''} /> )} { - if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.DESCRIPTION)); + if (isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.DESCRIPTION)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_DESCRIPTION.getRoute(props.iouType, props.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_DESCRIPTION.getRoute(iouType, reportID)); }} - style={[styles.moneyRequestMenuItem]} + style={styles.moneyRequestMenuItem} titleStyle={styles.flex1} disabled={didConfirm} - interactive={!props.isReadOnly} + interactive={!isReadOnly} numberOfLinesTitle={2} /> {!shouldShowAllFields && ( @@ -652,137 +648,133 @@ function MoneyRequestConfirmationList({ <> {shouldShowDate && ( { - if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.DATE)); + if (isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.DATE)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(props.iouType, props.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(iouType, reportID)); }} disabled={didConfirm} - interactive={!props.isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + interactive={!isReadOnly} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> )} - {props.isDistanceRequest && ( + {isDistanceRequest && ( Navigation.navigate(ROUTES.MONEY_REQUEST_DISTANCE.getRoute(props.iouType, props.reportID))} + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_DISTANCE.getRoute(iouType, reportID))} disabled={didConfirm || !isTypeRequest} - interactive={!props.isReadOnly} + interactive={!isReadOnly} /> )} {shouldShowMerchant && ( { - if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.MERCHANT)); + if (isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.MERCHANT)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_MERCHANT.getRoute(props.iouType, props.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_MERCHANT.getRoute(iouType, reportID)); }} disabled={didConfirm} - interactive={!props.isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + interactive={!isReadOnly} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayMerchantError || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction)) ? translate('common.error.enterMerchant') : ''} /> )} {shouldShowCategories && ( { - if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.CATEGORY)); + if (isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.CATEGORY)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_CATEGORY.getRoute(props.iouType, props.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_CATEGORY.getRoute(iouType, reportID)); }} - style={[styles.moneyRequestMenuItem]} + style={styles.moneyRequestMenuItem} titleStyle={styles.flex1} disabled={didConfirm} - interactive={!props.isReadOnly} - rightLabel={canUseViolations && Boolean(props.policy.requiresCategory) ? translate('common.required') : ''} + interactive={!isReadOnly} + rightLabel={canUseViolations && Boolean(policy?.requiresCategory) ? translate('common.required') : ''} /> )} {shouldShowTags && ( { - if (props.isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.TAG)); + if (isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.TAG)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(props.iouType, props.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(iouType, reportID)); }} - style={[styles.moneyRequestMenuItem]} + style={styles.moneyRequestMenuItem} disabled={didConfirm} - interactive={!props.isReadOnly} - rightLabel={canUseViolations && Boolean(props.policy.requiresTag) ? translate('common.required') : ''} + interactive={!isReadOnly} + rightLabel={canUseViolations && Boolean(policy?.requiresTag) ? translate('common.required') : ''} /> )} {shouldShowTax && ( - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(props.iouType, props.transaction.transactionID, props.reportID, Navigation.getActiveRouteWithoutParams()), - ) + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) } disabled={didConfirm} - interactive={!props.isReadOnly} + interactive={!isReadOnly} /> )} {shouldShowTax && ( - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(props.iouType, props.transaction.transactionID, props.reportID, Navigation.getActiveRouteWithoutParams()), - ) + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) } disabled={didConfirm} - interactive={!props.isReadOnly} + interactive={!isReadOnly} /> )} {shouldShowBillable && ( - {translate('common.billable')} + {translate('common.billable')} )} @@ -792,13 +784,10 @@ function MoneyRequestConfirmationList({ ); } -MoneyRequestConfirmationList.propTypes = propTypes; -MoneyRequestConfirmationList.defaultProps = defaultProps; MoneyRequestConfirmationList.displayName = 'MoneyRequestConfirmationList'; export default compose( - withCurrentUserPersonalDetails, - withOnyx({ + withOnyx({ session: { key: ONYXKEYS.SESSION, }, @@ -825,4 +814,5 @@ export default compose( key: ONYXKEYS.IOU, }, }), + withCurrentUserPersonalDetails, )(MoneyRequestConfirmationList); diff --git a/src/components/TextInput/BaseTextInput/types.ts b/src/components/TextInput/BaseTextInput/types.ts index 21875d4dcc64..5564f3919542 100644 --- a/src/components/TextInput/BaseTextInput/types.ts +++ b/src/components/TextInput/BaseTextInput/types.ts @@ -111,7 +111,7 @@ type CustomBaseTextInputProps = { autoCompleteType?: string; }; -type BaseTextInputRef = ForwardedRef>>; +type BaseTextInputRef = ForwardedRef>>; type BaseTextInputProps = CustomBaseTextInputProps & TextInputProps; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0d7658adf180..a90a1f10a2f4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4534,4 +4534,4 @@ export { shouldDisableThread, }; -export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport}; +export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport, Participant}; diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 2cd686c115b4..63091d983840 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -10,6 +10,12 @@ type Rate = { currency: string; }; +type MileageRate = { + unit: Unit; + rate: number; + currency: string; +}; + type CustomUnit = { customUnitID?: string; name?: string; @@ -79,8 +85,13 @@ type Policy = { /** The employee list of the policy */ employeeList?: []; + + /** Whether tax tracking enabled for policy */ + isTaxTrackingEnabled?: boolean; + + disabledFields?: Record; }; export default Policy; -export type {Unit}; +export type {Unit, MileageRate}; From eb8677e5005772586627d0787b9d237f21ca018a Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 10 Jan 2024 12:21:16 +0700 Subject: [PATCH 019/213] create getUserDetailsTooltipText util --- src/components/MultipleAvatars.tsx | 18 +++++++++--------- src/libs/OptionsListUtils.js | 2 +- src/libs/ReportUtils.ts | 8 ++++++++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index a6f34cd459fc..82480ed0a4da 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -1,11 +1,10 @@ import React, {memo, useMemo} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; -import {View} from 'react-native'; -import type {ValueOf} from 'type-fest'; +import {StyleProp, View, ViewStyle} from 'react-native'; +import {ValueOf} from 'type-fest'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {AvatarSource} from '@libs/UserUtils'; +import {AvatarSource} from '@libs/UserUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {Icon} from '@src/types/onyx/OnyxCommon'; @@ -13,6 +12,7 @@ import Avatar from './Avatar'; import Text from './Text'; import Tooltip from './Tooltip'; import UserDetailsTooltip from './UserDetailsTooltip'; +import * as ReportUtils from '@libs/ReportUtils'; type MultipleAvatarsProps = { /** Array of avatar URLs or icons */ @@ -143,7 +143,7 @@ function MultipleAvatars({ if (icons.length === 1 && !shouldStackHorizontally) { return ( ( maxAvatarsInRow && ( Number(icon.id)))} > {icons.length === 2 ? ( getDisplayNameForParticipant(accountIDs)).join(', '); +} + /** * For a deleted parent report action within a chat report, * let us return the appropriate display message @@ -4533,6 +4540,7 @@ export { shouldAutoFocusOnKeyPress, shouldDisplayThreadReplies, shouldDisableThread, + getUserDetailsTooltipText }; export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport, OptimisticCreatedReportAction}; From e0dc2e94a3912c965421e7304a8ec483373106ca Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 10 Jan 2024 08:07:25 +0100 Subject: [PATCH 020/213] ref: wip --- src/components/MoneyRequestConfirmationList.tsx | 5 +++-- src/types/onyx/PolicyTag.ts | 1 + src/types/onyx/Transaction.ts | 12 ++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 87c6db42c3b5..741118167a26 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -240,7 +240,7 @@ function MoneyRequestConfirmationList({ ? translate('common.tbd') : CurrencyUtils.convertToDisplayString( shouldCalculateDistanceAmount - ? DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate?.rate) + ? DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate?.rate ?? 0) : iouAmount, isDistanceRequest ? mileageRate?.currency : iouCurrencyCode, ); @@ -248,6 +248,7 @@ function MoneyRequestConfirmationList({ const defaultTaxKey = policyTaxRates.defaultExternalID; const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const taxRateTitle = transaction.taxRate?.text || defaultTaxName; const isFocused = useIsFocused(); @@ -347,7 +348,7 @@ function MoneyRequestConfirmationList({ })); } - const myIOUAmount = IOUUtils.calculateAmount(selectedParticipantsMemo.length, iouAmount, iouCurrencyCode, true); + const myIOUAmount = IOUUtils.calculateAmount(selectedParticipantsMemo.length, iouAmount, iouCurrencyCode ?? '', true); const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( payeePersonalDetailsMemo, iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', diff --git a/src/types/onyx/PolicyTag.ts b/src/types/onyx/PolicyTag.ts index 58a21dcf4df5..e9b909b1748a 100644 --- a/src/types/onyx/PolicyTag.ts +++ b/src/types/onyx/PolicyTag.ts @@ -8,6 +8,7 @@ type PolicyTag = { /** "General Ledger code" that corresponds to this tag in an accounting system. Similar to an ID. */ // eslint-disable-next-line @typescript-eslint/naming-convention 'GL Code': string; + tags: PolicyTags; }; type PolicyTags = Record; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 8b7e26280305..179f473dc71f 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -3,6 +3,15 @@ import type CONST from '@src/CONST'; import type * as OnyxCommon from './OnyxCommon'; import type RecentWaypoint from './RecentWaypoint'; +type TaxRate = { + text: string; + keyForList: string; + data: { + value: string; + }; + modifiedName?: string; +}; + type Waypoint = { /** The name associated with the address of the waypoint */ name?: string; @@ -18,6 +27,7 @@ type Waypoint = { }; type WaypointCollection = Record; + type Comment = { comment?: string; waypoints?: WaypointCollection; @@ -94,6 +104,8 @@ type Transaction = { /** If the transaction was made in a foreign currency, we send the original amount and currency */ originalAmount?: number; originalCurrency?: string; + taxAmount?: number; + taxRate?: TaxRate; }; export default Transaction; From ebabfc77dd2ed34b17d8add8ef72d9c7e66f5cd1 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 10 Jan 2024 14:42:32 +0700 Subject: [PATCH 021/213] fix lint --- src/components/MultipleAvatars.tsx | 13 ++++++++----- .../BaseUserDetailsTooltip/index.tsx | 4 ++-- src/libs/ReportUtils.ts | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index 82480ed0a4da..5f9575e09bfd 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -1,10 +1,12 @@ import React, {memo, useMemo} from 'react'; -import {StyleProp, View, ViewStyle} from 'react-native'; -import {ValueOf} from 'type-fest'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {AvatarSource} from '@libs/UserUtils'; +import * as ReportUtils from '@libs/ReportUtils'; +import type {AvatarSource} from '@libs/UserUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {Icon} from '@src/types/onyx/OnyxCommon'; @@ -12,7 +14,6 @@ import Avatar from './Avatar'; import Text from './Text'; import Tooltip from './Tooltip'; import UserDetailsTooltip from './UserDetailsTooltip'; -import * as ReportUtils from '@libs/ReportUtils'; type MultipleAvatarsProps = { /** Array of avatar URLs or icons */ @@ -214,7 +215,9 @@ function MultipleAvatars({ {avatars.length > maxAvatarsInRow && ( Number(icon.id)))} + text={ReportUtils.getUserDetailsTooltipText( + icons.slice(avatarRows.length * maxAvatarsInRow - 1, avatarRows.length * maxAvatarsInRow + 9).map((icon) => Number(icon.id)), + )} > getDisplayNameForParticipant(accountIDs)).join(', '); + return accountIDs.map((accountID) => getDisplayNameForParticipant(accountID)).join(', '); } /** @@ -4540,7 +4540,7 @@ export { shouldAutoFocusOnKeyPress, shouldDisplayThreadReplies, shouldDisableThread, - getUserDetailsTooltipText + getUserDetailsTooltipText, }; export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport, OptimisticCreatedReportAction}; From 83b98ea8d1d4749ea7246faff24686ed7e874839 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 10 Jan 2024 15:24:06 +0700 Subject: [PATCH 022/213] fix ts lint --- src/components/MultipleAvatars.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index 5f9575e09bfd..48a9bd77c09e 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -144,7 +144,7 @@ function MultipleAvatars({ if (icons.length === 1 && !shouldStackHorizontally) { return ( ( {icons.length === 2 ? ( Date: Wed, 10 Jan 2024 16:29:28 +0100 Subject: [PATCH 023/213] fix: wip --- src/ONYXKEYS.ts | 1 + src/components/MenuItem.tsx | 2 +- .../MoneyRequestConfirmationList.tsx | 108 +++++++++--------- src/libs/ReceiptUtils.ts | 5 +- src/libs/TransactionUtils.ts | 19 +-- src/types/onyx/Policy.ts | 4 + src/types/onyx/PolicyTaxRates.ts | 4 + src/types/onyx/index.ts | 4 + 8 files changed, 83 insertions(+), 64 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 89ddbdc06883..7fb29f348a44 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -439,6 +439,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.POLICY_DRAFTS]: OnyxTypes.Policy; [ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategory; [ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTags; + [ONYXKEYS.COLLECTION.POLICY_TAX_RATE]: OnyxTypes.PolicyTaxRate; [ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers; [ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories; diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index ce44db72598a..9b35a0963b14 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -204,7 +204,7 @@ type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { shouldBlockSelection?: boolean; /** Whether should render title as HTML or as Text */ - shouldParseTitle?: false; + shouldParseTitle?: boolean; /** Should check anonymous user in onPress function */ shouldCheckActionAllowedOnPress?: boolean; diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 741118167a26..b0a9ce1d7a91 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -11,7 +11,6 @@ import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; @@ -46,13 +45,12 @@ import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; type MoneyRequestConfirmationListOnyxProps = { iou: OnyxEntry; - policyTaxRates: any; - session: OnyxTypes.Session; - transaction: OnyxTypes.Transaction; + policyTaxRates: OnyxEntry; + session: OnyxEntry; mileageRate: OnyxEntry; - policyCategories: OnyxEntry; + policyCategories: OnyxEntry; policyTags: OnyxEntry; - policy?: OnyxEntry; + policy: OnyxEntry; }; type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & @@ -150,6 +148,8 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & hasSmartScanFailed?: boolean; reportActionID?: string; + + transaction: OnyxTypes.Transaction; }; function MoneyRequestConfirmationList({ @@ -210,7 +210,7 @@ function MoneyRequestConfirmationList({ const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories))); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -244,12 +244,12 @@ function MoneyRequestConfirmationList({ : iouAmount, isDistanceRequest ? mileageRate?.currency : iouCurrencyCode, ); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); - const defaultTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; + const defaultTaxKey = policyTaxRates?.defaultExternalID; + const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) ?? ''; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const taxRateTitle = transaction.taxRate?.text || defaultTaxName; + const taxRateTitle = transaction?.taxRate?.text || defaultTaxName; const isFocused = useIsFocused(); const [formError, setFormError] = useState(null); @@ -286,7 +286,7 @@ function MoneyRequestConfirmationList({ return; } - const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate.rate); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate?.rate); IOU.setMoneyRequestAmount(amount); }, [shouldCalculateDistanceAmount, distance, mileageRate?.rate, mileageRate?.unit]); @@ -398,8 +398,10 @@ function MoneyRequestConfirmationList({ if (!hasMultipleParticipants) { return []; } - return [...selectedParticipantsMemo, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetailsMemo)]; - }, [selectedParticipantsMemo, hasMultipleParticipants, payeePersonalDetailsMemo]); + // TODO: check if this is needed + const myIOUAmount = IOUUtils.calculateAmount(selectedParticipantsMemo.length, iouAmount, iouCurrencyCode ?? '', true); + return [...selectedParticipantsMemo, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetailsMemo, String(myIOUAmount))]; + }, [hasMultipleParticipants, selectedParticipantsMemo, iouAmount, iouCurrencyCode, payeePersonalDetailsMemo]); useEffect(() => { if (!isDistanceRequest) { @@ -428,14 +430,14 @@ function MoneyRequestConfirmationList({ } onSelectParticipant(option); }, - [session.accountID, onSelectParticipant], + [session?.accountID, onSelectParticipant], ); /** * Navigate to report details or profile of selected user */ - const navigateToReportOrUserDetail = (option) => { - if (option.accountID) { + const navigateToReportOrUserDetail = (option: Participant | OnyxTypes.Report) => { + if ('accountID' in option) { const activeRoute = Navigation.getActiveRouteWithoutParams(); Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, activeRoute)); @@ -505,6 +507,7 @@ function MoneyRequestConfirmationList({ const button = shouldShowSettlementButton ? ( - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ) } disabled={didConfirm} interactive={!isReadOnly} @@ -758,11 +764,13 @@ function MoneyRequestConfirmationList({ - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ) } disabled={didConfirm} interactive={!isReadOnly} @@ -787,33 +795,29 @@ function MoneyRequestConfirmationList({ MoneyRequestConfirmationList.displayName = 'MoneyRequestConfirmationList'; -export default compose( - withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - policyCategories: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - }, - policyTags: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - }, - mileageRate: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - selector: DistanceRequestUtils.getDefaultMileageRate, - }, - splitTransactionDraft: { - key: ({transactionID}) => `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, - }, - policy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, - policyTaxRates: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, - }, - iou: { - key: ONYXKEYS.IOU, - }, - }), - withCurrentUserPersonalDetails, -)(MoneyRequestConfirmationList); +const MoneyRequestConfirmationListWithCurrentUserPersonalDetails = withCurrentUserPersonalDetails(MoneyRequestConfirmationList); + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, + policyCategories: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + }, + policyTags: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + }, + mileageRate: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: DistanceRequestUtils.getDefaultMileageRate, + }, + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + }, + policyTaxRates: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, + }, + iou: { + key: ONYXKEYS.IOU, + }, +})(MoneyRequestConfirmationListWithCurrentUserPersonalDetails); diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index bcba68a3a0bd..7858c3d7277d 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -1,5 +1,6 @@ import Str from 'expensify-common/lib/str'; import type {ImageSourcePropType} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import ReceiptDoc from '@assets/images/receipt-doc.png'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; import ReceiptHTML from '@assets/images/receipt-html.png'; @@ -28,7 +29,7 @@ type FileNameAndExtension = { * @param receiptPath * @param receiptFileName */ -function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { +function getThumbnailAndImageURIs(transaction: OnyxEntry, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg const path = transaction?.receipt?.source ?? receiptPath ?? ''; // filename of uploaded image or last part of remote URI @@ -39,7 +40,7 @@ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string if (!Object.hasOwn(transaction?.pendingFields ?? {}, 'waypoints')) { if (hasEReceipt) { - return {thumbnail: null, image: ROUTES.ERECEIPT.getRoute(transaction.transactionID), transaction}; + return {thumbnail: null, image: ROUTES.ERECEIPT.getRoute(transaction?.transactionID), transaction}; } // For local files, we won't have a thumbnail yet diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index c34a6753c1d5..48a7d4065700 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -139,23 +139,24 @@ function hasReceipt(transaction: Transaction | undefined | null): boolean { return !!transaction?.receipt?.state || hasEReceipt(transaction); } -function isMerchantMissing(transaction: Transaction) { - const isMerchantEmpty = transaction.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction.merchant === ''; +function isMerchantMissing(transaction: OnyxEntry) { + const isMerchantEmpty = transaction?.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction?.merchant === ''; - const isModifiedMerchantEmpty = !transaction.modifiedMerchant || transaction.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction.modifiedMerchant === ''; + const isModifiedMerchantEmpty = + !transaction?.modifiedMerchant || transaction?.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction?.modifiedMerchant === ''; return isMerchantEmpty && isModifiedMerchantEmpty; } -function isAmountMissing(transaction: Transaction) { - return transaction.amount === 0 && (!transaction.modifiedAmount || transaction.modifiedAmount === 0); +function isAmountMissing(transaction: OnyxEntry) { + return transaction?.amount === 0 && (!transaction?.modifiedAmount || transaction?.modifiedAmount === 0); } -function isCreatedMissing(transaction: Transaction) { - return transaction.created === '' && (!transaction.created || transaction.modifiedCreated === ''); +function isCreatedMissing(transaction: OnyxEntry) { + return transaction?.created === '' && (!transaction?.created || transaction?.modifiedCreated === ''); } -function areRequiredFieldsEmpty(transaction: Transaction): boolean { +function areRequiredFieldsEmpty(transaction: OnyxEntry): boolean { const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`] ?? null; const isFromExpenseReport = parentReport?.type === CONST.REPORT.TYPE.EXPENSE; return (isFromExpenseReport && isMerchantMissing(transaction)) || isAmountMissing(transaction) || isCreatedMissing(transaction); @@ -434,7 +435,7 @@ function hasMissingSmartscanFields(transaction: OnyxEntry): boolean /** * Check if the transaction has a defined route */ -function hasRoute(transaction: Transaction): boolean { +function hasRoute(transaction: OnyxEntry): boolean { return !!transaction?.routes?.route0?.geometry?.coordinates; } diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 63091d983840..af65dd57607f 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -90,6 +90,10 @@ type Policy = { isTaxTrackingEnabled?: boolean; disabledFields?: Record; + + requiresCategory?: boolean; + + requiresTag?: boolean; }; export default Policy; diff --git a/src/types/onyx/PolicyTaxRates.ts b/src/types/onyx/PolicyTaxRates.ts index d549b620f51f..492cf37a3181 100644 --- a/src/types/onyx/PolicyTaxRates.ts +++ b/src/types/onyx/PolicyTaxRates.ts @@ -7,6 +7,10 @@ type PolicyTaxRate = { /** Whether the tax is disabled */ isDisabled?: boolean; + + defaultExternalID: string; + + taxes: PolicyTaxRates; }; type PolicyTaxRates = Record; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 7bd9c321be5e..2ef702226384 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -32,6 +32,8 @@ import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type PolicyReportField from './PolicyReportField'; import type {PolicyTag, PolicyTags} from './PolicyTag'; +import type {PolicyTaxRates} from './PolicyTaxRates'; +import type PolicyTaxRate from './PolicyTaxRates'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; @@ -137,4 +139,6 @@ export type { ReportUserIsTyping, PolicyReportField, RecentlyUsedReportFields, + PolicyTaxRate, + PolicyTaxRates, }; From 4ab2ec1000b86078ea558e2d8fae6b71b6c4d444 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 11 Jan 2024 17:12:11 +0700 Subject: [PATCH 024/213] Change logic page not found in profile page --- src/pages/ProfilePage.js | 239 ++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 126 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index c0c782f176ca..590a4635f9e5 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -8,12 +8,11 @@ import _ from 'underscore'; import AttachmentModal from '@components/AttachmentModal'; import AutoUpdateTime from '@components/AutoUpdateTime'; import Avatar from '@components/Avatar'; -import BlockingView from '@components/BlockingViews/BlockingView'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import CommunicationsLink from '@components/CommunicationsLink'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; -import * as Illustrations from '@components/Icon/Illustrations'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -30,7 +29,6 @@ import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ReportUtils from '@libs/ReportUtils'; import * as UserUtils from '@libs/UserUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; -import variables from '@styles/variables'; import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Report from '@userActions/Report'; import * as Session from '@userActions/Session'; @@ -124,9 +122,6 @@ function ProfilePage(props) { const hasMinimumDetails = !_.isEmpty(details.avatar); const isLoading = lodashGet(details, 'isLoading', false) || _.isEmpty(details); - // If the API returns an error for some reason there won't be any details and isLoading will get set to false, so we want to show a blocking screen - const shouldShowBlockingView = !hasMinimumDetails && !isLoading; - const statusEmojiCode = lodashGet(details, 'status.emojiCode', ''); const statusText = lodashGet(details, 'status.text', ''); const hasStatus = !!statusEmojiCode; @@ -146,132 +141,124 @@ function ProfilePage(props) { return ( - Navigation.goBack(navigateBackTo)} - /> - - {hasMinimumDetails && ( - - - - {({show}) => ( - - - - - - )} - - {Boolean(displayName) && ( - + Navigation.goBack(navigateBackTo)} + /> + + {hasMinimumDetails && ( + + + - {displayName} - - )} - {hasStatus && ( - + {({show}) => ( + + + + + + )} + + {Boolean(displayName) && ( - {props.translate('statusPage.status')} + {displayName} - {statusContent} - - )} + )} + {hasStatus && ( + + + {props.translate('statusPage.status')} + + {statusContent} + + )} - {login ? ( - - - {props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} - - - - {isSMSLogin ? props.formatPhoneNumber(phoneNumber) : login} - - - - ) : null} - {pronouns ? ( - - - {props.translate('profilePage.preferredPronouns')} - - {pronouns} - - ) : null} - {shouldShowLocalTime && } - - {shouldShowNotificationPreference && ( - Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(props.report.reportID))} - wrapperStyle={[styles.mtn6, styles.mb5]} - /> - )} - {!isCurrentUser && !Session.isAnonymousUser() && ( - Report.navigateToAndOpenReportWithAccountIDs([accountID])} - wrapperStyle={styles.breakAll} - shouldShowRightIcon - /> - )} - {!_.isEmpty(props.report) && ( - ReportUtils.navigateToPrivateNotes(props.report, props.session)} - wrapperStyle={styles.breakAll} - shouldShowRightIcon - brickRoadIndicator={Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} - /> - )} - - )} - {!hasMinimumDetails && isLoading && } - {shouldShowBlockingView && ( - - )} - + {login ? ( + + + {props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} + + + + {isSMSLogin ? props.formatPhoneNumber(phoneNumber) : login} + + + + ) : null} + {pronouns ? ( + + + {props.translate('profilePage.preferredPronouns')} + + {pronouns} + + ) : null} + {shouldShowLocalTime && } + + {shouldShowNotificationPreference && ( + Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(props.report.reportID))} + wrapperStyle={[styles.mtn6, styles.mb5]} + /> + )} + {!isCurrentUser && !Session.isAnonymousUser() && ( + Report.navigateToAndOpenReportWithAccountIDs([accountID])} + wrapperStyle={styles.breakAll} + shouldShowRightIcon + /> + )} + {!_.isEmpty(props.report) && ( + ReportUtils.navigateToPrivateNotes(props.report, props.session)} + wrapperStyle={styles.breakAll} + shouldShowRightIcon + brickRoadIndicator={Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + /> + )} + + )} + {!hasMinimumDetails && isLoading && } + + ); } From ef6ae5f2735ca7cce0441454828f70b3be5210a3 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 11 Jan 2024 15:30:18 +0100 Subject: [PATCH 025/213] Update policyCategories type in MoneyRequestConfirmationList --- src/ONYXKEYS.ts | 2 +- .../MoneyRequestConfirmationList.tsx | 60 +++++++++---------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 45f70d1864bc..f5e8bba63361 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -437,7 +437,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.DOWNLOAD]: OnyxTypes.Download; [ONYXKEYS.COLLECTION.POLICY]: OnyxTypes.Policy; [ONYXKEYS.COLLECTION.POLICY_DRAFTS]: OnyxTypes.Policy; - [ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategory; + [ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategories; [ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTags; [ONYXKEYS.COLLECTION.POLICY_TAX_RATE]: OnyxTypes.PolicyTaxRate; [ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers; diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index b0a9ce1d7a91..9c46144d3a56 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -46,9 +46,8 @@ import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; type MoneyRequestConfirmationListOnyxProps = { iou: OnyxEntry; policyTaxRates: OnyxEntry; - session: OnyxEntry; mileageRate: OnyxEntry; - policyCategories: OnyxEntry; + policyCategories: OnyxEntry; policyTags: OnyxEntry; policy: OnyxEntry; }; @@ -210,7 +209,8 @@ function MoneyRequestConfirmationList({ const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories))); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); + // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -286,7 +286,7 @@ function MoneyRequestConfirmationList({ return; } - const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate?.rate); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, mileageRate?.rate ?? 0); IOU.setMoneyRequestAmount(amount); }, [shouldCalculateDistanceAmount, distance, mileageRate?.rate, mileageRate?.unit]); @@ -299,7 +299,8 @@ function MoneyRequestConfirmationList({ return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( participantsList, calculatedIouAmount > 0 ? CurrencyUtils.convertToDisplayString(calculatedIouAmount, iouCurrencyCode) : '', - ); + // TODO: Remove assertion after OptionsListUtils will be migrated + ) as Participant[]; }, [iouAmount, iouCurrencyCode], ); @@ -795,29 +796,26 @@ function MoneyRequestConfirmationList({ MoneyRequestConfirmationList.displayName = 'MoneyRequestConfirmationList'; -const MoneyRequestConfirmationListWithCurrentUserPersonalDetails = withCurrentUserPersonalDetails(MoneyRequestConfirmationList); - -export default withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - policyCategories: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - }, - policyTags: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - }, - mileageRate: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - selector: DistanceRequestUtils.getDefaultMileageRate, - }, - policy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, - policyTaxRates: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, - }, - iou: { - key: ONYXKEYS.IOU, - }, -})(MoneyRequestConfirmationListWithCurrentUserPersonalDetails); +export default withCurrentUserPersonalDetails( + withOnyx({ + policyCategories: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + }, + policyTags: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + }, + mileageRate: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: DistanceRequestUtils.getDefaultMileageRate, + }, + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + }, + policyTaxRates: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, + }, + iou: { + key: ONYXKEYS.IOU, + }, + })(MoneyRequestConfirmationList), +); From 441e437de6ab31f4b80a4ccf5f4b8ddddfcf47c7 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 15 Jan 2024 08:40:17 +0100 Subject: [PATCH 026/213] fix: wip --- src/components/MoneyRequestConfirmationList.tsx | 2 +- src/components/TextInput/BaseTextInput/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 9c46144d3a56..5e292e310313 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -646,7 +646,7 @@ function MoneyRequestConfirmationList({ /> {!shouldShowAllFields && ( )} diff --git a/src/components/TextInput/BaseTextInput/types.ts b/src/components/TextInput/BaseTextInput/types.ts index 5564f3919542..21875d4dcc64 100644 --- a/src/components/TextInput/BaseTextInput/types.ts +++ b/src/components/TextInput/BaseTextInput/types.ts @@ -111,7 +111,7 @@ type CustomBaseTextInputProps = { autoCompleteType?: string; }; -type BaseTextInputRef = ForwardedRef>>; +type BaseTextInputRef = ForwardedRef>>; type BaseTextInputProps = CustomBaseTextInputProps & TextInputProps; From 814715b85a10ba75bc517946404639a7b96d4939 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 15 Jan 2024 09:02:53 +0100 Subject: [PATCH 027/213] fix: wip --- src/components/MoneyRequestConfirmationList.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 5e292e310313..c66a58625300 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -44,11 +44,22 @@ import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersona import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; type MoneyRequestConfirmationListOnyxProps = { + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: OnyxEntry; + + /** Collection of tax rates attached to a policy */ policyTaxRates: OnyxEntry; + + /** Unit and rate used for if the money request is a distance request */ mileageRate: OnyxEntry; + + /** Collection of categories attached to a policy */ policyCategories: OnyxEntry; + + /** Collection of tags attached to a policy */ policyTags: OnyxEntry; + + /** The policy of root parent report */ policy: OnyxEntry; }; @@ -169,7 +180,7 @@ function MoneyRequestConfirmationList({ receiptPath = '', receiptFilename = '', transactionID = '', - mileageRate = {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'}, + mileageRate, isDistanceRequest = false, isScanRequest = false, shouldShowSmartScanFields = true, @@ -399,7 +410,6 @@ function MoneyRequestConfirmationList({ if (!hasMultipleParticipants) { return []; } - // TODO: check if this is needed const myIOUAmount = IOUUtils.calculateAmount(selectedParticipantsMemo.length, iouAmount, iouCurrencyCode ?? '', true); return [...selectedParticipantsMemo, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetailsMemo, String(myIOUAmount))]; }, [hasMultipleParticipants, selectedParticipantsMemo, iouAmount, iouCurrencyCode, payeePersonalDetailsMemo]); @@ -420,9 +430,6 @@ function MoneyRequestConfirmationList({ IOU.setMoneyRequestMerchant_temporaryForRefactor(transactionID, distanceMerchant); }, [hasRoute, distance, mileageRate?.unit, mileageRate?.rate, mileageRate?.currency, translate, toLocaleDigit, isDistanceRequest, transactionID]); - /** - * @param {Object} option - */ const selectParticipant = useCallback( (option: Participant) => { // Return early if selected option is currently logged in user. From e5921cee015660fa3a5b59b453a9617dbeefa53a Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 17 Jan 2024 10:39:47 +0100 Subject: [PATCH 028/213] fix: resolve comments --- src/components/ConfirmedRoute.tsx | 6 +- .../MoneyRequestConfirmationList.tsx | 65 ++++++++++++------- src/libs/ReportUtils.ts | 5 +- src/types/onyx/OriginalMessage.ts | 4 +- src/types/onyx/Policy.ts | 3 + src/types/onyx/PolicyTaxRates.ts | 2 + 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/components/ConfirmedRoute.tsx b/src/components/ConfirmedRoute.tsx index c01f7c6250f4..8a3b39305dba 100644 --- a/src/components/ConfirmedRoute.tsx +++ b/src/components/ConfirmedRoute.tsx @@ -30,13 +30,13 @@ type ConfirmedRoutePropsOnyxProps = { type ConfirmedRouteProps = ConfirmedRoutePropsOnyxProps & { /** Transaction that stores the distance request data */ - transaction: Transaction; + transaction: Transaction | undefined; }; function ConfirmedRoute({mapboxAccessToken, transaction}: ConfirmedRouteProps) { const {isOffline} = useNetwork(); - const {route0: route} = transaction.routes ?? {}; - const waypoints = transaction.comment?.waypoints ?? {}; + const {route0: route} = transaction?.routes ?? {}; + const waypoints = transaction?.comment?.waypoints ?? {}; const coordinates = route?.geometry?.coordinates ?? []; const theme = useTheme(); const styles = useThemeStyles(); diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index c66a58625300..f1d042b29cad 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -1,6 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; -import {isEmpty} from 'lodash'; import React, {useCallback, useEffect, useMemo, useReducer, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; @@ -29,6 +28,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; +import type {PaymentType} from '@src/types/onyx/OriginalMessage'; import type {MileageRate} from '@src/types/onyx/Policy'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import ConfirmedRoute from './ConfirmedRoute'; @@ -43,6 +43,15 @@ import Text from './Text'; import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; +type Option = Partial; + +type CategorySection = { + title: string | undefined; + shouldShow: boolean; + indexOffset: number; + data: Option[]; +}; + type MoneyRequestConfirmationListOnyxProps = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: OnyxEntry; @@ -69,7 +78,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & onConfirm?: (selectedParticipants: Participant[]) => void; /** Callback to parent modal to send money */ - onSendMoney?: (paymentMethod: ValueOf) => void; + onSendMoney?: (paymentMethod: PaymentType) => void; /** Callback to inform a participant is selected */ onSelectParticipant?: (option: Participant) => void; @@ -155,11 +164,14 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & /** A flag for verifying that the current report is a sub-report of a workspace chat */ isPolicyExpenseChat?: boolean; + /** Whether there is smartscan failed */ hasSmartScanFailed?: boolean; + /** ID of the report action */ reportActionID?: string; - transaction: OnyxTypes.Transaction; + /** Transaction object */ + transaction?: OnyxTypes.Transaction; }; function MoneyRequestConfirmationList({ @@ -171,7 +183,7 @@ function MoneyRequestConfirmationList({ iouTag = '', iouIsBillable = false, onToggleBillable = () => {}, - payeePersonalDetails = null, + payeePersonalDetails, canModifyParticipants = false, isReadOnly = false, bankAccountRoute = '', @@ -245,7 +257,7 @@ function MoneyRequestConfirmationList({ // A flag for showing the billable field const shouldShowBillable = !policy?.disabledFields?.defaultBillable ?? true; - const hasRoute = TransactionUtils.hasRoute(transaction); + const hasRoute = TransactionUtils.hasRoute(transaction ?? null); const isDistanceRequestWithoutRoute = isDistanceRequest && !hasRoute; const formattedAmount = isDistanceRequestWithoutRoute ? translate('common.tbd') @@ -273,7 +285,7 @@ function MoneyRequestConfirmationList({ return false; } - return (!!hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); + return (!!hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)); }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; @@ -310,7 +322,7 @@ function MoneyRequestConfirmationList({ return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( participantsList, calculatedIouAmount > 0 ? CurrencyUtils.convertToDisplayString(calculatedIouAmount, iouCurrencyCode) : '', - // TODO: Remove assertion after OptionsListUtils will be migrated + // TODO: Remove the assertion once OptionsListUtils (https://github.com/Expensify/App/issues/24921) is migrated to TypeScript. ) as Participant[]; }, [iouAmount, iouCurrencyCode], @@ -346,7 +358,7 @@ function MoneyRequestConfirmationList({ const payeePersonalDetailsMemo = useMemo(() => payeePersonalDetails ?? currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); const canModifyParticipantsValue = !isReadOnly && canModifyParticipants && hasMultipleParticipants; - const optionSelectorSections = useMemo(() => { + const optionSelectorSections: CategorySection[] = useMemo(() => { const sections = []; const unselectedParticipants = selectedParticipants.filter((participant) => !participant.selected); if (hasMultipleParticipants) { @@ -445,17 +457,17 @@ function MoneyRequestConfirmationList({ * Navigate to report details or profile of selected user */ const navigateToReportOrUserDetail = (option: Participant | OnyxTypes.Report) => { - if ('accountID' in option) { + if ('accountID' in option && option.accountID) { const activeRoute = Navigation.getActiveRouteWithoutParams(); Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, activeRoute)); - } else if (option.reportID) { + } else if ('reportID' in option && option.reportID) { Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(option.reportID)); } }; const confirm = useCallback( - (paymentMethod: ValueOf) => { + (paymentMethod: PaymentType) => { if (selectedParticipantsMemo.length === 0) { return; } @@ -480,7 +492,7 @@ function MoneyRequestConfirmationList({ return; } - if (isEditingSplitBill && TransactionUtils.areRequiredFieldsEmpty(transaction)) { + if (isEditingSplitBill && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)) { setDidConfirmSplit(true); setFormError('iou.error.genericSmartscanFailureMessage'); return; @@ -540,7 +552,7 @@ function MoneyRequestConfirmationList({ pressOnEnter isDisabled={shouldDisableButton} // eslint-disable-next-line @typescript-eslint/naming-convention - onPress={(_event, value) => confirm(value)} + onPress={(_, value) => confirm(value)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} /> @@ -574,7 +586,7 @@ function MoneyRequestConfirmationList({ translate, ]); - const receiptData = ReceiptUtils.getThumbnailAndImageURIs(transaction, receiptPath, receiptFilename); + const receiptData = ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename); return ( // @ts-expect-error TODO: Remove this once OptionsSelector (https://github.com/Expensify/App/issues/25125) is migrated to TypeScript. @@ -626,11 +638,11 @@ function MoneyRequestConfirmationList({ } Navigation.navigate(ROUTES.MONEY_REQUEST_AMOUNT.getRoute(iouType, reportID)); }} - style={{...styles.moneyRequestMenuItem, ...styles.mt2}} + style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? translate('common.error.enterAmount') : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? translate('common.error.enterAmount') : ''} /> )} )} {isDistanceRequest && ( @@ -707,8 +720,12 @@ function MoneyRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - error={shouldDisplayMerchantError || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction)) ? translate('common.error.enterMerchant') : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + error={ + shouldDisplayMerchantError || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null)) + ? translate('common.error.enterMerchant') + : '' + } /> )} {shouldShowCategories && ( @@ -747,7 +764,7 @@ function MoneyRequestConfirmationList({ style={styles.moneyRequestMenuItem} disabled={didConfirm} interactive={!isReadOnly} - rightLabel={canUseViolations && Boolean(policy?.requiresTag) ? translate('common.required') : ''} + rightLabel={canUseViolations && !!policy?.requiresTag ? translate('common.required') : ''} /> )} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5b3347dbfa3e..ecccb451b17f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,12 +16,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; +import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated, PaymentType} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import type {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -2698,7 +2697,7 @@ function buildOptimisticIOUReportAction( comment: string, participants: Participant[], transactionID: string, - paymentType: DeepValueOf, + paymentType: PaymentType, iouReportID = '', isSettlingUp = false, isSendMoneyFlow = false, diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index f10696ced00f..5549b96acdb2 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -2,6 +2,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; +type PaymentType = DeepValueOf; type ActionName = DeepValueOf; type OriginalMessageActionName = | 'ADDCOMMENT' @@ -43,7 +44,7 @@ type IOUMessage = { lastModified?: string; participantAccountIDs?: number[]; type: ValueOf; - paymentType?: DeepValueOf; + paymentType?: PaymentType; /** Only exists when we are sending money */ IOUDetails?: IOUDetails; }; @@ -280,4 +281,5 @@ export type { OriginalMessageIOU, OriginalMessageCreated, OriginalMessageAddComment, + PaymentType, }; diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index a2182298acdf..19cecf3b9e6d 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -92,10 +92,13 @@ type Policy = { /** Whether tax tracking enabled for policy */ isTaxTrackingEnabled?: boolean; + /** List of fields which should disabled for the policy */ disabledFields?: Record; + /** Whether or not the policy requires categories */ requiresCategory?: boolean; + /** Whether or not the policy requires tags */ requiresTag?: boolean; }; diff --git a/src/types/onyx/PolicyTaxRates.ts b/src/types/onyx/PolicyTaxRates.ts index 492cf37a3181..5709ce6338ab 100644 --- a/src/types/onyx/PolicyTaxRates.ts +++ b/src/types/onyx/PolicyTaxRates.ts @@ -8,8 +8,10 @@ type PolicyTaxRate = { /** Whether the tax is disabled */ isDisabled?: boolean; + /** Default policy tax ID */ defaultExternalID: string; + /** List of tax names and values */ taxes: PolicyTaxRates; }; From 1e8778c4ec1f2fc0b083f2bc0c33fa237ccb88ae Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 17 Jan 2024 16:44:02 +0700 Subject: [PATCH 029/213] fix: status bar style not update --- .../CustomStatusBarAndBackground/index.tsx | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx index f66a0204ac5e..9ca5edeb7ed8 100644 --- a/src/components/CustomStatusBarAndBackground/index.tsx +++ b/src/components/CustomStatusBarAndBackground/index.tsx @@ -3,6 +3,7 @@ import {interpolateColor, runOnJS, useAnimatedReaction, useSharedValue, withDela import useTheme from '@hooks/useTheme'; import {navigationRef} from '@libs/Navigation/Navigation'; import StatusBar from '@libs/StatusBar'; +import {StatusBarStyle} from '@styles/index'; import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackgroundContext'; import updateGlobalBackgroundColor from './updateGlobalBackgroundColor'; import updateStatusBarAppearance from './updateStatusBarAppearance'; @@ -16,7 +17,7 @@ type CustomStatusBarAndBackgroundProps = { function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBackgroundProps) { const {isRootStatusBarDisabled, disableRootStatusBar} = useContext(CustomStatusBarAndBackgroundContext); const theme = useTheme(); - const [statusBarStyle, setStatusBarStyle] = useState(theme.statusBarStyle); + const [statusBarStyle, setStatusBarStyle] = useState(); const isDisabled = !isNested && isRootStatusBarDisabled; @@ -34,6 +35,8 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack }; }, [disableRootStatusBar, isNested]); + const didForceUpdateStatusBarRef = useRef(false); + const prevIsRootStatusBarDisabled = useRef(isRootStatusBarDisabled); const prevStatusBarBackgroundColor = useRef(theme.appBG); const statusBarBackgroundColor = useRef(theme.appBG); const statusBarAnimation = useSharedValue(0); @@ -94,27 +97,40 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack } // Don't update the status bar style if it's the same as the current one, to prevent flashing. - if (newStatusBarStyle !== statusBarStyle) { + // Force update if the root status bar is back on active or it won't overwirte the nested status bar style + if ((!didForceUpdateStatusBarRef.current && prevIsRootStatusBarDisabled.current && !isRootStatusBarDisabled) || newStatusBarStyle !== statusBarStyle) { updateStatusBarAppearance({statusBarStyle: newStatusBarStyle}); setStatusBarStyle(newStatusBarStyle); + + if (prevIsRootStatusBarDisabled.current && !isRootStatusBarDisabled) { + didForceUpdateStatusBarRef.current = true; + } } }, - [statusBarAnimation, statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle], + [isRootStatusBarDisabled, statusBarAnimation, statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle], ); - // Add navigation state listeners to update the status bar every time the route changes - // We have to pass a count as the listener id, because "react-navigation" somehow doesn't remove listeners properly + useEffect(() => { + prevIsRootStatusBarDisabled.current = isRootStatusBarDisabled; + didForceUpdateStatusBarRef.current = false; + }, [isRootStatusBarDisabled]); + useEffect(() => { if (isDisabled) { return; } + // Update status bar when theme changes + updateStatusBarStyle(); + + // Add navigation state listeners to update the status bar every time the route changes + // We have to pass a count as the listener id, because "react-navigation" somehow doesn't remove listeners properly const listenerId = ++listenerCount.current; const listener = () => updateStatusBarStyle(listenerId); navigationRef.addListener('state', listener); return () => navigationRef.removeListener('state', listener); - }, [isDisabled, theme.appBG, updateStatusBarStyle]); + }, [isDisabled, updateStatusBarStyle]); // Update the global background (on web) everytime the theme changes. // The background of the html element needs to be updated, otherwise you will see a big contrast when resizing the window or when the keyboard is open on iOS web. From 84b4eb44595dbe70a5752cb6e9f044a61975af89 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 17 Jan 2024 17:00:55 +0700 Subject: [PATCH 030/213] use usePrevious --- src/components/CustomStatusBarAndBackground/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx index 9ca5edeb7ed8..576a7249fe03 100644 --- a/src/components/CustomStatusBarAndBackground/index.tsx +++ b/src/components/CustomStatusBarAndBackground/index.tsx @@ -7,6 +7,7 @@ import {StatusBarStyle} from '@styles/index'; import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackgroundContext'; import updateGlobalBackgroundColor from './updateGlobalBackgroundColor'; import updateStatusBarAppearance from './updateStatusBarAppearance'; +import usePrevious from '@hooks/usePrevious'; type CustomStatusBarAndBackgroundProps = { /** Whether the CustomStatusBar is nested within another CustomStatusBar. @@ -36,7 +37,7 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack }, [disableRootStatusBar, isNested]); const didForceUpdateStatusBarRef = useRef(false); - const prevIsRootStatusBarDisabled = useRef(isRootStatusBarDisabled); + const prevIsRootStatusBarDisabled = usePrevious(isRootStatusBarDisabled); const prevStatusBarBackgroundColor = useRef(theme.appBG); const statusBarBackgroundColor = useRef(theme.appBG); const statusBarAnimation = useSharedValue(0); @@ -98,11 +99,11 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack // Don't update the status bar style if it's the same as the current one, to prevent flashing. // Force update if the root status bar is back on active or it won't overwirte the nested status bar style - if ((!didForceUpdateStatusBarRef.current && prevIsRootStatusBarDisabled.current && !isRootStatusBarDisabled) || newStatusBarStyle !== statusBarStyle) { + if ((!didForceUpdateStatusBarRef.current && prevIsRootStatusBarDisabled && !isRootStatusBarDisabled) || newStatusBarStyle !== statusBarStyle) { updateStatusBarAppearance({statusBarStyle: newStatusBarStyle}); setStatusBarStyle(newStatusBarStyle); - if (prevIsRootStatusBarDisabled.current && !isRootStatusBarDisabled) { + if (prevIsRootStatusBarDisabled && !isRootStatusBarDisabled) { didForceUpdateStatusBarRef.current = true; } } @@ -111,7 +112,6 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack ); useEffect(() => { - prevIsRootStatusBarDisabled.current = isRootStatusBarDisabled; didForceUpdateStatusBarRef.current = false; }, [isRootStatusBarDisabled]); From acfc8233be9c2382d77a7aa3bd4da6371deca33e Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 17 Jan 2024 17:06:24 +0700 Subject: [PATCH 031/213] fix lint --- src/components/CustomStatusBarAndBackground/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx index 576a7249fe03..fa1ab2362151 100644 --- a/src/components/CustomStatusBarAndBackground/index.tsx +++ b/src/components/CustomStatusBarAndBackground/index.tsx @@ -1,13 +1,13 @@ import React, {useCallback, useContext, useEffect, useRef, useState} from 'react'; import {interpolateColor, runOnJS, useAnimatedReaction, useSharedValue, withDelay, withTiming} from 'react-native-reanimated'; +import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; import {navigationRef} from '@libs/Navigation/Navigation'; import StatusBar from '@libs/StatusBar'; -import {StatusBarStyle} from '@styles/index'; +import type {StatusBarStyle} from '@styles/index'; import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackgroundContext'; import updateGlobalBackgroundColor from './updateGlobalBackgroundColor'; import updateStatusBarAppearance from './updateStatusBarAppearance'; -import usePrevious from '@hooks/usePrevious'; type CustomStatusBarAndBackgroundProps = { /** Whether the CustomStatusBar is nested within another CustomStatusBar. @@ -108,7 +108,7 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack } } }, - [isRootStatusBarDisabled, statusBarAnimation, statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle], + [prevIsRootStatusBarDisabled, isRootStatusBarDisabled, statusBarAnimation, statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle], ); useEffect(() => { From fab9c8e6ed313751ec28fcd8bb15b8aeba0ccc1d Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 17 Jan 2024 15:37:20 +0100 Subject: [PATCH 032/213] fix: wip --- src/libs/actions/EmojiPickerAction.ts | 2 +- src/libs/actions/User.ts | 6 +- ...tionCompose.js => ReportActionCompose.tsx} | 153 ++++++++++-------- 3 files changed, 88 insertions(+), 73 deletions(-) rename src/pages/home/report/ReportActionCompose/{ReportActionCompose.js => ReportActionCompose.tsx} (81%) diff --git a/src/libs/actions/EmojiPickerAction.ts b/src/libs/actions/EmojiPickerAction.ts index 56a5f34c0b8e..3a4da86dcbb9 100644 --- a/src/libs/actions/EmojiPickerAction.ts +++ b/src/libs/actions/EmojiPickerAction.ts @@ -66,7 +66,7 @@ function showEmojiPicker( /** * Hide the Emoji Picker modal. */ -function hideEmojiPicker(isNavigating: boolean) { +function hideEmojiPicker(isNavigating?: boolean) { if (!emojiPickerRef.current) { return; } diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 8e3bd5f2c017..e0f3003ed9e8 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -13,7 +13,7 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {FrequentlyUsedEmoji} from '@src/types/onyx'; +import type {BlockedFromConcierge, FrequentlyUsedEmoji} from '@src/types/onyx'; import type Login from '@src/types/onyx/Login'; import type {OnyxServerUpdate} from '@src/types/onyx/OnyxUpdatesFromServer'; import type OnyxPersonalDetails from '@src/types/onyx/PersonalDetails'; @@ -27,8 +27,6 @@ import * as PersonalDetails from './PersonalDetails'; import * as Report from './Report'; import * as Session from './Session'; -type BlockedFromConciergeNVP = {expiresAt: number}; - let currentUserAccountID = -1; let currentEmail = ''; Onyx.connect({ @@ -445,7 +443,7 @@ function validateSecondaryLogin(contactMethod: string, validateCode: string) { * and if so whether the expiresAt date of a user's ban is before right now * */ -function isBlockedFromConcierge(blockedFromConciergeNVP: OnyxEntry): boolean { +function isBlockedFromConcierge(blockedFromConciergeNVP: OnyxEntry): boolean { if (isEmptyObject(blockedFromConciergeNVP)) { return false; } diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx similarity index 81% rename from src/pages/home/report/ReportActionCompose/ReportActionCompose.js rename to src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index c072666920ae..1f0574f577cb 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -1,21 +1,23 @@ import {PortalHost} from '@gorhom/portal'; import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; +import type {SyntheticEvent} from 'react'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import {View} from 'react-native'; +import {MeasureInWindowOnSuccessCallback, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import {runOnJS, setNativeProps, useAnimatedRef} from 'react-native-reanimated'; -import _ from 'underscore'; import AttachmentModal from '@components/AttachmentModal'; import EmojiPickerButton from '@components/EmojiPicker/EmojiPickerButton'; import ExceededCommentLength from '@components/ExceededCommentLength'; import OfflineIndicator from '@components/OfflineIndicator'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {usePersonalDetails, withNetwork} from '@components/OnyxProvider'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; +import {usePersonalDetails} from '@components/OnyxProvider'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import useDebounce from '@hooks/useDebounce'; import useHandleExceedMaxCommentLength from '@hooks/useHandleExceedMaxCommentLength'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; @@ -26,69 +28,88 @@ import getModalState from '@libs/getModalState'; import * as ReportUtils from '@libs/ReportUtils'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; import ParticipantLocalTime from '@pages/home/report/ParticipantLocalTime'; -import reportActionPropTypes from '@pages/home/report/reportActionPropTypes'; import ReportDropUI from '@pages/home/report/ReportDropUI'; import ReportTypingIndicator from '@pages/home/report/ReportTypingIndicator'; -import reportPropTypes from '@pages/reportPropTypes'; import * as EmojiPickerActions from '@userActions/EmojiPickerAction'; import * as Report from '@userActions/Report'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type * as OnyxTypes from '@src/types/onyx'; +import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import ComposerWithSuggestions from './ComposerWithSuggestions'; import SendButton from './SendButton'; -const propTypes = { +type ComposerRef = { + blur: () => void; + focus: (shouldDelay?: boolean) => void; + replaceSelectionWithText: (text: string, shouldAddTrailSpace: boolean) => void; + prepareCommentAndResetComposer: () => string; + isFocused: () => boolean; +}; + +type SuggestionsRef = { + resetSuggestions: () => void; + onSelectionChange: (event: any) => void; + triggerHotkeyActions: (event: any) => void; + updateShouldShowSuggestionMenuToFalse: () => void; + setShouldBlockSuggestionCalc: (shouldBlock: boolean) => void; + getSuggestions: () => string[]; +}; + +type ReportActionComposeOnyxProps = { + /** The NVP describing a user's block status */ + blockedFromConcierge: OnyxEntry; + + /** Whether the composer input should be shown */ + shouldShowComposeInput: OnyxEntry; +}; + +type ReportActionComposeProps = { /** A method to call when the form is submitted */ - onSubmit: PropTypes.func.isRequired, + onSubmit: (newComment: string | undefined) => void; /** The ID of the report actions will be created for */ - reportID: PropTypes.string.isRequired, + reportID: string; /** Array of report actions for this report */ - reportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)), + reportActions?: OnyxTypes.ReportAction[]; /** The report currently being looked at */ - report: reportPropTypes, + report: OnyxEntry; /** Is composer full size */ - isComposerFullSize: PropTypes.bool, + isComposerFullSize?: boolean; /** Whether user interactions should be disabled */ - disabled: PropTypes.bool, + disabled?: boolean; /** Height of the list which the composer is part of */ - listHeight: PropTypes.number, - - // The NVP describing a user's block status - blockedFromConcierge: PropTypes.shape({ - // The date that the user will be unblocked - expiresAt: PropTypes.string, - }), + listHeight?: number; /** Whether the composer input should be shown */ - shouldShowComposeInput: PropTypes.bool, + shouldShowComposeInput?: boolean; /** The type of action that's pending */ - pendingAction: PropTypes.oneOf(['add', 'update', 'delete']), + pendingAction?: OnyxCommon.PendingAction; /** /** Whetjer the report is ready for display */ - isReportReadyForDisplay: PropTypes.bool, - ...withCurrentUserPersonalDetailsPropTypes, -}; - -const defaultProps = { - report: {}, - blockedFromConcierge: {}, - preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, - isComposerFullSize: false, - pendingAction: null, - shouldShowComposeInput: true, - listHeight: 0, - isReportReadyForDisplay: true, - ...withCurrentUserPersonalDetailsDefaultProps, -}; + isReportReadyForDisplay?: boolean; +} & ReportActionComposeOnyxProps & + WithCurrentUserPersonalDetailsProps; + +// const defaultProps = { +// report: {}, +// blockedFromConcierge: {}, +// preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, +// isComposerFullSize: false, +// pendingAction: null, +// shouldShowComposeInput: true, +// listHeight: 0, +// isReportReadyForDisplay: true, +// ...withCurrentUserPersonalDetailsDefaultProps, +// }; // We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will // prevent auto focus on existing chat for mobile device @@ -101,7 +122,6 @@ function ReportActionCompose({ currentUserPersonalDetails, disabled, isComposerFullSize, - network, onSubmit, pendingAction, report, @@ -110,10 +130,11 @@ function ReportActionCompose({ listHeight, shouldShowComposeInput, isReportReadyForDisplay, -}) { +}: ReportActionComposeProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isMediumScreenWidth, isSmallScreenWidth} = useWindowDimensions(); + const {isOffline} = useNetwork(); const animatedRef = useAnimatedRef(); const actionButtonRef = useRef(null); const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; @@ -122,7 +143,7 @@ function ReportActionCompose({ */ const [isFocused, setIsFocused] = useState(() => { const initialModalState = getModalState(); - return shouldFocusInputOnScreenFocus && shouldShowComposeInput && !initialModalState.isVisible && !initialModalState.willAlertModalBecomeVisible; + return shouldFocusInputOnScreenFocus && shouldShowComposeInput && !initialModalState?.isVisible && !initialModalState?.willAlertModalBecomeVisible; }); const [isFullComposerAvailable, setIsFullComposerAvailable] = useState(isComposerFullSize); @@ -167,11 +188,11 @@ function ReportActionCompose({ */ const {hasExceededMaxCommentLength, validateCommentMaxLength} = useHandleExceedMaxCommentLength(); - const suggestionsRef = useRef(null); - const composerRef = useRef(null); + const suggestionsRef = useRef(null); + const composerRef = useRef(null); const reportParticipantIDs = useMemo( - () => _.without(lodashGet(report, 'participantAccountIDs', []), currentUserPersonalDetails.accountID), + () => report?.participantAccountIDs?.filter((accountID) => accountID !== currentUserPersonalDetails.accountID), [currentUserPersonalDetails.accountID, report], ); @@ -184,7 +205,7 @@ function ReportActionCompose({ // If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions const conciergePlaceholderRandomIndex = useMemo( - () => _.random(translate('reportActionCompose.conciergePlaceholderOptions').length - (isSmallScreenWidth ? 4 : 1)), + () => Math.floor(Math.random() * (translate('reportActionCompose.conciergePlaceholderOptions').length - (isSmallScreenWidth ? 4 : 1) + 1)), // eslint-disable-next-line react-hooks/exhaustive-deps [], ); @@ -203,7 +224,7 @@ function ReportActionCompose({ }, [report, blockedFromConcierge, translate, conciergePlaceholderRandomIndex]); const focus = () => { - if (composerRef === null || composerRef.current === null) { + if (composerRef?.current === null) { return; } composerRef.current.focus(true); @@ -219,9 +240,9 @@ function ReportActionCompose({ isKeyboardVisibleWhenShowingModalRef.current = false; }, []); - const containerRef = useRef(null); + const containerRef = useRef(null); const measureContainer = useCallback( - (callback) => { + (callback: MeasureInWindowOnSuccessCallback) => { if (!containerRef.current) { return; } @@ -234,9 +255,9 @@ function ReportActionCompose({ const onAddActionPressed = useCallback(() => { if (!willBlurTextInputOnTapOutside) { - isKeyboardVisibleWhenShowingModalRef.current = composerRef.current.isFocused(); + isKeyboardVisibleWhenShowingModalRef.current = !!composerRef.current?.isFocused(); } - composerRef.current.blur(); + composerRef.current?.blur(); }, []); const onItemSelected = useCallback(() => { @@ -250,12 +271,9 @@ function ReportActionCompose({ suggestionsRef.current.updateShouldShowSuggestionMenuToFalse(false); }, []); - /** - * @param {Object} file - */ const addAttachment = useCallback( - (file) => { - const newComment = composerRef.current.prepareCommentAndResetComposer(); + (file: File) => { + const newComment = composerRef.current?.prepareCommentAndResetComposer(); Report.addAttachment(reportID, file, newComment); setTextInputShouldClear(false); }, @@ -273,16 +291,14 @@ function ReportActionCompose({ /** * Add a new comment to this chat - * - * @param {SyntheticEvent} [e] */ const submitForm = useCallback( - (e) => { + (e?: SyntheticEvent) => { if (e) { e.preventDefault(); } - const newComment = composerRef.current.prepareCommentAndResetComposer(); + const newComment = composerRef.current?.prepareCommentAndResetComposer(); if (!newComment) { return; } @@ -323,7 +339,7 @@ function ReportActionCompose({ // We are returning a callback here as we want to incoke the method on unmount only useEffect( () => () => { - if (!EmojiPickerActions.isActive(report.reportID)) { + if (!EmojiPickerActions.isActive(report?.reportID ?? '')) { return; } EmojiPickerActions.hideEmojiPicker(); @@ -338,7 +354,7 @@ function ReportActionCompose({ const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); - const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; + const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || !!disabled || hasExceededMaxCommentLength; const handleSendMessage = useCallback(() => { 'worklet'; @@ -355,7 +371,7 @@ function ReportActionCompose({ }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); return ( - + {shouldShowReportRecipientLocalTime && hasReportRecipient && } @@ -376,12 +392,14 @@ function ReportActionCompose({ hasExceededMaxCommentLength && styles.borderColorDanger, ]} > + {/* @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript. */} setIsAttachmentPreviewActive(true)} onModalHide={onAttachmentPreviewClose} > + {/* @ts-expect-error TODO: Remove this once AttachmentModal (https://github.com/Expensify/App/issues/25130) is migrated to TypeScript. */} {({displayFileInModal}) => ( <> composerRef.current.replaceSelectionWithText(...args)} - emojiPickerID={report.reportID} + // @ts-expect-error TODO: Remove this once EmojiPickerButton is migrated to TypeScript. + onEmojiSelected={(...args) => composerRef.current?.replaceSelectionWithText(...args)} + emojiPickerID={report?.reportID} /> )} {!isSmallScreenWidth && } @@ -478,12 +498,9 @@ function ReportActionCompose({ ); } -ReportActionCompose.propTypes = propTypes; -ReportActionCompose.defaultProps = defaultProps; ReportActionCompose.displayName = 'ReportActionCompose'; export default compose( - withNetwork(), withCurrentUserPersonalDetails, withOnyx({ blockedFromConcierge: { From 544e11833abb07788808bf86a02315879867c8da Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 18 Jan 2024 12:35:36 +0800 Subject: [PATCH 033/213] remove promise chaining --- src/libs/actions/IOU.js | 3 +-- src/pages/iou/request/step/IOURequestStepParticipants.js | 9 +-------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 4a5f5bb01bfb..fe173a448793 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -270,10 +270,9 @@ function setMoneyRequestBillable_temporaryForRefactor(transactionID, billable) { /** * @param {String} transactionID * @param {Object[]} participants - * @returns {Promise} */ function setMoneyRequestParticipants_temporaryForRefactor(transactionID, participants) { - return Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); } /** diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 1d35f7b7a421..83f2082bdb63 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -66,14 +66,7 @@ function IOURequestStepParticipants({ newIouType = CONST.IOU.TYPE.REQUEST; } - IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val).then(() => { - if (!newIouType) { - return; - } - // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before - // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. - IOU.updateMoneyRequestTypeParams(routes, newIouType); - }); + IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); numberOfParticipants.current = val.length; // When multiple participants are selected, the reportID is generated at the end of the confirmation step. From b3eca3db124f455cbaf2b7a2702d571ec8250655 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 18 Jan 2024 12:38:40 +0800 Subject: [PATCH 034/213] update route params after participant is updated --- .../step/IOURequestStepParticipants.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 83f2082bdb63..21ed1a76c141 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -45,6 +45,7 @@ function IOURequestStepParticipants({ const headerTitle = isSplitRequest ? translate('iou.split') : translate(TransactionUtils.getHeaderTitleTranslationKey(transaction)); const receiptFilename = lodashGet(transaction, 'filename'); const receiptPath = lodashGet(transaction, 'receipt.source'); + const newIouType = useRef(); // When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow. // This is because until the request is saved, the receipt file is only stored in the browsers memory as a blob:// and if the browser is refreshed, then @@ -53,17 +54,29 @@ function IOURequestStepParticipants({ IOUUtils.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, () => {}, iouRequestType, iouType, transactionID, reportID); }, [receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID]); + const updateRouteParams = useCallback(() => { + IOU.updateMoneyRequestTypeParams(routes, newIouType.current); + }, [routes]); + + useEffect(() => { + if (newIouType.current) { + // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before + // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. + updateRouteParams(); + newIouType.current = null; + } + }, [participants, routes, updateRouteParams]); + const addParticipant = useCallback( (val, isSplit) => { - let newIouType; // It's only possible to switch between REQUEST and SPLIT. // We want to update the IOU type only if it's not updated yet to prevent unnecessary updates. if (isSplit && iouType !== CONST.IOU.TYPE.SPLIT) { - newIouType = CONST.IOU.TYPE.SPLIT; + newIouType.current = CONST.IOU.TYPE.SPLIT; } else if (!isSplit && iouType === CONST.IOU.TYPE.SPLIT) { // Non-split can be either REQUEST or SEND. Instead of checking whether // the current IOU type is not a REQUEST (true for SEND), we check whether the current IOU type is a SPLIT. - newIouType = CONST.IOU.TYPE.REQUEST; + newIouType.current = CONST.IOU.TYPE.REQUEST; } IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); From df2089845783a77b3c5943c41f0cca1bdc2ba2c7 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 18 Jan 2024 12:46:26 +0800 Subject: [PATCH 035/213] update route params immediately if participants are the same --- src/pages/iou/request/step/IOURequestStepParticipants.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 21ed1a76c141..497370ebcfb5 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -79,6 +79,13 @@ function IOURequestStepParticipants({ newIouType.current = CONST.IOU.TYPE.REQUEST; } + // If the Onyx participants has the same items as the selected participants (val), Onyx won't update it + // thus this component won't rerender, so we can immediately update the route params. + if (newIouType.current && _.isEqual(participants, val)) { + updateRouteParams(); + newIouType.current = null; + } + IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); numberOfParticipants.current = val.length; From 5ad0cbacbc654614356e3e5287501a3d6c6a6392 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 18 Jan 2024 13:21:20 +0800 Subject: [PATCH 036/213] replace useNavigationState hook --- src/pages/iou/request/IOURequestStartPage.js | 12 +++++----- .../step/IOURequestStepParticipants.js | 24 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.js index 89b2bfd43393..e6f98ae4dab2 100644 --- a/src/pages/iou/request/IOURequestStartPage.js +++ b/src/pages/iou/request/IOURequestStartPage.js @@ -1,4 +1,4 @@ -import {useNavigationState} from '@react-navigation/native'; +import {useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; @@ -70,7 +70,7 @@ function IOURequestStartPage({ }) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const routes = useNavigationState((state) => state.routes); + const navigation = useNavigation(); const [isDraggingOver, setIsDraggingOver] = useState(false); const tabTitles = { [CONST.IOU.TYPE.REQUEST]: translate('iou.requestMoney'), @@ -114,12 +114,12 @@ function IOURequestStartPage({ return; } if (iouType === CONST.IOU.TYPE.SPLIT) { - IOU.updateMoneyRequestTypeParams(routes, CONST.IOU.TYPE.REQUEST); + IOU.updateMoneyRequestTypeParams(navigation.getState().routes, CONST.IOU.TYPE.REQUEST); } IOU.startMoneyRequest_temporaryForRefactor(reportID, isFromGlobalCreate, newIouType); transactionRequestType.current = newIouType; }, - [previousIOURequestType, reportID, isFromGlobalCreate, iouType, routes], + [previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation], ); if (!transaction.transactionID) { @@ -151,10 +151,10 @@ function IOURequestStartPage({ id={CONST.TAB.IOU_REQUEST_TYPE} selectedTab={selectedTab || CONST.IOU.REQUEST_TYPE.SCAN} onTabSelected={resetIOUTypeIfChanged} - tabBar={({state, navigation, position}) => ( + tabBar={({state, navigation: tabNavigation, position}) => ( )} diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 497370ebcfb5..d11fcc150076 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -1,6 +1,7 @@ -import {useNavigationState} from '@react-navigation/native'; +import {useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef} from 'react'; +import _ from 'underscore'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; @@ -37,7 +38,7 @@ function IOURequestStepParticipants({ transaction: {participants = []}, }) { const {translate} = useLocalize(); - const routes = useNavigationState((state) => state.routes); + const navigation = useNavigation(); const selectedReportID = useRef(reportID); const numberOfParticipants = useRef(participants.length); const iouRequestType = TransactionUtils.getRequestType(transaction); @@ -55,17 +56,18 @@ function IOURequestStepParticipants({ }, [receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID]); const updateRouteParams = useCallback(() => { - IOU.updateMoneyRequestTypeParams(routes, newIouType.current); - }, [routes]); + IOU.updateMoneyRequestTypeParams(navigation.getState().routes, newIouType.current); + }, [navigation]); useEffect(() => { - if (newIouType.current) { - // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before - // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. - updateRouteParams(); - newIouType.current = null; + if (!newIouType.current) { + return; } - }, [participants, routes, updateRouteParams]); + // Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before + // updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI. + updateRouteParams(); + newIouType.current = null; + }, [participants, updateRouteParams]); const addParticipant = useCallback( (val, isSplit) => { @@ -99,7 +101,7 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. selectedReportID.current = lodashGet(val, '[0].reportID', reportID); }, - [reportID, transactionID, iouType, routes], + [reportID, transactionID, iouType, participants, updateRouteParams], ); const goToNextStep = useCallback( From 20b9724989b5a6e59186a8a87e6138db8df6c4e3 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 18 Jan 2024 15:39:48 +0800 Subject: [PATCH 037/213] fix money request tab navigator params are not updated properly --- src/libs/actions/IOU.js | 16 +++++++++++++--- src/pages/iou/request/IOURequestStartPage.js | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index fe173a448793..862f9f43d1b6 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -178,13 +178,23 @@ function clearMoneyRequest(transactionID) { * * @param {Object[]} routes * @param {String} newIouType + * @param {String} tab */ -function updateMoneyRequestTypeParams(routes, newIouType) { +function updateMoneyRequestTypeParams(routes, newIouType, tab) { routes.forEach((route) => { - if (!route.name.startsWith('Money_Request_')) { + if (!route.name.startsWith('Money_Request_') && ![CONST.TAB_REQUEST.DISTANCE, CONST.TAB_REQUEST.MANUAL, CONST.TAB_REQUEST.SCAN].includes(route.name)) { return; } - Navigation.setParams({iouType: newIouType}, route.key); + const newParams = {iouType: newIouType}; + if (route.name === 'Money_Request_Create') { + // Both screen and nested params are needed to properly update the nested tab navigator + newParams.params = {...newParams}; + newParams.screen = tab; + } + Navigation.setParams(newParams, route.key); + + // Recursively update nested money request tab params + updateMoneyRequestTypeParams(lodashGet(route, 'state.routes', []), newIouType, tab); }); } diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.js index e6f98ae4dab2..c057c4d8a6e1 100644 --- a/src/pages/iou/request/IOURequestStartPage.js +++ b/src/pages/iou/request/IOURequestStartPage.js @@ -114,7 +114,7 @@ function IOURequestStartPage({ return; } if (iouType === CONST.IOU.TYPE.SPLIT) { - IOU.updateMoneyRequestTypeParams(navigation.getState().routes, CONST.IOU.TYPE.REQUEST); + IOU.updateMoneyRequestTypeParams(navigation.getState().routes, CONST.IOU.TYPE.REQUEST, newIouType); } IOU.startMoneyRequest_temporaryForRefactor(reportID, isFromGlobalCreate, newIouType); transactionRequestType.current = newIouType; From 25f6646d0486eb0ce45714956e7118c65281ef31 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 18 Jan 2024 10:46:39 +0100 Subject: [PATCH 038/213] fix: finish working on migration --- .../ReportActionCompose.tsx | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 1f0574f577cb..2cdb4e4d3602 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -1,8 +1,8 @@ import {PortalHost} from '@gorhom/portal'; -import lodashGet from 'lodash/get'; import type {SyntheticEvent} from 'react'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import {MeasureInWindowOnSuccessCallback, View} from 'react-native'; +import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; +import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import {runOnJS, setNativeProps, useAnimatedRef} from 'react-native-reanimated'; @@ -21,7 +21,6 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; -import compose from '@libs/compose'; import getDraftComment from '@libs/ComposerUtils/getDraftComment'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getModalState from '@libs/getModalState'; @@ -37,6 +36,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import ComposerWithSuggestions from './ComposerWithSuggestions'; import SendButton from './SendButton'; @@ -51,9 +51,9 @@ type ComposerRef = { type SuggestionsRef = { resetSuggestions: () => void; - onSelectionChange: (event: any) => void; - triggerHotkeyActions: (event: any) => void; - updateShouldShowSuggestionMenuToFalse: () => void; + onSelectionChange: (event: NativeSyntheticEvent) => void; + triggerHotkeyActions: (event: KeyboardEvent) => void; + updateShouldShowSuggestionMenuToFalse: (shouldShowSuggestionMenu: boolean) => void; setShouldBlockSuggestionCalc: (shouldBlock: boolean) => void; getSuggestions: () => string[]; }; @@ -99,18 +99,6 @@ type ReportActionComposeProps = { } & ReportActionComposeOnyxProps & WithCurrentUserPersonalDetailsProps; -// const defaultProps = { -// report: {}, -// blockedFromConcierge: {}, -// preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, -// isComposerFullSize: false, -// pendingAction: null, -// shouldShowComposeInput: true, -// listHeight: 0, -// isReportReadyForDisplay: true, -// ...withCurrentUserPersonalDetailsDefaultProps, -// }; - // We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will // prevent auto focus on existing chat for mobile device const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); @@ -119,17 +107,17 @@ const willBlurTextInputOnTapOutside = willBlurTextInputOnTapOutsideFunc(); function ReportActionCompose({ blockedFromConcierge, - currentUserPersonalDetails, + currentUserPersonalDetails = {}, disabled, - isComposerFullSize, + isComposerFullSize = false, onSubmit, pendingAction, report, reportID, reportActions, - listHeight, - shouldShowComposeInput, - isReportReadyForDisplay, + listHeight = 0, + shouldShowComposeInput = true, + isReportReadyForDisplay = true, }: ReportActionComposeProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -313,7 +301,7 @@ function ReportActionCompose({ isKeyboardVisibleWhenShowingModalRef.current = true; }, []); - const onBlur = useCallback((e) => { + const onBlur = useCallback((e: FocusEvent) => { setIsFocused(false); if (suggestionsRef.current) { suggestionsRef.current.resetSuggestions(); @@ -352,7 +340,7 @@ function ReportActionCompose({ const reportRecipient = personalDetails[reportRecipientAcountIDs[0]]; const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; - const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); + const hasReportRecipient = !isEmptyObject(reportRecipient); const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || !!disabled || hasExceededMaxCommentLength; @@ -403,6 +391,7 @@ function ReportActionCompose({ {({displayFileInModal}) => ( <> { + onDrop={(e: DragEvent) => { if (isAttachmentPreviewActive) { return; } - const data = lodashGet(e, ['dataTransfer', 'items', 0]); + const data = e.dataTransfer?.items[0]; displayFileInModal(data); }} /> @@ -500,14 +489,13 @@ function ReportActionCompose({ ReportActionCompose.displayName = 'ReportActionCompose'; -export default compose( - withCurrentUserPersonalDetails, - withOnyx({ +export default withCurrentUserPersonalDetails( + withOnyx({ blockedFromConcierge: { key: ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE, }, shouldShowComposeInput: { key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, }, - }), -)(ReportActionCompose); + })(ReportActionCompose), +); From 032e9e32f529ab28001a5db554460cbfd3125fa4 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 18 Jan 2024 17:01:26 +0700 Subject: [PATCH 039/213] display correct tooltip for the case subscription --- src/components/MultipleAvatars.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index 48a9bd77c09e..5b639d832dce 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -107,7 +107,6 @@ function MultipleAvatars({ let avatarContainerStyles = StyleUtils.getContainerStyles(size, isInReportAction); const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[size as AvatarSizeToStyles] ?? avatarSizeToStylesMap.default, [size, avatarSizeToStylesMap]); - const tooltipTexts = useMemo(() => (shouldShowTooltip ? icons.map((icon) => icon.name) : ['']), [shouldShowTooltip, icons]); const avatarSize = useMemo(() => { if (isFocusMode) { return CONST.AVATAR_SIZE.MID_SUBSCRIPT; @@ -215,9 +214,13 @@ function MultipleAvatars({ {avatars.length > maxAvatarsInRow && ( Number(icon.id)), - )} + text={ + shouldShowTooltip + ? ReportUtils.getUserDetailsTooltipText( + icons.slice(avatarRows.length * maxAvatarsInRow - 1, avatarRows.length * maxAvatarsInRow + 9).map((icon) => Number(icon.id)), + ) + : '' + } > ) : ( - + Number(icon.id))) : ''}> Date: Thu, 18 Jan 2024 13:40:22 +0100 Subject: [PATCH 040/213] fix: typecheck --- src/components/ButtonWithDropdownMenu.tsx | 4 ++-- src/components/MoneyRequestConfirmationList.tsx | 4 ++-- src/components/PopoverMenu.tsx | 2 +- src/libs/DistanceRequestUtils.ts | 12 +++--------- src/types/onyx/IOU.ts | 2 ++ src/types/onyx/Policy.ts | 2 +- 6 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/components/ButtonWithDropdownMenu.tsx b/src/components/ButtonWithDropdownMenu.tsx index 466c68229a32..fc33c373ad45 100644 --- a/src/components/ButtonWithDropdownMenu.tsx +++ b/src/components/ButtonWithDropdownMenu.tsx @@ -19,7 +19,7 @@ import PopoverMenu from './PopoverMenu'; type DropdownOption = { value: string; text: string; - icon: IconAsset; + icon?: IconAsset; iconWidth?: number; iconHeight?: number; iconDescription?: string; @@ -55,7 +55,7 @@ type ButtonWithDropdownMenuProps = { anchorAlignment?: AnchorAlignment; /* ref for the button */ - buttonRef: RefObject; + buttonRef?: RefObject; }; function ButtonWithDropdownMenu({ diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index f1d042b29cad..3712e928a5e1 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -20,7 +20,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import type {Participant} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; @@ -28,6 +27,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; +import type {Participant} from '@src/types/onyx/IOU'; import type {PaymentType} from '@src/types/onyx/OriginalMessage'; import type {MileageRate} from '@src/types/onyx/Policy'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; @@ -552,7 +552,7 @@ function MoneyRequestConfirmationList({ pressOnEnter isDisabled={shouldDisableButton} // eslint-disable-next-line @typescript-eslint/naming-convention - onPress={(_, value) => confirm(value)} + onPress={(_, value) => confirm(value as PaymentType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} /> diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 17b1a119671a..ae995c899521 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -17,7 +17,7 @@ import Text from './Text'; type PopoverMenuItem = { /** An icon element displayed on the left side */ - icon: IconAsset; + icon?: IconAsset; /** Text label */ text: string; diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index c92e9bfd3f67..6c7444ea12c7 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -1,17 +1,11 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import CONST from '@src/CONST'; -import type {Unit} from '@src/types/onyx/Policy'; +import type {MileageRate, Unit} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; -type DefaultMileageRate = { - rate?: number; - currency?: string; - unit: Unit; -}; - /** * Retrieves the default mileage rate based on a given policy. * @@ -22,7 +16,7 @@ type DefaultMileageRate = { * @returns [currency] - The currency associated with the rate. * @returns [unit] - The unit of measurement for the distance. */ -function getDefaultMileageRate(policy: OnyxEntry): DefaultMileageRate | null { +function getDefaultMileageRate(policy: OnyxEntry): MileageRate | null { if (!policy?.customUnits) { return null; } @@ -39,7 +33,7 @@ function getDefaultMileageRate(policy: OnyxEntry): DefaultMileageRate | return { rate: distanceRate.rate, - currency: distanceRate.currency, + currency: distanceRate.currency ?? 'USD', unit: distanceUnit.attributes.unit, }; } diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index a89b0d4530ef..220af7005c45 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -23,3 +23,5 @@ type IOU = { }; export default IOU; + +export type {Participant}; diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index ee8c69fa3f21..e5681e53dc15 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -19,7 +19,7 @@ type Attributes = { type MileageRate = { unit: Unit; - rate: number; + rate?: number; currency: string; }; From 5e2f31c82fda91671e4c4c1e601a7755335fb67f Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 19 Jan 2024 11:33:28 +0100 Subject: [PATCH 041/213] fix: resolve comments --- .../ReportActionCompose.tsx | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 2cdb4e4d3602..73b025c50df2 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -88,13 +88,10 @@ type ReportActionComposeProps = { /** Height of the list which the composer is part of */ listHeight?: number; - /** Whether the composer input should be shown */ - shouldShowComposeInput?: boolean; - /** The type of action that's pending */ pendingAction?: OnyxCommon.PendingAction; - /** /** Whetjer the report is ready for display */ + /** Whether the report is ready for display */ isReportReadyForDisplay?: boolean; } & ReportActionComposeOnyxProps & WithCurrentUserPersonalDetailsProps; @@ -212,7 +209,7 @@ function ReportActionCompose({ }, [report, blockedFromConcierge, translate, conciergePlaceholderRandomIndex]); const focus = () => { - if (composerRef?.current === null) { + if (composerRef.current === null) { return; } composerRef.current.focus(true); @@ -281,9 +278,9 @@ function ReportActionCompose({ * Add a new comment to this chat */ const submitForm = useCallback( - (e?: SyntheticEvent) => { - if (e) { - e.preventDefault(); + (event?: SyntheticEvent) => { + if (event) { + event.preventDefault(); } const newComment = composerRef.current?.prepareCommentAndResetComposer(); @@ -301,12 +298,12 @@ function ReportActionCompose({ isKeyboardVisibleWhenShowingModalRef.current = true; }, []); - const onBlur = useCallback((e: FocusEvent) => { + const onBlur = useCallback((event: FocusEvent) => { setIsFocused(false); if (suggestionsRef.current) { suggestionsRef.current.resetSuggestions(); } - if (e.relatedTarget && e.relatedTarget === actionButtonRef.current) { + if (event.relatedTarget && event.relatedTarget === actionButtonRef.current) { isKeyboardVisibleWhenShowingModalRef.current = true; } }, []); @@ -444,11 +441,11 @@ function ReportActionCompose({ onValueChange={validateCommentMaxLength} /> { + onDrop={(event: DragEvent) => { if (isAttachmentPreviewActive) { return; } - const data = e.dataTransfer?.items[0]; + const data = event.dataTransfer?.items[0]; displayFileInModal(data); }} /> @@ -459,7 +456,7 @@ function ReportActionCompose({ composerRef.current?.replaceSelectionWithText(...args)} emojiPickerID={report?.reportID} /> From 2a0e3e21c0b61a5f9409137e29986e4d14c93bac Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 19 Jan 2024 15:14:18 +0100 Subject: [PATCH 042/213] ref: move SuggestionMention, Suggestions to TS, fixes to MentionSuggestions and ReportActionCompose --- src/components/MentionSuggestions.tsx | 6 +- .../ReportActionCompose.tsx | 9 +- ...SuggestionEmoji.js => SuggestionEmoji.tsx} | 84 ++++------ ...estionMention.js => SuggestionMention.tsx} | 145 ++++++++---------- .../{Suggestions.js => Suggestions.tsx} | 119 +++++++------- src/types/onyx/OnyxCommon.ts | 2 +- 6 files changed, 159 insertions(+), 206 deletions(-) rename src/pages/home/report/ReportActionCompose/{SuggestionEmoji.js => SuggestionEmoji.tsx} (86%) rename src/pages/home/report/ReportActionCompose/{SuggestionMention.js => SuggestionMention.tsx} (73%) rename src/pages/home/report/ReportActionCompose/{Suggestions.js => Suggestions.tsx} (54%) diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index 459131ecc434..99930f995a3a 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -18,7 +18,7 @@ type Mention = { alternateText: string; /** Email/phone number of the user */ - login: string; + login?: string; /** Array of icons of the user. We use the first element of this array */ icons: Icon[]; @@ -32,7 +32,7 @@ type MentionSuggestionsProps = { mentions: Mention[]; /** Fired when the user selects a mention */ - onSelect: () => void; + onSelect: (highlightedMentionIndex: number) => void; /** Mention prefix that follows the @ sign */ prefix: string; @@ -142,3 +142,5 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe MentionSuggestions.displayName = 'MentionSuggestions'; export default MentionSuggestions; + +export type {Mention}; diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 73b025c50df2..025b7142df0d 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -9,6 +9,7 @@ import {runOnJS, setNativeProps, useAnimatedRef} from 'react-native-reanimated'; import AttachmentModal from '@components/AttachmentModal'; import EmojiPickerButton from '@components/EmojiPicker/EmojiPickerButton'; import ExceededCommentLength from '@components/ExceededCommentLength'; +import type {Mention} from '@components/MentionSuggestions'; import OfflineIndicator from '@components/OfflineIndicator'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxProvider'; @@ -51,11 +52,11 @@ type ComposerRef = { type SuggestionsRef = { resetSuggestions: () => void; - onSelectionChange: (event: NativeSyntheticEvent) => void; + onSelectionChange?: (event: NativeSyntheticEvent) => void; triggerHotkeyActions: (event: KeyboardEvent) => void; - updateShouldShowSuggestionMenuToFalse: (shouldShowSuggestionMenu: boolean) => void; + updateShouldShowSuggestionMenuToFalse: (shouldShowSuggestionMenu?: boolean) => void; setShouldBlockSuggestionCalc: (shouldBlock: boolean) => void; - getSuggestions: () => string[]; + getSuggestions: () => Mention[]; }; type ReportActionComposeOnyxProps = { @@ -496,3 +497,5 @@ export default withCurrentUserPersonalDetails( }, })(ReportActionCompose), ); + +export type {SuggestionsRef}; diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx similarity index 86% rename from src/pages/home/report/ReportActionCompose/SuggestionEmoji.js rename to src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index e35d1e90bd5a..06089b748554 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import React, {ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import EmojiSuggestions from '@components/EmojiSuggestions'; @@ -9,17 +10,15 @@ import * as EmojiUtils from '@libs/EmojiUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import * as SuggestionProps from './suggestionProps'; +// eslint-disable-next-line import/no-cycle +import {SuggestionProps} from './Suggestions'; /** * Check if this piece of string looks like an emoji - * @param {String} str - * @param {Number} pos - * @returns {Boolean} */ -const isEmojiCode = (str, pos) => { +const isEmojiCode = (str: string, pos: number): boolean => { const leftWords = str.slice(0, pos).split(CONST.REGEX.SPECIAL_CHAR_OR_EMOJI); - const leftWord = _.last(leftWords); + const leftWord = leftWords.at(-1) ?? ''; return CONST.REGEX.HAS_COLON_ONLY_AT_THE_BEGINNING.test(leftWord) && leftWord.length > 2; }; @@ -29,38 +28,33 @@ const defaultSuggestionsValues = { shouldShowSuggestionMenu: false, }; -const propTypes = { +type SuggestionEmojiOnyxProps = { /** Preferred skin tone */ - preferredSkinTone: PropTypes.number, - - /** A ref to this component */ - forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), - - /** Function to clear the input */ - resetKeyboardInput: PropTypes.func.isRequired, - - ...SuggestionProps.baseProps, + preferredSkinTone: OnyxEntry; }; -const defaultProps = { - preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, - forwardedRef: null, -}; - -function SuggestionEmoji({ - preferredSkinTone, - value, - setValue, - selection, - setSelection, - updateComment, - isComposerFullSize, - isAutoSuggestionPickerLarge, - forwardedRef, - resetKeyboardInput, - measureParentContainer, - isComposerFocused, -}) { +type SuggestionEmojiProps = { + /** Function to clear the input */ + resetKeyboardInput: () => void; +} & SuggestionEmojiOnyxProps & + SuggestionProps; + +function SuggestionEmoji( + { + preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, + value, + setValue, + selection, + setSelection, + updateComment, + isComposerFullSize, + isAutoSuggestionPickerLarge, + resetKeyboardInput, + measureParentContainer, + isComposerFocused, + }: SuggestionEmojiProps, + ref: ForwardedRef, +) { const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); const isEmojiSuggestionsMenuVisible = !_.isEmpty(suggestionValues.suggestedEmojis) && suggestionValues.shouldShowSuggestionMenu; @@ -214,7 +208,7 @@ function SuggestionEmoji({ }, []); useImperativeHandle( - forwardedRef, + ref, () => ({ resetSuggestions, onSelectionChange, @@ -248,23 +242,11 @@ function SuggestionEmoji({ ); } -SuggestionEmoji.propTypes = propTypes; -SuggestionEmoji.defaultProps = defaultProps; SuggestionEmoji.displayName = 'SuggestionEmoji'; -const SuggestionEmojiWithRef = React.forwardRef((props, ref) => ( - -)); - -SuggestionEmojiWithRef.displayName = 'SuggestionEmojiWithRef'; - -export default withOnyx({ +export default withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, selector: EmojiUtils.getPreferredSkinToneIndex, }, -})(SuggestionEmojiWithRef); +})(forwardRef(SuggestionEmoji)); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx similarity index 73% rename from src/pages/home/report/ReportActionCompose/SuggestionMention.js rename to src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index af3074eec06d..5ce33d916a08 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -1,7 +1,7 @@ -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; -import _ from 'underscore'; +import type {ForwardedRef} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import * as Expensicons from '@components/Icon/Expensicons'; +import type {Mention} from '@components/MentionSuggestions'; import MentionSuggestions from '@components/MentionSuggestions'; import {usePersonalDetails} from '@components/OnyxProvider'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; @@ -11,52 +11,41 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; -import * as SuggestionProps from './suggestionProps'; +import type {PersonalDetailsList} from '@src/types/onyx'; +import type {SuggestionsRef} from './ReportActionCompose'; +import type {SuggestionProps} from './Suggestions'; + +type SuggestionMentionProps = {isAutoSuggestionPickerLarge: boolean} & SuggestionProps; + +type SuggestionValues = { + suggestedMentions: Mention[]; + atSignIndex: number; + shouldShowSuggestionMenu: boolean; + mentionPrefix: string; +}; /** * Check if this piece of string looks like a mention - * @param {String} str - * @returns {Boolean} */ -const isMentionCode = (str) => CONST.REGEX.HAS_AT_MOST_TWO_AT_SIGNS.test(str); +const isMentionCode = (str: string): boolean => CONST.REGEX.HAS_AT_MOST_TWO_AT_SIGNS.test(str); -const defaultSuggestionsValues = { +const defaultSuggestionsValues: SuggestionValues = { suggestedMentions: [], atSignIndex: -1, shouldShowSuggestionMenu: false, mentionPrefix: '', }; -const propTypes = { - /** A ref to this component */ - forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), - - ...SuggestionProps.implementationBaseProps, -}; - -const defaultProps = { - forwardedRef: null, -}; - -function SuggestionMention({ - value, - setValue, - selection, - setSelection, - isComposerFullSize, - updateComment, - composerHeight, - forwardedRef, - isAutoSuggestionPickerLarge, - measureParentContainer, - isComposerFocused, -}) { - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; +function SuggestionMention( + {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused}: SuggestionMentionProps, + ref: ForwardedRef, +) { + const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; const {translate, formatPhoneNumber} = useLocalize(); const previousValue = usePrevious(value); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); - const isMentionSuggestionsMenuVisible = !_.isEmpty(suggestionValues.suggestedMentions) && suggestionValues.shouldShowSuggestionMenu; + const isMentionSuggestionsMenuVisible = !!suggestionValues.suggestedMentions.length && suggestionValues.shouldShowSuggestionMenu; const [highlightedMentionIndex, setHighlightedMentionIndex] = useArrowKeyFocusManager({ isActive: isMentionSuggestionsMenuVisible, @@ -69,10 +58,9 @@ function SuggestionMention({ /** * Replace the code of mention and update selection - * @param {Number} highlightedMentionIndex */ const insertSelectedMention = useCallback( - (highlightedMentionIndexInner) => { + (highlightedMentionIndexInner: number) => { const commentBeforeAtSign = value.slice(0, suggestionValues.atSignIndex); const mentionObject = suggestionValues.suggestedMentions[highlightedMentionIndexInner]; const mentionCode = mentionObject.text === CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT ? CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT : `@${mentionObject.login}`; @@ -100,23 +88,21 @@ function SuggestionMention({ /** * Listens for keyboard shortcuts and applies the action - * - * @param {Object} e */ const triggerHotkeyActions = useCallback( - (e) => { + (event: KeyboardEvent) => { const suggestionsExist = suggestionValues.suggestedMentions.length > 0; - if (((!e.shiftKey && e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) || e.key === CONST.KEYBOARD_SHORTCUTS.TAB.shortcutKey) && suggestionsExist) { - e.preventDefault(); + if (((!event.shiftKey && event.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) || event.key === CONST.KEYBOARD_SHORTCUTS.TAB.shortcutKey) && suggestionsExist) { + event.preventDefault(); if (suggestionValues.suggestedMentions.length > 0) { insertSelectedMention(highlightedMentionIndex); return true; } } - if (e.key === CONST.KEYBOARD_SHORTCUTS.ESCAPE.shortcutKey) { - e.preventDefault(); + if (event.key === CONST.KEYBOARD_SHORTCUTS.ESCAPE.shortcutKey) { + event.preventDefault(); if (suggestionsExist) { resetSuggestions(); @@ -129,7 +115,7 @@ function SuggestionMention({ ); const getMentionOptions = useCallback( - (personalDetailsParam, searchValue = '') => { + (personalDetailsParam: PersonalDetailsList, searchValue = ''): Mention[] => { const suggestions = []; if (CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT.includes(searchValue.toLowerCase())) { @@ -139,15 +125,15 @@ function SuggestionMention({ icons: [ { source: Expensicons.Megaphone, - type: 'avatar', + type: CONST.ICON_TYPE_AVATAR, }, ], }); } - const filteredPersonalDetails = _.filter(_.values(personalDetailsParam), (detail) => { + const filteredPersonalDetails = Object.values(personalDetailsParam ?? {}).filter((detail) => { // If we don't have user's primary login, that member is not known to the current user and hence we do not allow them to be mentioned - if (!detail.login || detail.isOptimisticPersonalDetail) { + if (!detail?.login || detail.isOptimisticPersonalDetail) { return false; } const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(detail); @@ -158,18 +144,29 @@ function SuggestionMention({ return true; }); - const sortedPersonalDetails = _.sortBy(filteredPersonalDetails, (detail) => detail.displayName || detail.login); - _.each(_.first(sortedPersonalDetails, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length), (detail) => { + const sortedPersonalDetails = filteredPersonalDetails.sort((a, b) => { + const nameA = a?.displayName ?? a?.login ?? ''; + const nameB = b?.displayName ?? b?.login ?? ''; + + if (nameA < nameB) { + return -1; + } + if (nameA > nameB) { + return 1; + } + return 0; + }); + sortedPersonalDetails.slice(0, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length).forEach((detail) => { suggestions.push({ text: PersonalDetailsUtils.getDisplayNameOrDefault(detail), - alternateText: formatPhoneNumber(detail.login), - login: detail.login, + alternateText: formatPhoneNumber(detail?.login ?? ''), + login: detail?.login, icons: [ { - name: detail.login, - source: UserUtils.getAvatar(detail.avatar, detail.accountID), - type: 'avatar', - fallbackIcon: detail.fallbackIcon, + name: detail?.login, + source: UserUtils.getAvatar(detail?.avatar, detail?.accountID), + type: CONST.ICON_TYPE_AVATAR, + fallbackIcon: detail?.fallbackIcon, }, ], }); @@ -181,7 +178,7 @@ function SuggestionMention({ ); const calculateMentionSuggestion = useCallback( - (selectionEnd) => { + (selectionEnd: number) => { if (shouldBlockCalc.current || selectionEnd < 1 || !isComposerFocused) { shouldBlockCalc.current = false; resetSuggestions(); @@ -201,11 +198,11 @@ function SuggestionMention({ const leftString = value.substring(0, suggestionEndIndex); const words = leftString.split(CONST.REGEX.SPACE_OR_EMOJI); - const lastWord = _.last(words); + const lastWord = words.at(-1) ?? ''; const secondToLastWord = words[words.length - 3]; let atSignIndex; - let suggestionWord; + let suggestionWord = ''; let prefix; // Detect if the last two words contain a mention (two words are needed to detect a mention with a space in it) @@ -223,7 +220,7 @@ function SuggestionMention({ prefix = lastWord.substring(1); } - const nextState = { + const nextState: Partial = { suggestedMentions: [], atSignIndex, mentionPrefix: prefix, @@ -235,7 +232,7 @@ function SuggestionMention({ const suggestions = getMentionOptions(personalDetails, prefix); nextState.suggestedMentions = suggestions; - nextState.shouldShowSuggestionMenu = !_.isEmpty(suggestions); + nextState.shouldShowSuggestionMenu = !!suggestions.length; } setSuggestionValues((prevState) => ({ @@ -268,20 +265,16 @@ function SuggestionMention({ }, []); const setShouldBlockSuggestionCalc = useCallback( - (shouldBlockSuggestionCalc) => { + (shouldBlockSuggestionCalc: boolean) => { shouldBlockCalc.current = shouldBlockSuggestionCalc; }, [shouldBlockCalc], ); - const onClose = useCallback(() => { - setSuggestionValues((prevState) => ({...prevState, suggestedMentions: []})); - }, []); - const getSuggestions = useCallback(() => suggestionValues.suggestedMentions, [suggestionValues]); useImperativeHandle( - forwardedRef, + ref, () => ({ resetSuggestions, triggerHotkeyActions, @@ -298,34 +291,16 @@ function SuggestionMention({ return ( ); } -SuggestionMention.propTypes = propTypes; -SuggestionMention.defaultProps = defaultProps; SuggestionMention.displayName = 'SuggestionMention'; -const SuggestionMentionWithRef = React.forwardRef((props, ref) => ( - -)); - -SuggestionMentionWithRef.displayName = 'SuggestionMentionWithRef'; - -export default SuggestionMentionWithRef; +export default forwardRef(SuggestionMention); diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.js b/src/pages/home/report/ReportActionCompose/Suggestions.tsx similarity index 54% rename from src/pages/home/report/ReportActionCompose/Suggestions.js rename to src/pages/home/report/ReportActionCompose/Suggestions.tsx index 5dc71fec6419..f997637a8c4c 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.js +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -1,64 +1,67 @@ -import PropTypes from 'prop-types'; -import React, {useCallback, useContext, useEffect, useImperativeHandle, useRef} from 'react'; +import type {ForwardedRef} from 'react'; +import React, {forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useRef} from 'react'; +import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; import {DragAndDropContext} from '@components/DragAndDrop/Provider'; import usePrevious from '@hooks/usePrevious'; +import type {SuggestionsRef} from './ReportActionCompose'; import SuggestionEmoji from './SuggestionEmoji'; import SuggestionMention from './SuggestionMention'; -import * as SuggestionProps from './suggestionProps'; -const propTypes = { - /** A ref to this component */ - forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), - - /** Function to clear the input */ - resetKeyboardInput: PropTypes.func.isRequired, - - /** Is auto suggestion picker large */ - isAutoSuggestionPickerLarge: PropTypes.bool, - - ...SuggestionProps.baseProps, +type Selection = { + start: number; + end: number; }; -const defaultProps = { - forwardedRef: null, - isAutoSuggestionPickerLarge: true, +type SuggestionProps = { + value: string; + setValue: (newValue: string) => void; + selection: Selection; + setSelection: (newSelection: Selection) => void; + updateComment: (newComment: string, shouldDebounceSaveComment?: boolean) => void; + measureParentContainer: () => void; + isComposerFullSize: boolean; + isComposerFocused?: boolean; + resetKeyboardInput: () => void; + isAutoSuggestionPickerLarge?: boolean; + composerHeight?: number; }; /** * This component contains the individual suggestion components. * If you want to add a new suggestion type, add it here. * - * @returns {React.Component} */ -function Suggestions({ - isComposerFullSize, - value, - setValue, - selection, - setSelection, - updateComment, - composerHeight, - forwardedRef, - resetKeyboardInput, - measureParentContainer, - isAutoSuggestionPickerLarge, - isComposerFocused, -}) { - const suggestionEmojiRef = useRef(null); - const suggestionMentionRef = useRef(null); +function Suggestions( + { + isComposerFullSize, + value, + setValue, + selection, + setSelection, + updateComment, + composerHeight, + resetKeyboardInput, + measureParentContainer, + isAutoSuggestionPickerLarge = true, + isComposerFocused, + }: SuggestionProps, + ref: ForwardedRef, +) { + const suggestionEmojiRef = useRef(null); + const suggestionMentionRef = useRef(null); const {isDraggingOver} = useContext(DragAndDropContext); const prevIsDraggingOver = usePrevious(isDraggingOver); const getSuggestions = useCallback(() => { - if (suggestionEmojiRef.current && suggestionEmojiRef.current.getSuggestions) { + if (suggestionEmojiRef.current?.getSuggestions) { const emojiSuggestions = suggestionEmojiRef.current.getSuggestions(); if (emojiSuggestions.length > 0) { return emojiSuggestions; } } - if (suggestionMentionRef.current && suggestionMentionRef.current.getSuggestions) { + if (suggestionMentionRef.current?.getSuggestions) { const mentionSuggestions = suggestionMentionRef.current.getSuggestions(); if (mentionSuggestions.length > 0) { return mentionSuggestions; @@ -72,38 +75,36 @@ function Suggestions({ * Clean data related to EmojiSuggestions */ const resetSuggestions = useCallback(() => { - suggestionEmojiRef.current.resetSuggestions(); - suggestionMentionRef.current.resetSuggestions(); + suggestionEmojiRef.current?.resetSuggestions(); + suggestionMentionRef.current?.resetSuggestions(); }, []); /** * Listens for keyboard shortcuts and applies the action - * - * @param {Object} e */ - const triggerHotkeyActions = useCallback((e) => { - const emojiHandler = suggestionEmojiRef.current.triggerHotkeyActions(e); - const mentionHandler = suggestionMentionRef.current.triggerHotkeyActions(e); - return emojiHandler || mentionHandler; + const triggerHotkeyActions = useCallback((e: KeyboardEvent) => { + const emojiHandler = suggestionEmojiRef.current?.triggerHotkeyActions(e); + const mentionHandler = suggestionMentionRef.current?.triggerHotkeyActions(e); + return emojiHandler ?? mentionHandler; }, []); - const onSelectionChange = useCallback((e) => { - const emojiHandler = suggestionEmojiRef.current.onSelectionChange(e); + const onSelectionChange = useCallback((e: NativeSyntheticEvent) => { + const emojiHandler = suggestionEmojiRef.current?.onSelectionChange(e); return emojiHandler; }, []); const updateShouldShowSuggestionMenuToFalse = useCallback(() => { - suggestionEmojiRef.current.updateShouldShowSuggestionMenuToFalse(); - suggestionMentionRef.current.updateShouldShowSuggestionMenuToFalse(); + suggestionEmojiRef.current?.updateShouldShowSuggestionMenuToFalse(); + suggestionMentionRef.current?.updateShouldShowSuggestionMenuToFalse(); }, []); - const setShouldBlockSuggestionCalc = useCallback((shouldBlock) => { - suggestionEmojiRef.current.setShouldBlockSuggestionCalc(shouldBlock); - suggestionMentionRef.current.setShouldBlockSuggestionCalc(shouldBlock); + const setShouldBlockSuggestionCalc = useCallback((shouldBlock: boolean) => { + suggestionEmojiRef.current?.setShouldBlockSuggestionCalc(shouldBlock); + suggestionMentionRef.current?.setShouldBlockSuggestionCalc(shouldBlock); }, []); useImperativeHandle( - forwardedRef, + ref, () => ({ resetSuggestions, onSelectionChange, @@ -152,18 +153,8 @@ function Suggestions({ ); } -Suggestions.propTypes = propTypes; -Suggestions.defaultProps = defaultProps; Suggestions.displayName = 'Suggestions'; -const SuggestionsWithRef = React.forwardRef((props, ref) => ( - -)); - -SuggestionsWithRef.displayName = 'SuggestionsWithRef'; +export default forwardRef(Suggestions); -export default SuggestionsWithRef; +export type {SuggestionProps}; diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index b26dc167ed44..a4d29967be1e 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -20,7 +20,7 @@ type Icon = { type: AvatarType; /** Owner of the avatar. If user, displayName. If workspace, policy name */ - name: string; + name?: string; /** Avatar id */ id?: number | string; From 717a5e8d5f3229cf1611c14382adfb0ae5cd1268 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Sun, 21 Jan 2024 14:38:40 +0100 Subject: [PATCH 043/213] fix: started migrating SuggestionEmoji component --- .../ReportActionCompose/SuggestionEmoji.tsx | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index 06089b748554..c5eb13176d44 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -1,8 +1,7 @@ -import PropTypes from 'prop-types'; -import React, {ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import type {ForwardedRef} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import EmojiSuggestions from '@components/EmojiSuggestions'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useLocalize from '@hooks/useLocalize'; @@ -10,22 +9,12 @@ import * as EmojiUtils from '@libs/EmojiUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -// eslint-disable-next-line import/no-cycle -import {SuggestionProps} from './Suggestions'; +import type {SuggestionProps} from './Suggestions'; -/** - * Check if this piece of string looks like an emoji - */ -const isEmojiCode = (str: string, pos: number): boolean => { - const leftWords = str.slice(0, pos).split(CONST.REGEX.SPECIAL_CHAR_OR_EMOJI); - const leftWord = leftWords.at(-1) ?? ''; - return CONST.REGEX.HAS_COLON_ONLY_AT_THE_BEGINNING.test(leftWord) && leftWord.length > 2; -}; - -const defaultSuggestionsValues = { - suggestedEmojis: [], - colonSignIndex: -1, - shouldShowSuggestionMenu: false, +type SuggestionsValue = { + suggestedEmojis: any[]; + colonIndex: number; + shouldShowSuggestionMenu: boolean; }; type SuggestionEmojiOnyxProps = { @@ -39,6 +28,21 @@ type SuggestionEmojiProps = { } & SuggestionEmojiOnyxProps & SuggestionProps; +/** + * Check if this piece of string looks like an emoji + */ +const isEmojiCode = (str: string, pos: number): boolean => { + const leftWords = str.slice(0, pos).split(CONST.REGEX.SPECIAL_CHAR_OR_EMOJI); + const leftWord = leftWords.at(-1) ?? ''; + return CONST.REGEX.HAS_COLON_ONLY_AT_THE_BEGINNING.test(leftWord) && leftWord.length > 2; +}; + +const defaultSuggestionsValues: SuggestionsValue = { + suggestedEmojis: [], + colonIndex: -1, + shouldShowSuggestionMenu: false, +}; + function SuggestionEmoji( { preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, @@ -61,7 +65,7 @@ function SuggestionEmoji( const [highlightedEmojiIndex, setHighlightedEmojiIndex] = useArrowKeyFocusManager({ isActive: isEmojiSuggestionsMenuVisible, - maxIndex: SuggestionsUtils.getMaxArrowIndex(suggestionValues.suggestedEmojis.length, isAutoSuggestionPickerLarge), + maxIndex: SuggestionsUtils.getMaxArrowIndex(suggestionValues.suggestedEmojis.length, !!isAutoSuggestionPickerLarge), shouldExcludeTextAreaNodes: false, }); @@ -75,7 +79,7 @@ function SuggestionEmoji( * @param {Number} selectedEmoji */ const insertSelectedEmoji = useCallback( - (highlightedEmojiIndexInner) => { + (highlightedEmojiIndexInner: number) => { const commentBeforeColon = value.slice(0, suggestionValues.colonIndex); const emojiObject = suggestionValues.suggestedEmojis[highlightedEmojiIndexInner]; const emojiCode = emojiObject.types && emojiObject.types[preferredSkinTone] ? emojiObject.types[preferredSkinTone] : emojiObject.code; From 1d21ded647fbd25ab4d1375133fe345a31beed02 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 22 Jan 2024 11:01:20 +0700 Subject: [PATCH 044/213] fix: Delay in updating green dot and total amount --- src/libs/actions/IOU.js | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 7ee752a1f0ef..aeff964cac6b 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -329,7 +329,7 @@ function getReceiptError(receipt, filename, isScanRequest = true) { * @param {Object} policy - May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) * @param {Array} policyTags * @param {Array} policyCategories - * @param {Boolean} hasOutstandingChildRequest + * @param {Array} needsToBeManuallySubmitted * @returns {Array} - An array containing the optimistic data, success data, and failure data. */ function buildOnyxDataForMoneyRequest( @@ -348,7 +348,7 @@ function buildOnyxDataForMoneyRequest( policy, policyTags, policyCategories, - hasOutstandingChildRequest = false, + needsToBeManuallySubmitted, ) { const isScanRequest = TransactionUtils.isScanRequest(transaction); const optimisticData = [ @@ -361,7 +361,7 @@ function buildOnyxDataForMoneyRequest( lastReadTime: DateUtils.getDBTime(), lastMessageTranslationKey: '', iouReportID: iouReport.reportID, - hasOutstandingChildRequest, + hasOutstandingChildRequest: needsToBeManuallySubmitted && iouReport.managerID === userAccountID && iouReport.total !== 0, ...(isNewChatReport ? {pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}} : {}), }, }, @@ -687,7 +687,7 @@ function getMoneyRequestInformation( let iouReport = isNewIOUReport ? null : allReports[`${ONYXKEYS.COLLECTION.REPORT}${chatReport.iouReportID}`]; // Check if the Scheduled Submit is enabled in case of expense report - let needsToBeManuallySubmitted = false; + let needsToBeManuallySubmitted = true; let isFromPaidPolicy = false; if (isPolicyExpenseChat) { isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); @@ -806,10 +806,6 @@ function getMoneyRequestInformation( } : undefined; - // The policy expense chat should have the GBR only when its a paid policy and the scheduled submit is turned off - // so the employee has to submit to their manager manually. - const hasOutstandingChildRequest = isPolicyExpenseChat && needsToBeManuallySubmitted; - // STEP 5: Build Onyx Data const [optimisticData, successData, failureData] = buildOnyxDataForMoneyRequest( chatReport, @@ -827,7 +823,7 @@ function getMoneyRequestInformation( policy, policyTags, policyCategories, - hasOutstandingChildRequest, + needsToBeManuallySubmitted, ); return { @@ -2544,7 +2540,19 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView let updatedIOUReport = {...iouReport}; const updatedReportPreviewAction = {...reportPreviewAction}; updatedReportPreviewAction.pendingAction = shouldDeleteIOUReport ? CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE : CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; - if (ReportUtils.isExpenseReport(iouReport)) { + + const isPolicyExpenseChat = ReportUtils.isExpenseReport(iouReport); + + let needsToBeManuallySubmitted = true; + if (isPolicyExpenseChat) { + const policy = ReportUtils.getPolicy(iouReport.policyID); + const isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); + + // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN + needsToBeManuallySubmitted = isFromPaidPolicy && !(policy.isHarvestingEnabled || false); + } + + if (isPolicyExpenseChat) { updatedIOUReport = {...iouReport}; // Because of the Expense reports are stored as negative values, we add the total from the amount @@ -2646,6 +2654,13 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView }, ] : []), + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + hasOutstandingChildRequest: needsToBeManuallySubmitted && updatedIOUReport.managerID === userAccountID && updatedIOUReport.total !== 0, + }, + }, ]; const successData = [ From 7c7132531962d803f64392171d7c3c9d95fc9b6c Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 22 Jan 2024 13:55:47 +0100 Subject: [PATCH 045/213] fix: migrate SuggestionEmoji --- src/libs/EmojiUtils.ts | 2 +- .../ReportActionCompose.tsx | 2 +- .../ReportActionCompose/SuggestionEmoji.tsx | 51 ++++++++----------- .../ReportActionCompose/Suggestions.tsx | 2 +- 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index e34fa0b90fc6..7971e6147c19 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -384,7 +384,7 @@ function replaceAndExtractEmojis(text: string, preferredSkinTone: number = CONST * Suggest emojis when typing emojis prefix after colon * @param [limit] - matching emojis limit */ -function suggestEmojis(text: string, lang: keyof SupportedLanguage, limit = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { +function suggestEmojis(text: string, lang: SupportedLanguage, limit: number = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { // emojisTrie is importing the emoji JSON file on the app starting and we want to avoid it const emojisTrie = require('./EmojiTrie').default; diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 025b7142df0d..9754d45be834 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -56,7 +56,7 @@ type SuggestionsRef = { triggerHotkeyActions: (event: KeyboardEvent) => void; updateShouldShowSuggestionMenuToFalse: (shouldShowSuggestionMenu?: boolean) => void; setShouldBlockSuggestionCalc: (shouldBlock: boolean) => void; - getSuggestions: () => Mention[]; + getSuggestions: () => Mention[] | Emoji[]; }; type ReportActionComposeOnyxProps = { diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index c5eb13176d44..dac59065371d 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -1,25 +1,29 @@ import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; +import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import type {Emoji} from '@assets/emojis/types'; import EmojiSuggestions from '@components/EmojiSuggestions'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useLocalize from '@hooks/useLocalize'; +import type {SupportedLanguage} from '@libs/EmojiTrie'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import type {SuggestionsRef} from './ReportActionCompose'; import type {SuggestionProps} from './Suggestions'; type SuggestionsValue = { - suggestedEmojis: any[]; + suggestedEmojis: Emoji[]; colonIndex: number; shouldShowSuggestionMenu: boolean; }; type SuggestionEmojiOnyxProps = { /** Preferred skin tone */ - preferredSkinTone: OnyxEntry; + preferredSkinTone: number; }; type SuggestionEmojiProps = { @@ -47,21 +51,19 @@ function SuggestionEmoji( { preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, value, - setValue, selection, setSelection, updateComment, - isComposerFullSize, isAutoSuggestionPickerLarge, resetKeyboardInput, measureParentContainer, isComposerFocused, }: SuggestionEmojiProps, - ref: ForwardedRef, + ref: ForwardedRef, ) { const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); - const isEmojiSuggestionsMenuVisible = !_.isEmpty(suggestionValues.suggestedEmojis) && suggestionValues.shouldShowSuggestionMenu; + const isEmojiSuggestionsMenuVisible = suggestionValues.suggestedEmojis.length > 0 && suggestionValues.shouldShowSuggestionMenu; const [highlightedEmojiIndex, setHighlightedEmojiIndex] = useArrowKeyFocusManager({ isActive: isEmojiSuggestionsMenuVisible, @@ -82,7 +84,7 @@ function SuggestionEmoji( (highlightedEmojiIndexInner: number) => { const commentBeforeColon = value.slice(0, suggestionValues.colonIndex); const emojiObject = suggestionValues.suggestedEmojis[highlightedEmojiIndexInner]; - const emojiCode = emojiObject.types && emojiObject.types[preferredSkinTone] ? emojiObject.types[preferredSkinTone] : emojiObject.code; + const emojiCode = emojiObject.types?.[preferredSkinTone] ? emojiObject.types[preferredSkinTone] : emojiObject.code; const commentAfterColonWithEmojiNameRemoved = value.slice(selection.end); updateComment(`${commentBeforeColon}${emojiCode} ${SuggestionsUtils.trimLeadingSpace(commentAfterColonWithEmojiNameRemoved)}`, true); @@ -119,11 +121,9 @@ function SuggestionEmoji( /** * Listens for keyboard shortcuts and applies the action - * - * @param {Object} e */ const triggerHotkeyActions = useCallback( - (e) => { + (e: KeyboardEvent) => { const suggestionsExist = suggestionValues.suggestedEmojis.length > 0; if (((!e.shiftKey && e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) || e.key === CONST.KEYBOARD_SHORTCUTS.TAB.shortcutKey) && suggestionsExist) { @@ -151,7 +151,7 @@ function SuggestionEmoji( * Calculates and cares about the content of an Emoji Suggester */ const calculateEmojiSuggestion = useCallback( - (selectionEnd) => { + (selectionEnd: number) => { if (shouldBlockCalc.current || !value) { shouldBlockCalc.current = false; resetSuggestions(); @@ -161,16 +161,16 @@ function SuggestionEmoji( const colonIndex = leftString.lastIndexOf(':'); const isCurrentlyShowingEmojiSuggestion = isEmojiCode(value, selectionEnd); - const nextState = { + const nextState: SuggestionsValue = { suggestedEmojis: [], colonIndex, shouldShowSuggestionMenu: false, }; - const newSuggestedEmojis = EmojiUtils.suggestEmojis(leftString, preferredLocale); + const newSuggestedEmojis = EmojiUtils.suggestEmojis(leftString, preferredLocale as SupportedLanguage); - if (newSuggestedEmojis.length && isCurrentlyShowingEmojiSuggestion) { + if (newSuggestedEmojis?.length && isCurrentlyShowingEmojiSuggestion) { nextState.suggestedEmojis = newSuggestedEmojis; - nextState.shouldShowSuggestionMenu = !_.isEmpty(newSuggestedEmojis); + nextState.shouldShowSuggestionMenu = !isEmptyObject(newSuggestedEmojis); } setSuggestionValues((prevState) => ({...prevState, ...nextState})); @@ -187,7 +187,7 @@ function SuggestionEmoji( }, [selection, calculateEmojiSuggestion, isComposerFocused]); const onSelectionChange = useCallback( - (e) => { + (e: NativeSyntheticEvent) => { /** * we pass here e.nativeEvent.selection.end directly to calculateEmojiSuggestion * because in other case calculateEmojiSuggestion will have an old calculation value @@ -199,7 +199,7 @@ function SuggestionEmoji( ); const setShouldBlockSuggestionCalc = useCallback( - (shouldBlockSuggestionCalc) => { + (shouldBlockSuggestionCalc: boolean) => { shouldBlockCalc.current = shouldBlockSuggestionCalc; }, [shouldBlockCalc], @@ -207,10 +207,6 @@ function SuggestionEmoji( const getSuggestions = useCallback(() => suggestionValues.suggestedEmojis, [suggestionValues]); - const resetEmojiSuggestions = useCallback(() => { - setSuggestionValues((prevState) => ({...prevState, suggestedEmojis: []})); - }, []); - useImperativeHandle( ref, () => ({ @@ -230,17 +226,12 @@ function SuggestionEmoji( return ( ); @@ -248,9 +239,11 @@ function SuggestionEmoji( SuggestionEmoji.displayName = 'SuggestionEmoji'; +const SuggestionEmojiForwardedRef = forwardRef(SuggestionEmoji); + export default withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, selector: EmojiUtils.getPreferredSkinToneIndex, }, -})(forwardRef(SuggestionEmoji)); +})(SuggestionEmojiForwardedRef); diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.tsx b/src/pages/home/report/ReportActionCompose/Suggestions.tsx index f997637a8c4c..3a009728a915 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -89,7 +89,7 @@ function Suggestions( }, []); const onSelectionChange = useCallback((e: NativeSyntheticEvent) => { - const emojiHandler = suggestionEmojiRef.current?.onSelectionChange(e); + const emojiHandler = suggestionEmojiRef.current?.onSelectionChange?.(e); return emojiHandler; }, []); From be57be30f750da1ca8a1374213907c3c8e8b8eb1 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 22 Jan 2024 14:11:08 +0100 Subject: [PATCH 046/213] fix: SuggestionEmoji and Suggestions types, migrate SendButton --- .../{SendButton.js => SendButton.tsx} | 14 +++++++------- .../report/ReportActionCompose/SuggestionEmoji.tsx | 12 +++++------- .../report/ReportActionCompose/Suggestions.tsx | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) rename src/pages/home/report/ReportActionCompose/{SendButton.js => SendButton.tsx} (90%) diff --git a/src/pages/home/report/ReportActionCompose/SendButton.js b/src/pages/home/report/ReportActionCompose/SendButton.tsx similarity index 90% rename from src/pages/home/report/ReportActionCompose/SendButton.js rename to src/pages/home/report/ReportActionCompose/SendButton.tsx index d0b0453ace2f..b6c6200fc7c0 100644 --- a/src/pages/home/report/ReportActionCompose/SendButton.js +++ b/src/pages/home/report/ReportActionCompose/SendButton.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; @@ -11,21 +10,21 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -const propTypes = { +type SendButtonProps = { /** Whether the button is disabled */ - isDisabled: PropTypes.bool.isRequired, + isDisabled: boolean; /** Handle clicking on send button */ - handleSendMessage: PropTypes.func.isRequired, + handleSendMessage: () => void; }; -function SendButton({isDisabled: isDisabledProp, handleSendMessage}) { +function SendButton({isDisabled: isDisabledProp, handleSendMessage}: SendButtonProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); const Tap = Gesture.Tap() - .enabled() + .enabled(!isDisabledProp) .onEnd(() => { handleSendMessage(); }); @@ -46,6 +45,8 @@ function SendButton({isDisabled: isDisabledProp, handleSendMessage}) { ]} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.send')} + accessible + onPress={() => {}} > {({pressed}) => ( void; + resetKeyboardInput: (() => void) | undefined; } & SuggestionEmojiOnyxProps & SuggestionProps; @@ -92,7 +92,7 @@ function SuggestionEmoji( // In some Android phones keyboard, the text to search for the emoji is not cleared // will be added after the user starts typing again on the keyboard. This package is // a workaround to reset the keyboard natively. - resetKeyboardInput(); + resetKeyboardInput?.(); setSelection({ start: suggestionValues.colonIndex + emojiCode.length + CONST.SPACE_LENGTH, @@ -239,11 +239,9 @@ function SuggestionEmoji( SuggestionEmoji.displayName = 'SuggestionEmoji'; -const SuggestionEmojiForwardedRef = forwardRef(SuggestionEmoji); - -export default withOnyx({ +export default withOnyx, SuggestionEmojiOnyxProps>({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, selector: EmojiUtils.getPreferredSkinToneIndex, }, -})(SuggestionEmojiForwardedRef); +})(forwardRef(SuggestionEmoji)); diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.tsx b/src/pages/home/report/ReportActionCompose/Suggestions.tsx index 3a009728a915..2500af40c7c6 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -22,7 +22,7 @@ type SuggestionProps = { measureParentContainer: () => void; isComposerFullSize: boolean; isComposerFocused?: boolean; - resetKeyboardInput: () => void; + resetKeyboardInput?: () => void; isAutoSuggestionPickerLarge?: boolean; composerHeight?: number; }; From 929a51a9a8be77696c674b881b7835d85147e481 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 22 Jan 2024 14:38:55 +0100 Subject: [PATCH 047/213] fix: migrate AttachmentPickerWithMenuItems --- src/components/PopoverMenu.tsx | 2 +- ...s.js => AttachmentPickerWithMenuItems.tsx} | 119 ++++++++---------- 2 files changed, 52 insertions(+), 69 deletions(-) rename src/pages/home/report/ReportActionCompose/{AttachmentPickerWithMenuItems.js => AttachmentPickerWithMenuItems.tsx} (82%) diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 17b1a119671a..b411de1103f1 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -69,7 +69,7 @@ type PopoverMenuProps = Partial & { anchorPosition: AnchorPosition; /** Ref of the anchor */ - anchorRef: RefObject; + anchorRef: RefObject; /** Where the popover should be positioned relative to the anchor points. */ anchorAlignment?: AnchorAlignment; diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx similarity index 82% rename from src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js rename to src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 444dd939142b..547283cc4eb0 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,23 +1,23 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; +import {useIsFocused} from '@react-navigation/native'; +import type {FC} from 'react'; import React, {useCallback, useEffect, useMemo} from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {SvgProps} from 'react-native-svg'; +import type {ValueOf} from 'type-fest'; import AttachmentPicker from '@components/AttachmentPicker'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PopoverMenu from '@components/PopoverMenu'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; -import withNavigationFocus from '@components/withNavigationFocus'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as IOU from '@userActions/IOU'; @@ -26,88 +26,77 @@ import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; -const propTypes = { - /** The report currently being looked at */ - report: PropTypes.shape({ - /** ID of the report */ - reportID: PropTypes.string, +type MoneyRequestOption = { + icon: FC; + text: string; + onSelected: () => void; +}; - /** Whether or not the report is in the process of being created */ - loading: PropTypes.bool, - }).isRequired, +type MoneyRequestOptions = Record, MoneyRequestOption>; +type AttachmentPickerWithMenuItemsOnyxProps = { /** The policy tied to the report */ - policy: PropTypes.shape({ - /** Type of the policy */ - type: PropTypes.string, - }), + policy: OnyxEntry; +}; - /** The personal details of everyone in the report */ - reportParticipantIDs: PropTypes.arrayOf(PropTypes.number), +type AttachmentPickerWithMenuItemsProps = { + /** The report currently being looked at */ + report: OnyxTypes.Report; /** Callback to open the file in the modal */ - displayFileInModal: PropTypes.func.isRequired, + displayFileInModal: (url: string) => void; /** Whether or not the full size composer is available */ - isFullComposerAvailable: PropTypes.bool.isRequired, + isFullComposerAvailable: boolean; /** Whether or not the composer is full size */ - isComposerFullSize: PropTypes.bool.isRequired, + isComposerFullSize: boolean; /** Whether or not the user is blocked from concierge */ - isBlockedFromConcierge: PropTypes.bool.isRequired, + isBlockedFromConcierge: boolean; /** Whether or not the attachment picker is disabled */ - disabled: PropTypes.bool.isRequired, + disabled: boolean; /** Sets the menu visibility */ - setMenuVisibility: PropTypes.func.isRequired, + setMenuVisibility: (isVisible: boolean) => void; /** Whether or not the menu is visible */ - isMenuVisible: PropTypes.bool.isRequired, + isMenuVisible: boolean; /** Report ID */ - reportID: PropTypes.string.isRequired, + reportID: string; /** Called when opening the attachment picker */ - onTriggerAttachmentPicker: PropTypes.func.isRequired, + onTriggerAttachmentPicker: () => void; /** Called when cancelling the attachment picker */ - onCanceledAttachmentPicker: PropTypes.func.isRequired, + onCanceledAttachmentPicker: () => void; /** Called when the menu with the items is closed after it was open */ - onMenuClosed: PropTypes.func.isRequired, + onMenuClosed: () => void; /** Called when the add action button is pressed */ - onAddActionPressed: PropTypes.func.isRequired, + onAddActionPressed: () => void; /** Called when the menu item is selected */ - onItemSelected: PropTypes.func.isRequired, + onItemSelected: () => void; /** A ref for the add action button */ - actionButtonRef: PropTypes.shape({ - // eslint-disable-next-line react/forbid-prop-types - current: PropTypes.object, - }).isRequired, - - /** Whether or not the screen is focused */ - isFocused: PropTypes.bool.isRequired, + actionButtonRef: React.RefObject; /** A function that toggles isScrollLikelyLayoutTriggered flag for a certain period of time */ - raiseIsScrollLikelyLayoutTriggered: PropTypes.func.isRequired, -}; + raiseIsScrollLikelyLayoutTriggered: () => void; -const defaultProps = { - reportParticipantIDs: [], - policy: {}, -}; + /** The personal details of everyone in the report */ + reportParticipantIDs?: number[]; +} & AttachmentPickerWithMenuItemsOnyxProps; /** * This includes the popover of options you see when pressing the + button in the composer. * It also contains the attachment picker, as the menu items need to be able to open it. - * - * @returns {React.Component} */ function AttachmentPickerWithMenuItems({ report, @@ -127,9 +116,9 @@ function AttachmentPickerWithMenuItems({ onAddActionPressed, onItemSelected, actionButtonRef, - isFocused, raiseIsScrollLikelyLayoutTriggered, -}) { +}: AttachmentPickerWithMenuItemsProps) { + const isFocused = useIsFocused(); const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -137,10 +126,9 @@ function AttachmentPickerWithMenuItems({ /** * Returns the list of IOU Options - * @returns {Array} */ const moneyRequestOptions = useMemo(() => { - const options = { + const options: MoneyRequestOptions = { [CONST.IOU.TYPE.SPLIT]: { icon: Expensicons.Receipt, text: translate('iou.splitBill'), @@ -158,16 +146,15 @@ function AttachmentPickerWithMenuItems({ }, }; - return _.map(ReportUtils.getMoneyRequestOptions(report, policy, reportParticipantIDs), (option) => ({ + return ReportUtils.getMoneyRequestOptions(report, policy, reportParticipantIDs ?? []).map((option) => ({ ...options[option], })); }, [report, policy, reportParticipantIDs, translate]); /** * Determines if we can show the task option - * @returns {Boolean} */ - const taskOption = useMemo(() => { + const taskOption: MoneyRequestOption[] = useMemo(() => { if (!ReportUtils.canCreateTaskInReport(report)) { return []; } @@ -206,6 +193,7 @@ function AttachmentPickerWithMenuItems({ return ( + {/* @ts-expect-error TODO: Remove this once SettlementButton (https://github.com/Expensify/App/issues/25134) is migrated to TypeScript. */} {({openPicker}) => { const triggerAttachmentPicker = () => { onTriggerAttachmentPicker(); @@ -235,7 +223,7 @@ function AttachmentPickerWithMenuItems({ { - e.preventDefault(); + e?.preventDefault(); raiseIsScrollLikelyLayoutTriggered(); Report.setIsComposerFullSize(reportID, false); }} @@ -257,7 +245,7 @@ function AttachmentPickerWithMenuItems({ { - e.preventDefault(); + e?.preventDefault(); raiseIsScrollLikelyLayoutTriggered(); Report.setIsComposerFullSize(reportID, true); }} @@ -279,14 +267,14 @@ function AttachmentPickerWithMenuItems({ { - e.preventDefault(); + e?.preventDefault(); if (!isFocused) { return; } onAddActionPressed(); // Drop focus to avoid blue focus ring. - actionButtonRef.current.blur(); + actionButtonRef.current?.blur(); setMenuVisibility(!isMenuVisible); }} style={styles.composerSizeButton} @@ -329,15 +317,10 @@ function AttachmentPickerWithMenuItems({ ); } -AttachmentPickerWithMenuItems.propTypes = propTypes; -AttachmentPickerWithMenuItems.defaultProps = defaultProps; AttachmentPickerWithMenuItems.displayName = 'AttachmentPickerWithMenuItems'; -export default compose( - withNavigationFocus, - withOnyx({ - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${lodashGet(report, 'policyID')}`, - }, - }), -)(AttachmentPickerWithMenuItems); +export default withOnyx({ + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, + }, +})(AttachmentPickerWithMenuItems); From 0cc6502443d17483711b9a26bd010e878e944626 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 22 Jan 2024 15:09:18 +0100 Subject: [PATCH 048/213] fix: Migrate SilentCommentUpdater --- ...estions.js => ComposerWithSuggestions.tsx} | 0 .../{index.android.js => index.android.tsx} | 22 ++-------- .../{index.js => index.tsx} | 42 +++---------------- .../SilentCommentUpdater/types.ts | 29 +++++++++++++ 4 files changed, 38 insertions(+), 55 deletions(-) rename src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/{ComposerWithSuggestions.js => ComposerWithSuggestions.tsx} (100%) rename src/pages/home/report/ReportActionCompose/SilentCommentUpdater/{index.android.js => index.android.tsx} (69%) rename src/pages/home/report/ReportActionCompose/SilentCommentUpdater/{index.js => index.tsx} (60%) create mode 100644 src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx similarity index 100% rename from src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js rename to src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.android.js b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.android.tsx similarity index 69% rename from src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.android.js rename to src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.android.tsx index f924a7b59194..cee9cbf794bf 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.android.js +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.android.tsx @@ -1,19 +1,7 @@ -import PropTypes from 'prop-types'; import {useEffect} from 'react'; import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; - -const propTypes = { - /** The comment of the report */ - comment: PropTypes.string, - - /** Updates the comment */ - updateComment: PropTypes.func.isRequired, -}; - -const defaultProps = { - comment: '', -}; +import type {SilentCommentUpdaterOnyxProps, SilentCommentUpdaterProps} from './types'; /** * Adding .android component to disable updating comment when prev comment is different @@ -24,9 +12,8 @@ const defaultProps = { * This component doesn't render anything. It runs a side effect to update the comment of a report under certain conditions. * It is connected to the actual draft comment in onyx. The comment in onyx might updates multiple times, and we want to avoid * re-rendering a UI component for that. That's why the side effect was moved down to a separate component. - * @returns {null} */ -function SilentCommentUpdater({comment, updateComment}) { +function SilentCommentUpdater({comment = '', updateComment}: SilentCommentUpdaterProps) { useEffect(() => { updateComment(comment); // eslint-disable-next-line react-hooks/exhaustive-deps -- We need to run this on mount @@ -35,13 +22,10 @@ function SilentCommentUpdater({comment, updateComment}) { return null; } -SilentCommentUpdater.propTypes = propTypes; -SilentCommentUpdater.defaultProps = defaultProps; SilentCommentUpdater.displayName = 'SilentCommentUpdater'; -export default withOnyx({ +export default withOnyx({ comment: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, - initialValue: '', }, })(SilentCommentUpdater); diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.js b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx similarity index 60% rename from src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.js rename to src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx index 9aa997a892f4..afee06dbc8ff 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.js +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx @@ -1,46 +1,18 @@ -import PropTypes from 'prop-types'; import {useEffect} from 'react'; import {withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import ONYXKEYS from '@src/ONYXKEYS'; - -const propTypes = { - /** The comment of the report */ - comment: PropTypes.string, - - /** The report associated with the comment */ - report: PropTypes.shape({ - /** The ID of the report */ - reportID: PropTypes.string, - }).isRequired, - - /** The value of the comment */ - value: PropTypes.string.isRequired, - - /** The ref of the comment */ - commentRef: PropTypes.shape({ - /** The current value of the comment */ - current: PropTypes.string, - }).isRequired, - - /** Updates the comment */ - updateComment: PropTypes.func.isRequired, -}; - -const defaultProps = { - comment: '', -}; +import type {SilentCommentUpdaterOnyxProps, SilentCommentUpdaterProps} from './types'; /** * This component doesn't render anything. It runs a side effect to update the comment of a report under certain conditions. * It is connected to the actual draft comment in onyx. The comment in onyx might updates multiple times, and we want to avoid * re-rendering a UI component for that. That's why the side effect was moved down to a separate component. - * @returns {null} */ -function SilentCommentUpdater({comment, commentRef, report, value, updateComment}) { +function SilentCommentUpdater({comment = '', commentRef, report, value, updateComment}: SilentCommentUpdaterProps) { const prevCommentProp = usePrevious(comment); - const prevReportId = usePrevious(report.reportID); + const prevReportId = usePrevious(report?.reportID); const {preferredLocale} = useLocalize(); const prevPreferredLocale = usePrevious(preferredLocale); @@ -56,21 +28,19 @@ function SilentCommentUpdater({comment, commentRef, report, value, updateComment // As the report IDs change, make sure to update the composer comment as we need to make sure // we do not show incorrect data in there (ie. draft of message from other report). - if (preferredLocale === prevPreferredLocale && report.reportID === prevReportId && !shouldSyncComment) { + if (preferredLocale === prevPreferredLocale && report?.reportID === prevReportId && !shouldSyncComment) { return; } updateComment(comment); - }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, report.reportID, updateComment, value, commentRef]); + }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, report?.reportID, updateComment, value, commentRef]); return null; } -SilentCommentUpdater.propTypes = propTypes; -SilentCommentUpdater.defaultProps = defaultProps; SilentCommentUpdater.displayName = 'SilentCommentUpdater'; -export default withOnyx({ +export default withOnyx({ comment: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, initialValue: '', diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts new file mode 100644 index 000000000000..60924f24099b --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts @@ -0,0 +1,29 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {Report} from '@src/types/onyx'; + +type SilentCommentUpdaterOnyxProps = { + /** The comment of the report */ + comment: OnyxEntry; +}; + +type SilentCommentUpdaterProps = { + /** Updates the comment */ + updateComment: (comment: OnyxEntry) => void; + + /** The ID of the report associated with the comment */ + reportID: string; + + /** The report associated with the comment */ + report: OnyxEntry; + + /** The value of the comment */ + value: string; + + /** The ref of the comment */ + commentRef: React.RefObject; + + /** The comment of the report */ + commnet: string; +} & SilentCommentUpdaterOnyxProps; + +export type {SilentCommentUpdaterProps, SilentCommentUpdaterOnyxProps}; From 8ed208cb62610d729bd6a7d1c9da1ec477b96c68 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 23 Jan 2024 10:49:06 +0100 Subject: [PATCH 049/213] fix: ComposerWithSuggestion migration --- .../AttachmentPickerWithMenuItems.tsx | 8 +- .../ComposerWithSuggestions.tsx | 239 ++++++++++-------- .../ReportActionCompose.tsx | 9 +- 3 files changed, 146 insertions(+), 110 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 547283cc4eb0..0dbfbba2759d 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -43,7 +43,7 @@ type AttachmentPickerWithMenuItemsOnyxProps = { type AttachmentPickerWithMenuItemsProps = { /** The report currently being looked at */ - report: OnyxTypes.Report; + report: OnyxEntry; /** Callback to open the file in the modal */ displayFileInModal: (url: string) => void; @@ -132,17 +132,17 @@ function AttachmentPickerWithMenuItems({ [CONST.IOU.TYPE.SPLIT]: { icon: Expensicons.Receipt, text: translate('iou.splitBill'), - onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.SPLIT, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report.reportID)), + onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.SPLIT, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report?.reportID ?? '')), }, [CONST.IOU.TYPE.REQUEST]: { icon: Expensicons.MoneyCircle, text: translate('iou.requestMoney'), - onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report.reportID)), + onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, report?.reportID ?? '')), }, [CONST.IOU.TYPE.SEND]: { icon: Expensicons.Send, text: translate('iou.sendMoney'), - onSelected: () => IOU.startMoneyRequest(CONST.IOU.TYPE.SEND, report.reportID), + onSelected: () => IOU.startMoneyRequest(CONST.IOU.TYPE.SEND, report?.reportID), }, }; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 413807b1f992..bce7d80121b4 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -1,11 +1,14 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; -import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import lodashDebounce from 'lodash/debounce'; +import type {ForwardedRef, RefAttributes, RefObject} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import type {MeasureInWindowOnSuccessCallback} from 'react-native'; import {findNodeHandle, InteractionManager, NativeModules, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {Emoji} from '@assets/emojis/types'; import Composer from '@components/Composer'; -import withKeyboardState from '@components/withKeyboardState'; +import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -14,7 +17,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; -import compose from '@libs/compose'; import * as ComposerUtils from '@libs/ComposerUtils'; import getDraftComment from '@libs/ComposerUtils/getDraftComment'; import convertToLTRForComposer from '@libs/convertToLTRForComposer'; @@ -28,6 +30,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as SuggestionUtils from '@libs/SuggestionUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; +import type {ComposerRef, SuggestionsRef} from '@pages/home/report/ReportActionCompose/ReportActionCompose'; import SilentCommentUpdater from '@pages/home/report/ReportActionCompose/SilentCommentUpdater'; import Suggestions from '@pages/home/report/ReportActionCompose/Suggestions'; import * as EmojiPickerActions from '@userActions/EmojiPickerAction'; @@ -36,17 +39,65 @@ import * as Report from '@userActions/Report'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type * as OnyxTypes from '@src/types/onyx'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; import {defaultProps, propTypes} from './composerWithSuggestionsProps'; +type ComposerWithSuggestionsOnyxProps = { + /** The number of lines the comment should take up */ + numberOfLines: number; + + /** The parent report actions for the report */ + parentReportActions: OnyxEntry; + + /** The modal state */ + modal: OnyxEntry; + + /** The preferred skin tone of the user */ + preferredSkinTone: number; + + /** Whether the input is focused */ + editFocused: boolean; +}; +type ComposerWithSuggestionsProps = { + reportID: string; + report: OnyxEntry; + reportActions: OnyxTypes.ReportAction[] | undefined; + onFocus: () => void; + onBlur: (event: FocusEvent) => void; + onValueChange: (value: string) => void; + isComposerFullSize: boolean; + isMenuVisible: boolean; + inputPlaceholder: string; + displayFileInModal: (fileURL: string) => void; + textInputShouldClear: boolean; + setTextInputShouldClear: (shouldClear: boolean) => void; + isBlockedFromConcierge: boolean; + disabled: boolean; + isFullComposerAvailable: boolean; + setIsFullComposerAvailable: (isFullComposerAvailable: boolean) => void; + setIsCommentEmpty: (isCommentEmpty: boolean) => void; + handleSendMessage: () => void; + shouldShowComposeInput: OnyxEntry; + measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; + listHeight: number; + isScrollLikelyLayoutTriggered: RefObject; + raiseIsScrollLikelyLayoutTriggered: () => void; + suggestionsRef: React.RefObject; + animatedRef: RefObject; + isNextModalWillOpenRef: RefObject; + editFocused: boolean; +} & ComposerWithSuggestionsOnyxProps & + Partial; + const {RNTextInputReset} = NativeModules; const isIOSNative = getPlatform() === CONST.PLATFORM.IOS; /** * Broadcast that the user is typing. Debounced to limit how often we publish client events. - * @param {String} reportID */ -const debouncedBroadcastUserIsTyping = _.debounce((reportID) => { +const debouncedBroadcastUserIsTyping = lodashDebounce((reportID: string) => { Report.broadcastUserIsTyping(reportID); }, 100); @@ -61,61 +112,61 @@ const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); * If a component really needs access to these state values it should be put here. * However, double check if the component really needs access, as it will re-render * on every key press. - * @param {Object} props - * @returns {React.Component} */ -function ComposerWithSuggestions({ - // Onyx - modal, - preferredSkinTone, - parentReportActions, - numberOfLines, - // HOCs - isKeyboardShown, - // Props: Report - reportID, - report, - reportActions, - // Focus - onFocus, - onBlur, - onValueChange, - // Composer - isComposerFullSize, - isMenuVisible, - inputPlaceholder, - displayFileInModal, - textInputShouldClear, - setTextInputShouldClear, - isBlockedFromConcierge, - disabled, - isFullComposerAvailable, - setIsFullComposerAvailable, - setIsCommentEmpty, - handleSendMessage, - shouldShowComposeInput, - measureParentContainer, - listHeight, - isScrollLikelyLayoutTriggered, - raiseIsScrollLikelyLayoutTriggered, - // Refs - suggestionsRef, - animatedRef, - forwardedRef, - isNextModalWillOpenRef, - editFocused, - // For testing - children, -}) { +function ComposerWithSuggestions( + { + // Onyx + modal, + preferredSkinTone, + parentReportActions, + numberOfLines, + + // Props: Report + reportID, + report, + reportActions, + // Focus + onFocus, + onBlur, + onValueChange, + // Composer + isComposerFullSize, + isMenuVisible, + inputPlaceholder, + displayFileInModal, + textInputShouldClear, + setTextInputShouldClear, + isBlockedFromConcierge, + disabled, + isFullComposerAvailable, + setIsFullComposerAvailable, + setIsCommentEmpty, + handleSendMessage, + shouldShowComposeInput, + measureParentContainer, + listHeight, + isScrollLikelyLayoutTriggered, + raiseIsScrollLikelyLayoutTriggered, + // Refs + suggestionsRef, + animatedRef, + isNextModalWillOpenRef, + editFocused, + // For testing + children, + }: ComposerWithSuggestionsProps, + ref: ForwardedRef, +) { + const {isKeyboardShown} = useKeyboardState(); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {preferredLocale} = useLocalize(); const isFocused = useIsFocused(); const navigation = useNavigation(); - const emojisPresentBefore = useRef([]); + const emojisPresentBefore = useRef([]); const [value, setValue] = useState(() => { - const draft = getDraftComment(reportID) || ''; + const draft = getDraftComment(reportID) ?? ''; if (draft) { emojisPresentBefore.current = EmojiUtils.extractEmojis(draft); } @@ -126,9 +177,9 @@ function ComposerWithSuggestions({ const {isSmallScreenWidth} = useWindowDimensions(); const maxComposerLines = isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES; - const isEmptyChat = useMemo(() => _.size(reportActions) === 1, [reportActions]); - const parentReportAction = lodashGet(parentReportActions, [report.parentReportActionID]); - const shouldAutoFocus = !modal.isVisible && (shouldFocusInputOnScreenFocus || (isEmptyChat && !ReportActionsUtils.isTransactionThread(parentReportAction))) && shouldShowComposeInput; + const isEmptyChat = useMemo(() => reportActions?.length === 1, [reportActions]); + const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? '']; + const shouldAutoFocus = !modal?.isVisible && (shouldFocusInputOnScreenFocus || (isEmptyChat && !ReportActionsUtils.isTransactionThread(parentReportAction))) && shouldShowComposeInput; const valueRef = useRef(value); valueRef.current = value; @@ -138,11 +189,11 @@ function ComposerWithSuggestions({ const [composerHeight, setComposerHeight] = useState(0); const textInputRef = useRef(null); - const insertedEmojisRef = useRef([]); + const insertedEmojisRef = useRef([]); const syncSelectionWithOnChangeTextRef = useRef(null); - const suggestions = lodashGet(suggestionsRef, 'current.getSuggestions', () => [])(); + const suggestions = suggestionsRef.current?.getSuggestions(); const hasEnoughSpaceForLargeSuggestion = SuggestionUtils.hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, suggestions.length); @@ -159,15 +210,12 @@ function ComposerWithSuggestions({ /** * Set the TextInput Ref - * - * @param {Element} el - * @memberof ReportActionCompose */ const setTextInputRef = useCallback( (el) => { ReportActionComposeFocusManager.composerRef.current = el; textInputRef.current = el; - if (_.isFunction(animatedRef)) { + if (typeof animatedRef === 'function') { animatedRef(el); } }, @@ -196,7 +244,7 @@ function ComposerWithSuggestions({ * @param {Boolean} shouldDebounceSaveComment */ const updateComment = useCallback( - (commentValue, shouldDebounceSaveComment) => { + (commentValue: string, shouldDebounceSaveComment: boolean) => { raiseIsScrollLikelyLayoutTriggered(); const {text: newComment, emojis, cursorPosition} = EmojiUtils.replaceAndExtractEmojis(commentValue, preferredSkinTone, preferredLocale); if (!_.isEmpty(emojis)) { @@ -514,7 +562,7 @@ function ComposerWithSuggestions({ }, []); useImperativeHandle( - forwardedRef, + ref, () => ({ blur, focus, @@ -608,39 +656,28 @@ ComposerWithSuggestions.propTypes = propTypes; ComposerWithSuggestions.defaultProps = defaultProps; ComposerWithSuggestions.displayName = 'ComposerWithSuggestions'; -const ComposerWithSuggestionsWithRef = React.forwardRef((props, ref) => ( - -)); - -ComposerWithSuggestionsWithRef.displayName = 'ComposerWithSuggestionsWithRef'; - -export default compose( - withKeyboardState, - withOnyx({ - numberOfLines: { - key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, - // We might not have number of lines in onyx yet, for which the composer would be rendered as null - // during the first render, which we want to avoid: - initWithStoredValues: false, - }, - modal: { - key: ONYXKEYS.MODAL, - }, - preferredSkinTone: { - key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, - selector: EmojiUtils.getPreferredSkinToneIndex, - }, - editFocused: { - key: ONYXKEYS.INPUT_FOCUSED, - }, - parentReportActions: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, - canEvict: false, - initWithStoredValues: false, - }, - }), -)(ComposerWithSuggestionsWithRef); +const ComposerWithSuggestionsWithRef = forwardRef(ComposerWithSuggestions); + +export default withOnyx, ComposerWithSuggestionsOnyxProps>({ + numberOfLines: { + key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, + // We might not have number of lines in onyx yet, for which the composer would be rendered as null + // during the first render, which we want to avoid: + initWithStoredValues: false, + }, + modal: { + key: ONYXKEYS.MODAL, + }, + preferredSkinTone: { + key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, + selector: EmojiUtils.getPreferredSkinToneIndex, + }, + editFocused: { + key: ONYXKEYS.INPUT_FOCUSED, + }, + parentReportActions: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, + canEvict: false, + initWithStoredValues: false, + }, +})(ComposerWithSuggestionsWithRef); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 9754d45be834..b46eb145173e 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -6,6 +6,7 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import {runOnJS, setNativeProps, useAnimatedRef} from 'react-native-reanimated'; +import type {Emoji} from '@assets/emojis/types'; import AttachmentModal from '@components/AttachmentModal'; import EmojiPickerButton from '@components/EmojiPicker/EmojiPickerButton'; import ExceededCommentLength from '@components/ExceededCommentLength'; @@ -389,7 +390,6 @@ function ReportActionCompose({ {({displayFileInModal}) => ( <> Date: Wed, 24 Jan 2024 09:45:39 +0100 Subject: [PATCH 050/213] fix: keep working on ComposerWithSuggestions --- src/components/Composer/index.tsx | 5 +- src/libs/updateMultilineInputRange/types.ts | 2 +- .../ComposerWithSuggestions.tsx | 110 +++++++++--------- .../{index.e2e.js => index.e2e.tsx} | 0 .../{index.js => index.tsx} | 0 .../ReportActionCompose.tsx | 2 +- .../SilentCommentUpdater/index.tsx | 2 +- .../ReportActionCompose/Suggestions.tsx | 4 +- 8 files changed, 65 insertions(+), 60 deletions(-) rename src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/{index.e2e.js => index.e2e.tsx} (100%) rename src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/{index.js => index.tsx} (100%) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 3c2caf020ef7..e78e0c2a4039 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -4,9 +4,8 @@ import type {BaseSyntheticEvent, ForwardedRef} from 'react'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {flushSync} from 'react-dom'; // eslint-disable-next-line no-restricted-imports -import type {DimensionValue, NativeSyntheticEvent, Text as RNText, TextInput, TextInputKeyPressEventData, TextInputProps, TextInputSelectionChangeEventData} from 'react-native'; +import type {DimensionValue, NativeSyntheticEvent, Text as RNText, TextInput, TextInputKeyPressEventData, TextInputSelectionChangeEventData} from 'react-native'; import {StyleSheet, View} from 'react-native'; -import type {AnimatedProps} from 'react-native-reanimated'; import RNTextInput from '@components/RNTextInput'; import Text from '@components/Text'; import useIsScrollBarVisible from '@hooks/useIsScrollBarVisible'; @@ -75,7 +74,7 @@ function Composer( shouldContainScroll = false, ...props }: ComposerProps, - ref: ForwardedRef>>, + ref: ForwardedRef, ) { const theme = useTheme(); const styles = useThemeStyles(); diff --git a/src/libs/updateMultilineInputRange/types.ts b/src/libs/updateMultilineInputRange/types.ts index d1b134b09a99..ce8f553c51f8 100644 --- a/src/libs/updateMultilineInputRange/types.ts +++ b/src/libs/updateMultilineInputRange/types.ts @@ -1,5 +1,5 @@ import type {TextInput} from 'react-native'; -type UpdateMultilineInputRange = (input: HTMLInputElement | TextInput, shouldAutoFocus?: boolean) => void; +type UpdateMultilineInputRange = (input: HTMLInputElement | TextInput | null, shouldAutoFocus?: boolean) => void; export default UpdateMultilineInputRange; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index bce7d80121b4..cb5f2b52ffbc 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -2,10 +2,11 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef, RefAttributes, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; -import type {MeasureInWindowOnSuccessCallback} from 'react-native'; +import type {LayoutChangeEvent, MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInput, TextInputSelectionChangeEventData} from 'react-native'; import {findNodeHandle, InteractionManager, NativeModules, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; +import type {useAnimatedRef} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; import Composer from '@components/Composer'; import useKeyboardState from '@hooks/useKeyboardState'; @@ -43,9 +44,16 @@ import type * as OnyxTypes from '@src/types/onyx'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import {defaultProps, propTypes} from './composerWithSuggestionsProps'; +type SyncSelection = { + position: number; + value: string; +}; + +type AnimatedRef = ReturnType; + type ComposerWithSuggestionsOnyxProps = { /** The number of lines the comment should take up */ - numberOfLines: number; + numberOfLines: OnyxEntry; /** The parent report actions for the report */ parentReportActions: OnyxEntry; @@ -57,8 +65,9 @@ type ComposerWithSuggestionsOnyxProps = { preferredSkinTone: number; /** Whether the input is focused */ - editFocused: boolean; + editFocused: OnyxEntry; }; + type ComposerWithSuggestionsProps = { reportID: string; report: OnyxEntry; @@ -69,7 +78,7 @@ type ComposerWithSuggestionsProps = { isComposerFullSize: boolean; isMenuVisible: boolean; inputPlaceholder: string; - displayFileInModal: (fileURL: string) => void; + displayFileInModal: (file: File | undefined) => void; textInputShouldClear: boolean; setTextInputShouldClear: (shouldClear: boolean) => void; isBlockedFromConcierge: boolean; @@ -84,8 +93,8 @@ type ComposerWithSuggestionsProps = { isScrollLikelyLayoutTriggered: RefObject; raiseIsScrollLikelyLayoutTriggered: () => void; suggestionsRef: React.RefObject; - animatedRef: RefObject; - isNextModalWillOpenRef: RefObject; + animatedRef: AnimatedRef; + isNextModalWillOpenRef: RefObject; editFocused: boolean; } & ComposerWithSuggestionsOnyxProps & Partial; @@ -178,7 +187,7 @@ function ComposerWithSuggestions( const maxComposerLines = isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES; const isEmptyChat = useMemo(() => reportActions?.length === 1, [reportActions]); - const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? '']; + const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? ''] ?? null; const shouldAutoFocus = !modal?.isVisible && (shouldFocusInputOnScreenFocus || (isEmptyChat && !ReportActionsUtils.isTransactionThread(parentReportAction))) && shouldShowComposeInput; const valueRef = useRef(value); @@ -188,14 +197,14 @@ function ComposerWithSuggestions( const [composerHeight, setComposerHeight] = useState(0); - const textInputRef = useRef(null); + const textInputRef = useRef(null); const insertedEmojisRef = useRef([]); - const syncSelectionWithOnChangeTextRef = useRef(null); + const syncSelectionWithOnChangeTextRef = useRef(null); const suggestions = suggestionsRef.current?.getSuggestions(); - const hasEnoughSpaceForLargeSuggestion = SuggestionUtils.hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, suggestions.length); + const hasEnoughSpaceForLargeSuggestion = SuggestionUtils.hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, suggestions?.length ?? 0); const isAutoSuggestionPickerLarge = !isSmallScreenWidth || (isSmallScreenWidth && hasEnoughSpaceForLargeSuggestion); @@ -212,7 +221,7 @@ function ComposerWithSuggestions( * Set the TextInput Ref */ const setTextInputRef = useCallback( - (el) => { + (el: TextInput) => { ReportActionComposeFocusManager.composerRef.current = el; textInputRef.current = el; if (typeof animatedRef === 'function') { @@ -231,7 +240,7 @@ function ComposerWithSuggestions( const debouncedSaveReportComment = useMemo( () => - _.debounce((selectedReportID, newComment) => { + lodashDebounce((selectedReportID, newComment) => { Report.saveReportComment(selectedReportID, newComment || ''); }, 1000), [], @@ -239,17 +248,14 @@ function ComposerWithSuggestions( /** * Update the value of the comment in Onyx - * - * @param {String} comment - * @param {Boolean} shouldDebounceSaveComment */ const updateComment = useCallback( - (commentValue: string, shouldDebounceSaveComment: boolean) => { + (commentValue: string | null, shouldDebounceSaveComment?: boolean) => { raiseIsScrollLikelyLayoutTriggered(); - const {text: newComment, emojis, cursorPosition} = EmojiUtils.replaceAndExtractEmojis(commentValue, preferredSkinTone, preferredLocale); - if (!_.isEmpty(emojis)) { + const {text: newComment, emojis, cursorPosition} = EmojiUtils.replaceAndExtractEmojis(commentValue ?? '', preferredSkinTone, preferredLocale); + if (emojis.length) { const newEmojis = EmojiUtils.getAddedEmojis(emojis, emojisPresentBefore.current); - if (!_.isEmpty(newEmojis)) { + if (newEmojis.length) { // Ensure emoji suggestions are hidden after inserting emoji even when the selection is not changed if (suggestionsRef.current) { suggestionsRef.current.resetSuggestions(); @@ -269,7 +275,7 @@ function ComposerWithSuggestions( emojisPresentBefore.current = emojis; setValue(newCommentConverted); if (commentValue !== newComment) { - const position = Math.max(selection.end + (newComment.length - commentRef.current.length), cursorPosition || 0); + const position = Math.max(selection.end + (newComment.length - commentRef.current.length), cursorPosition ?? 0); if (isIOSNative) { syncSelectionWithOnChangeTextRef.current = {position, value: newComment}; @@ -316,10 +322,9 @@ function ComposerWithSuggestions( /** * Update the number of lines for a comment in Onyx - * @param {Number} numberOfLines */ const updateNumberOfLines = useCallback( - (newNumberOfLines) => { + (newNumberOfLines: number) => { if (newNumberOfLines === numberOfLines) { return; } @@ -328,9 +333,6 @@ function ComposerWithSuggestions( [reportID, numberOfLines], ); - /** - * @returns {String} - */ const prepareCommentAndResetComposer = useCallback(() => { const trimmedComment = commentRef.current.trim(); const commentLength = ReportUtils.getCommentLength(trimmedComment); @@ -356,11 +358,9 @@ function ComposerWithSuggestions( /** * Callback to add whatever text is chosen into the main input (used f.e as callback for the emoji picker) - * @param {String} text - * @param {Boolean} shouldAddTrailSpace */ const replaceSelectionWithText = useCallback( - (text, shouldAddTrailSpace = true) => { + (text: string, shouldAddTrailSpace = true) => { const updatedText = shouldAddTrailSpace ? `${text} ` : text; const selectionSpaceLength = shouldAddTrailSpace ? CONST.SPACE_LENGTH : 0; updateComment(ComposerUtils.insertText(commentRef.current, selection, updatedText)); @@ -373,12 +373,12 @@ function ComposerWithSuggestions( ); const triggerHotkeyActions = useCallback( - (e) => { + (e: KeyboardEvent) => { if (!e || ComposerUtils.canSkipTriggerHotkeys(isSmallScreenWidth, isKeyboardShown)) { return; } - if (suggestionsRef.current.triggerHotkeyActions(e)) { + if (suggestionsRef.current?.triggerHotkeyActions(e)) { return; } @@ -390,14 +390,20 @@ function ComposerWithSuggestions( // Trigger the edit box for last sent message if ArrowUp is pressed and the comment is empty and Chronos is not in the participants const valueLength = valueRef.current.length; - if (e.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && textInputRef.current.selectionStart === 0 && valueLength === 0 && !ReportUtils.chatIncludesChronos(report)) { + if ( + e.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && + textInputRef.current && + 'selectionStart' in textInputRef.current && + textInputRef.current?.selectionStart === 0 && + valueLength === 0 && + !ReportUtils.chatIncludesChronos(report) + ) { e.preventDefault(); - const lastReportAction = _.find( - [...reportActions, parentReportAction], + const lastReportAction = [...(reportActions ?? []), parentReportAction].find( (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action), ); if (lastReportAction) { - Report.saveReportActionDraft(reportID, lastReportAction, _.last(lastReportAction.message).html); + Report.saveReportActionDraft(reportID, lastReportAction, lastReportAction.message?.at(-1)?.html ?? ''); } } }, @@ -405,7 +411,7 @@ function ComposerWithSuggestions( ); const onChangeText = useCallback( - (commentValue) => { + (commentValue: string) => { updateComment(commentValue, true); if (isIOSNative && syncSelectionWithOnChangeTextRef.current) { @@ -416,7 +422,7 @@ function ComposerWithSuggestions( InteractionManager.runAfterInteractions(() => { // note: this implementation is only available on non-web RN, thus the wrapping // 'if' block contains a redundant (since the ref is only used on iOS) platform check - textInputRef.current.setSelection(positionSnapshot, positionSnapshot); + textInputRef.current?.setSelection(positionSnapshot, positionSnapshot); }); } }, @@ -424,8 +430,8 @@ function ComposerWithSuggestions( ); const onSelectionChange = useCallback( - (e) => { - if (textInputRef.current && textInputRef.current.isFocused() && suggestionsRef.current.onSelectionChange(e)) { + (e: NativeSyntheticEvent) => { + if (textInputRef.current?.isFocused() && suggestionsRef.current?.onSelectionChange?.(e)) { return; } @@ -451,7 +457,7 @@ function ComposerWithSuggestions( /** * Focus the composer text input - * @param {Boolean} [shouldDelay=false] Impose delay before focusing the composer + * @param [shouldDelay=false] Impose delay before focusing the composer * @memberof ReportActionCompose */ const focus = useCallback((shouldDelay = false) => { @@ -475,12 +481,12 @@ function ComposerWithSuggestions( */ const checkComposerVisibility = useCallback(() => { // Checking whether the screen is focused or not, helps avoid `modal.isVisible` false when popups are closed, even if the modal is opened. - const isComposerCoveredUp = !isFocused || EmojiPickerActions.isEmojiPickerVisible() || isMenuVisible || modal.isVisible || modal.willAlertModalBecomeVisible; + const isComposerCoveredUp = !isFocused || EmojiPickerActions.isEmojiPickerVisible() || isMenuVisible || !!modal?.isVisible || modal?.willAlertModalBecomeVisible; return !isComposerCoveredUp; }, [isMenuVisible, modal, isFocused]); const focusComposerOnKeyPress = useCallback( - (e) => { + (e: KeyboardEvent) => { const isComposerVisible = checkComposerVisibility(); if (!isComposerVisible) { return; @@ -491,7 +497,7 @@ function ComposerWithSuggestions( } // if we're typing on another input/text area, do not focus - if (['INPUT', 'TEXTAREA'].includes(e.target.nodeName)) { + if (['INPUT', 'TEXTAREA'].includes((e.target as Element)?.nodeName)) { return; } @@ -527,17 +533,17 @@ function ComposerWithSuggestions( }; }, [focusComposerOnKeyPress, navigation, setUpComposeFocusManager]); - const prevIsModalVisible = usePrevious(modal.isVisible); + const prevIsModalVisible = usePrevious(modal?.isVisible); const prevIsFocused = usePrevious(isFocused); useEffect(() => { - if (modal.isVisible && !prevIsModalVisible) { + if (modal?.isVisible && !prevIsModalVisible) { // eslint-disable-next-line no-param-reassign isNextModalWillOpenRef.current = false; } // We want to focus or refocus the input when a modal has been closed or the underlying screen is refocused. // We avoid doing this on native platforms since the software keyboard popping // open creates a jarring and broken UX. - if (!((willBlurTextInputOnTapOutside || shouldAutoFocus) && !isNextModalWillOpenRef.current && !modal.isVisible && isFocused && (prevIsModalVisible || !prevIsFocused))) { + if (!((willBlurTextInputOnTapOutside || shouldAutoFocus) && !isNextModalWillOpenRef.current && !modal?.isVisible && isFocused && (!!prevIsModalVisible || !prevIsFocused))) { return; } @@ -546,11 +552,11 @@ function ComposerWithSuggestions( return; } focus(true); - }, [focus, prevIsFocused, editFocused, prevIsModalVisible, isFocused, modal.isVisible, isNextModalWillOpenRef, shouldAutoFocus]); + }, [focus, prevIsFocused, editFocused, prevIsModalVisible, isFocused, modal?.isVisible, isNextModalWillOpenRef, shouldAutoFocus]); useEffect(() => { // Scrolls the composer to the bottom and sets the selection to the end, so that longer drafts are easier to edit - updateMultilineInputRange(textInputRef.current, shouldAutoFocus); + updateMultilineInputRange(textInputRef.current, !!shouldAutoFocus); if (value.length === 0) { return; @@ -568,7 +574,7 @@ function ComposerWithSuggestions( focus, replaceSelectionWithText, prepareCommentAndResetComposer, - isFocused: () => textInputRef.current.isFocused(), + isFocused: () => !!textInputRef.current?.isFocused(), }), [blur, focus, prepareCommentAndResetComposer, replaceSelectionWithText], ); @@ -582,7 +588,7 @@ function ComposerWithSuggestions( { + onLayout={(e: LayoutChangeEvent) => { const composerLayoutHeight = e.nativeEvent.layout.height; if (composerHeight === composerLayoutHeight) { return; @@ -625,7 +631,7 @@ function ComposerWithSuggestions( void; onSelectionChange?: (event: NativeSyntheticEvent) => void; - triggerHotkeyActions: (event: KeyboardEvent) => void; + triggerHotkeyActions: (event: KeyboardEvent) => boolean | undefined; updateShouldShowSuggestionMenuToFalse: (shouldShowSuggestionMenu?: boolean) => void; setShouldBlockSuggestionCalc: (shouldBlock: boolean) => void; getSuggestions: () => Mention[] | Emoji[]; diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx index afee06dbc8ff..3f883d46a995 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx @@ -10,7 +10,7 @@ import type {SilentCommentUpdaterOnyxProps, SilentCommentUpdaterProps} from './t * It is connected to the actual draft comment in onyx. The comment in onyx might updates multiple times, and we want to avoid * re-rendering a UI component for that. That's why the side effect was moved down to a separate component. */ -function SilentCommentUpdater({comment = '', commentRef, report, value, updateComment}: SilentCommentUpdaterProps) { +function SilentCommentUpdater({comment, commentRef, report, value, updateComment}: SilentCommentUpdaterProps) { const prevCommentProp = usePrevious(comment); const prevReportId = usePrevious(report?.reportID); const {preferredLocale} = useLocalize(); diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.tsx b/src/pages/home/report/ReportActionCompose/Suggestions.tsx index 2500af40c7c6..0968a61c3abb 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -1,6 +1,6 @@ import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useRef} from 'react'; -import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; +import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; import {DragAndDropContext} from '@components/DragAndDrop/Provider'; import usePrevious from '@hooks/usePrevious'; @@ -19,7 +19,7 @@ type SuggestionProps = { selection: Selection; setSelection: (newSelection: Selection) => void; updateComment: (newComment: string, shouldDebounceSaveComment?: boolean) => void; - measureParentContainer: () => void; + measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; isComposerFullSize: boolean; isComposerFocused?: boolean; resetKeyboardInput?: () => void; From 35c7989f0a50998748982e6a4722ea2953616035 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 24 Jan 2024 18:41:13 +0700 Subject: [PATCH 051/213] remove cached file when delete comment --- src/ONYXKEYS.ts | 4 ++ .../AttachmentCarousel/CarouselItem.js | 1 + .../Attachments/AttachmentView/index.js | 17 +++++++- src/components/PDFView/index.native.js | 6 ++- src/libs/actions/CachedPDFPaths.ts | 39 +++++++++++++++++++ src/libs/actions/Report.ts | 2 + 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/libs/actions/CachedPDFPaths.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 9693c907a5fe..e8caede964e6 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -249,6 +249,9 @@ const ONYXKEYS = { /** Indicates whether an forced upgrade is required */ UPDATE_REQUIRED: 'updateRequired', + // Paths of PDF file that has been cached during one session + CACHED_PDF_PATHS: 'cachedPDFPaths', + /** Collection Keys */ COLLECTION: { DOWNLOAD: 'download_', @@ -446,6 +449,7 @@ type OnyxValues = { [ONYXKEYS.MAX_CANVAS_HEIGHT]: number; [ONYXKEYS.MAX_CANVAS_WIDTH]: number; [ONYXKEYS.UPDATE_REQUIRED]: boolean; + [ONYXKEYS.CACHED_PDF_PATHS]: Record; // Collections [ONYXKEYS.COLLECTION.DOWNLOAD]: OnyxTypes.Download; diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.js index 5552f15320f3..53d72be49a47 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.js @@ -110,6 +110,7 @@ function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) { carouselActiveItemIndex={activeIndex} onPress={onPress} transactionID={item.transactionID} + reportActionID={item.reportActionID} /> diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index b0060afdb813..1a6f4d92c0e8 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -15,6 +15,7 @@ import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as CachedPDFPaths from '@libs/actions/CachedPDFPaths'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import compose from '@libs/compose'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -47,6 +48,9 @@ const propTypes = { /** The id of the transaction related to the attachment */ // eslint-disable-next-line react/no-unused-prop-types transactionID: PropTypes.string, + + /** The id of the report action related to the attachment */ + reportActionID: PropTypes.string, }; const defaultProps = { @@ -57,6 +61,7 @@ const defaultProps = { containerStyles: [], isWorkspaceAvatar: false, transactionID: '', + reportActionID: '', }; function AttachmentView({ @@ -79,6 +84,7 @@ function AttachmentView({ isWorkspaceAvatar, fallbackSource, transaction, + reportActionID, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -127,6 +133,15 @@ function AttachmentView({ if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) { const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; + const onPDFLoadComplete = (path) => { + if (isUsedInCarousel && reportActionID) { + CachedPDFPaths.add(reportActionID, path); + } + if (!loadComplete) { + setLoadComplete(true); + } + }; + // We need the following View component on android native // So that the event will propagate properly and // the Password protected preview will be shown for pdf attachement we are about to send. @@ -143,7 +158,7 @@ function AttachmentView({ onPress={onPress} onScaleChanged={onScaleChanged} onToggleKeyboard={onToggleKeyboard} - onLoadComplete={() => !loadComplete && setLoadComplete(true)} + onLoadComplete={onPDFLoadComplete} errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]} style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1} isUsedInCarousel={isUsedInCarousel} diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index cbe7f0e4608e..08f2eb2c6984 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -118,14 +118,16 @@ class PDFView extends Component { /** * After the PDF is successfully loaded hide PDFPasswordForm and the loading * indicator. + * @param {Number} numberOfPages + * @param {Number} path - Path to cache location */ - finishPDFLoad() { + finishPDFLoad(numberOfPages, path) { this.setState({ shouldRequestPassword: false, shouldShowLoadingIndicator: false, successToLoadPDF: true, }); - this.props.onLoadComplete(); + this.props.onLoadComplete(path); } renderPDFView() { diff --git a/src/libs/actions/CachedPDFPaths.ts b/src/libs/actions/CachedPDFPaths.ts new file mode 100644 index 000000000000..28626dc34856 --- /dev/null +++ b/src/libs/actions/CachedPDFPaths.ts @@ -0,0 +1,39 @@ +import {exists, unlink} from 'react-native-fs'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; + +let pdfPaths: Record = {}; +Onyx.connect({ + key: ONYXKEYS.CACHED_PDF_PATHS, + callback: (val) => { + pdfPaths = val ?? {}; + }, +}); + +function add(reportActionID: string, path: string): Promise { + return Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[reportActionID]: path}); +} + +function clear(path: string): Promise { + if (!path) { + return Promise.resolve(); + } + return new Promise((resolve) => { + exists(path).then((exist) => { + if (!exist) { + resolve(); + } + return unlink(path); + }); + }); +} + +function clearByKey(reportActionID: string) { + clear(pdfPaths[reportActionID] ?? '').then(() => Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[reportActionID]: null})); +} + +function clearAll() { + Promise.all(Object.values(pdfPaths).map(clear)).then(() => Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {})); +} + +export {add, clearByKey, clearAll}; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 228b88d194ba..753831470c66 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -40,6 +40,7 @@ import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/Rep import type ReportAction from '@src/types/onyx/ReportAction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import * as CachedPDFPaths from './CachedPDFPaths'; import * as Modal from './Modal'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -1223,6 +1224,7 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { reportActionID, }; + CachedPDFPaths.clearByKey(reportActionID); API.write('DeleteComment', parameters, {optimisticData, successData, failureData}); } From 223fdd804f52f0adf9eb5891f8cd00da0dd91726 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 24 Jan 2024 12:51:51 +0100 Subject: [PATCH 052/213] fix: wip --- src/components/Composer/index.tsx | 2 +- src/components/Composer/types.ts | 2 +- .../ComposerWithSuggestions.tsx | 10 ++++++---- .../ReportActionCompose/SilentCommentUpdater/index.tsx | 7 +++---- .../ReportActionCompose/SilentCommentUpdater/types.ts | 3 --- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index e78e0c2a4039..a23a176c5bcd 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -300,7 +300,7 @@ function Composer( }, []); const handleKeyPress = useCallback( - (e: NativeSyntheticEvent) => { + (e: NativeSyntheticEvent & KeyboardEvent) => { // Prevent onKeyPress from being triggered if the Enter key is pressed while text is being composed if (!onKeyPress || isEnterWhileComposition(e as unknown as KeyboardEvent)) { return; diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index d8d88970ea78..6e486771c9aa 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -74,7 +74,7 @@ type ComposerProps = { /** Whether the sull composer is open */ isComposerFullSize?: boolean; - onKeyPress?: (event: NativeSyntheticEvent) => void; + onKeyPress?: (event: KeyboardEvent & NativeSyntheticEvent) => void; onFocus?: (event: NativeSyntheticEvent) => void; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index cb5f2b52ffbc..034affefd747 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -2,7 +2,7 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef, RefAttributes, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; -import type {LayoutChangeEvent, MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInput, TextInputSelectionChangeEventData} from 'react-native'; +import type {LayoutChangeEvent, MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInput, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native'; import {findNodeHandle, InteractionManager, NativeModules, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -73,7 +73,7 @@ type ComposerWithSuggestionsProps = { report: OnyxEntry; reportActions: OnyxTypes.ReportAction[] | undefined; onFocus: () => void; - onBlur: (event: FocusEvent) => void; + onBlur: (event: NativeSyntheticEvent) => void; onValueChange: (value: string) => void; isComposerFullSize: boolean; isMenuVisible: boolean; @@ -222,6 +222,7 @@ function ComposerWithSuggestions( */ const setTextInputRef = useCallback( (el: TextInput) => { + // @ts-expect-error need to reassign this ref ReportActionComposeFocusManager.composerRef.current = el; textInputRef.current = el; if (typeof animatedRef === 'function') { @@ -373,7 +374,7 @@ function ComposerWithSuggestions( ); const triggerHotkeyActions = useCallback( - (e: KeyboardEvent) => { + (e: KeyboardEvent & NativeSyntheticEvent) => { if (!e || ComposerUtils.canSkipTriggerHotkeys(isSmallScreenWidth, isKeyboardShown)) { return; } @@ -537,6 +538,7 @@ function ComposerWithSuggestions( const prevIsFocused = usePrevious(isFocused); useEffect(() => { if (modal?.isVisible && !prevIsModalVisible) { + // @ts-expect-error need to reassign this ref // eslint-disable-next-line no-param-reassign isNextModalWillOpenRef.current = false; } @@ -682,7 +684,7 @@ export default withOnyx `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`, canEvict: false, initWithStoredValues: false, }, diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx index 3f883d46a995..626938c235da 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx @@ -10,14 +10,14 @@ import type {SilentCommentUpdaterOnyxProps, SilentCommentUpdaterProps} from './t * It is connected to the actual draft comment in onyx. The comment in onyx might updates multiple times, and we want to avoid * re-rendering a UI component for that. That's why the side effect was moved down to a separate component. */ -function SilentCommentUpdater({comment, commentRef, report, value, updateComment}: SilentCommentUpdaterProps) { +function SilentCommentUpdater({comment = '', commentRef, report, value, updateComment}: SilentCommentUpdaterProps) { const prevCommentProp = usePrevious(comment); const prevReportId = usePrevious(report?.reportID); const {preferredLocale} = useLocalize(); const prevPreferredLocale = usePrevious(preferredLocale); useEffect(() => { - updateComment(comment); + updateComment(comment ?? null); // eslint-disable-next-line react-hooks/exhaustive-deps -- We need to run this on mount }, []); @@ -32,7 +32,7 @@ function SilentCommentUpdater({comment, commentRef, report, value, updateComment return; } - updateComment(comment); + updateComment(comment ?? null); }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, report?.reportID, updateComment, value, commentRef]); return null; @@ -43,6 +43,5 @@ SilentCommentUpdater.displayName = 'SilentCommentUpdater'; export default withOnyx({ comment: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, - initialValue: '', }, })(SilentCommentUpdater); diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts index 60924f24099b..591ee43ce6cd 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts @@ -21,9 +21,6 @@ type SilentCommentUpdaterProps = { /** The ref of the comment */ commentRef: React.RefObject; - - /** The comment of the report */ - commnet: string; } & SilentCommentUpdaterOnyxProps; export type {SilentCommentUpdaterProps, SilentCommentUpdaterOnyxProps}; From 05bb0a591c9401ae62f15833afd801237e6af3ee Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 24 Jan 2024 16:20:44 +0100 Subject: [PATCH 053/213] fix: finished migrating ComposerWithSuggestions, removed unused files, start migrating ReportTypeingIndicator --- src/components/Composer/index.tsx | 3 +- src/components/Composer/types.ts | 10 +- src/components/MentionSuggestions.tsx | 3 +- src/libs/E2E/client.ts | 3 + .../ComposerWithSuggestions.tsx | 37 ++--- .../composerWithSuggestionsProps.js | 126 ------------------ .../ComposerWithSuggestions/index.e2e.tsx | 18 +-- .../ReportActionCompose.tsx | 9 +- .../ReportActionCompose/suggestionProps.js | 39 ------ ...Indicator.js => ReportTypingIndicator.tsx} | 0 src/types/modules/react-native.d.ts | 5 - 11 files changed, 46 insertions(+), 207 deletions(-) delete mode 100644 src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/composerWithSuggestionsProps.js delete mode 100644 src/pages/home/report/ReportActionCompose/suggestionProps.js rename src/pages/home/report/{ReportTypingIndicator.js => ReportTypingIndicator.tsx} (100%) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index a23a176c5bcd..493b37ff3a31 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -300,11 +300,12 @@ function Composer( }, []); const handleKeyPress = useCallback( - (e: NativeSyntheticEvent & KeyboardEvent) => { + (e: NativeSyntheticEvent) => { // Prevent onKeyPress from being triggered if the Enter key is pressed while text is being composed if (!onKeyPress || isEnterWhileComposition(e as unknown as KeyboardEvent)) { return; } + onKeyPress(e); }, [onKeyPress], diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index 6e486771c9aa..9565eaf6208f 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -1,4 +1,4 @@ -import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, TextInputKeyPressEventData, TextInputSelectionChangeEventData, TextStyle} from 'react-native'; +import type {NativeSyntheticEvent, StyleProp, TextInputProps, TextInputSelectionChangeEventData, TextStyle} from 'react-native'; type TextSelection = { start: number; @@ -74,14 +74,8 @@ type ComposerProps = { /** Whether the sull composer is open */ isComposerFullSize?: boolean; - onKeyPress?: (event: KeyboardEvent & NativeSyntheticEvent) => void; - - onFocus?: (event: NativeSyntheticEvent) => void; - - onBlur?: (event: NativeSyntheticEvent) => void; - /** Should make the input only scroll inside the element avoid scroll out to parent */ shouldContainScroll?: boolean; -}; +} & TextInputProps; export type {TextSelection, ComposerProps}; diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index 99930f995a3a..23040a242807 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -1,4 +1,5 @@ import React, {useCallback} from 'react'; +import type {MeasureInWindowOnSuccessCallback} from 'react-native'; import {View} from 'react-native'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -43,7 +44,7 @@ type MentionSuggestionsProps = { isMentionPickerLarge: boolean; /** Measures the parent container's position and dimensions. */ - measureParentContainer: () => void; + measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; }; /** diff --git a/src/libs/E2E/client.ts b/src/libs/E2E/client.ts index 472567cc6c1d..7dc3ad4073b9 100644 --- a/src/libs/E2E/client.ts +++ b/src/libs/E2E/client.ts @@ -11,6 +11,9 @@ type TestResult = { type TestConfig = { name: string; + reportScreen?: { + autoFocus?: boolean; + }; }; type NativeCommandPayload = { diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 034affefd747..c02bda08cac7 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -2,7 +2,15 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef, RefAttributes, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; -import type {LayoutChangeEvent, MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInput, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native'; +import type { + LayoutChangeEvent, + MeasureInWindowOnSuccessCallback, + NativeSyntheticEvent, + TextInput, + TextInputFocusEventData, + TextInputKeyPressEventData, + TextInputSelectionChangeEventData, +} from 'react-native'; import {findNodeHandle, InteractionManager, NativeModules, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -42,7 +50,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import {defaultProps, propTypes} from './composerWithSuggestionsProps'; type SyncSelection = { position: number; @@ -126,7 +133,7 @@ function ComposerWithSuggestions( { // Onyx modal, - preferredSkinTone, + preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, parentReportActions, numberOfLines, @@ -152,7 +159,7 @@ function ComposerWithSuggestions( setIsCommentEmpty, handleSendMessage, shouldShowComposeInput, - measureParentContainer, + measureParentContainer = () => {}, listHeight, isScrollLikelyLayoutTriggered, raiseIsScrollLikelyLayoutTriggered, @@ -164,7 +171,7 @@ function ComposerWithSuggestions( // For testing children, }: ComposerWithSuggestionsProps, - ref: ForwardedRef, + ref: ForwardedRef, ) { const {isKeyboardShown} = useKeyboardState(); const theme = useTheme(); @@ -374,32 +381,33 @@ function ComposerWithSuggestions( ); const triggerHotkeyActions = useCallback( - (e: KeyboardEvent & NativeSyntheticEvent) => { - if (!e || ComposerUtils.canSkipTriggerHotkeys(isSmallScreenWidth, isKeyboardShown)) { + (event: NativeSyntheticEvent) => { + const webEvent = event as unknown as KeyboardEvent; + if (!webEvent || ComposerUtils.canSkipTriggerHotkeys(isSmallScreenWidth, isKeyboardShown)) { return; } - if (suggestionsRef.current?.triggerHotkeyActions(e)) { + if (suggestionsRef.current?.triggerHotkeyActions(webEvent)) { return; } // Submit the form when Enter is pressed - if (e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey && !e.shiftKey) { - e.preventDefault(); + if (webEvent.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey && !webEvent.shiftKey) { + webEvent.preventDefault(); handleSendMessage(); } // Trigger the edit box for last sent message if ArrowUp is pressed and the comment is empty and Chronos is not in the participants const valueLength = valueRef.current.length; if ( - e.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && + webEvent.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && textInputRef.current && 'selectionStart' in textInputRef.current && textInputRef.current?.selectionStart === 0 && valueLength === 0 && !ReportUtils.chatIncludesChronos(report) ) { - e.preventDefault(); + webEvent.preventDefault(); const lastReportAction = [...(reportActions ?? []), parentReportAction].find( (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action), ); @@ -568,7 +576,6 @@ function ComposerWithSuggestions( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useImperativeHandle( ref, () => ({ @@ -660,8 +667,6 @@ function ComposerWithSuggestions( ); } -ComposerWithSuggestions.propTypes = propTypes; -ComposerWithSuggestions.defaultProps = defaultProps; ComposerWithSuggestions.displayName = 'ComposerWithSuggestions'; const ComposerWithSuggestionsWithRef = forwardRef(ComposerWithSuggestions); @@ -689,3 +694,5 @@ export default withOnyx {}, -}; - -export {propTypes, defaultProps}; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx index cbbd1758c9cb..87352981ba9a 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx @@ -1,6 +1,8 @@ -import _ from 'lodash'; -import React, {useEffect} from 'react'; +import type {ForwardedRef, RefObject} from 'react'; +import React, {forwardRef, useEffect} from 'react'; import E2EClient from '@libs/E2E/client'; +import type {ComposerRef} from '@pages/home/report/ReportActionCompose/ReportActionCompose'; +import type {ComposerWithSuggestionsProps} from './ComposerWithSuggestions'; import ComposerWithSuggestions from './ComposerWithSuggestions'; let rerenderCount = 0; @@ -14,20 +16,20 @@ function IncrementRenderCount() { return null; } -const ComposerWithSuggestionsE2e = React.forwardRef((props, ref) => { +function ComposerWithSuggestionsE2e(props: ComposerWithSuggestionsProps, ref: ForwardedRef) { // Eventually Auto focus on e2e tests useEffect(() => { - if (_.get(E2EClient.getCurrentActiveTestConfig(), 'reportScreen.autoFocus', false) === false) { + if ((E2EClient.getCurrentActiveTestConfig()?.reportScreen?.autoFocus ?? false) === false) { return; } // We need to wait for the component to be mounted before focusing setTimeout(() => { - if (!ref || !ref.current) { + if (!(ref as RefObject)?.current) { return; } - ref.current.focus(true); + (ref as RefObject).current?.focus(true); }, 1); }, [ref]); @@ -44,9 +46,9 @@ const ComposerWithSuggestionsE2e = React.forwardRef((props, ref) => { ); -}); +} ComposerWithSuggestionsE2e.displayName = 'ComposerWithSuggestionsE2e'; -export default ComposerWithSuggestionsE2e; +export default forwardRef(ComposerWithSuggestionsE2e); export {getRerenderCount, resetRerenderCount}; diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index d7e02fe29fcd..70c5f61d7725 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -1,7 +1,7 @@ import {PortalHost} from '@gorhom/portal'; import type {SyntheticEvent} from 'react'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; +import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -123,7 +123,7 @@ function ReportActionCompose({ const {isMediumScreenWidth, isSmallScreenWidth} = useWindowDimensions(); const {isOffline} = useNetwork(); const animatedRef = useAnimatedRef(); - const actionButtonRef = useRef(null); + const actionButtonRef = useRef(null); const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; /** * Updates the Highlight state of the composer @@ -300,12 +300,13 @@ function ReportActionCompose({ isKeyboardVisibleWhenShowingModalRef.current = true; }, []); - const onBlur = useCallback((event: FocusEvent) => { + const onBlur = useCallback((event: NativeSyntheticEvent) => { + const webEvent = event as unknown as FocusEvent; setIsFocused(false); if (suggestionsRef.current) { suggestionsRef.current.resetSuggestions(); } - if (event.relatedTarget && event.relatedTarget === actionButtonRef.current) { + if (webEvent.relatedTarget && webEvent.relatedTarget === actionButtonRef.current) { isKeyboardVisibleWhenShowingModalRef.current = true; } }, []); diff --git a/src/pages/home/report/ReportActionCompose/suggestionProps.js b/src/pages/home/report/ReportActionCompose/suggestionProps.js deleted file mode 100644 index 62c29f3d418e..000000000000 --- a/src/pages/home/report/ReportActionCompose/suggestionProps.js +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types'; - -const baseProps = { - /** The current input value */ - value: PropTypes.string.isRequired, - - /** Callback to update the current input value */ - setValue: PropTypes.func.isRequired, - - /** The current selection value */ - selection: PropTypes.shape({ - start: PropTypes.number.isRequired, - end: PropTypes.number.isRequired, - }).isRequired, - - /** Callback to update the current selection */ - setSelection: PropTypes.func.isRequired, - - /** Whether the composer is expanded */ - isComposerFullSize: PropTypes.bool.isRequired, - - /** Callback to update the comment draft */ - updateComment: PropTypes.func.isRequired, - - /** Meaures the parent container's position and dimensions. */ - measureParentContainer: PropTypes.func.isRequired, - - /** Report composer focus state */ - isComposerFocused: PropTypes.bool, -}; - -const implementationBaseProps = { - /** Whether to use the small or the big suggestion picker */ - isAutoSuggestionPickerLarge: PropTypes.bool.isRequired, - - ...baseProps, -}; - -export {baseProps, implementationBaseProps}; diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.tsx similarity index 100% rename from src/pages/home/report/ReportTypingIndicator.js rename to src/pages/home/report/ReportTypingIndicator.tsx diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index aaa7058737ae..7313a28984fc 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -279,14 +279,9 @@ declare module 'react-native' { * Extracted from react-native-web, packages/react-native-web/src/exports/TextInput/types.js */ interface WebTextInputProps extends WebSharedProps { - dir?: 'auto' | 'ltr' | 'rtl'; disabled?: boolean; - enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; - readOnly?: boolean; } interface TextInputProps extends WebTextInputProps { - // TODO: remove once the app is updated to RN 0.73 - smartInsertDelete?: boolean; isFullComposerAvailable?: boolean; } From cf84b3b817a85d6baa5c28e2913068e2d487b260 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 24 Jan 2024 17:47:16 +0100 Subject: [PATCH 054/213] fix: move ReportTypingIndicator to TS --- .../home/report/ReportTypingIndicator.tsx | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/pages/home/report/ReportTypingIndicator.tsx b/src/pages/home/report/ReportTypingIndicator.tsx index 785f1e3f6a1e..f7f135e7374e 100755 --- a/src/pages/home/report/ReportTypingIndicator.tsx +++ b/src/pages/home/report/ReportTypingIndicator.tsx @@ -1,7 +1,6 @@ -import PropTypes from 'prop-types'; import React, {useMemo} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import Text from '@components/Text'; import TextWithEllipsis from '@components/TextWithEllipsis'; import useLocalize from '@hooks/useLocalize'; @@ -9,28 +8,30 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ReportUtils from '@libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {ReportUserIsTyping} from '@src/types/onyx'; -const propTypes = { +type ReportTypingIndicatorOnyxProps = { /** Key-value pairs of user accountIDs/logins and whether or not they are typing. Keys are accountIDs or logins. */ - userTypingStatuses: PropTypes.objectOf(PropTypes.bool), + userTypingStatuses: OnyxEntry; }; -const defaultProps = { - userTypingStatuses: {}, -}; +type ReportTypingIndicatorProps = { + // eslint-disable-next-line react/no-unused-prop-types + reportID: string; +} & ReportTypingIndicatorOnyxProps; -function ReportTypingIndicator({userTypingStatuses}) { +function ReportTypingIndicator({userTypingStatuses}: ReportTypingIndicatorProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); const styles = useThemeStyles(); - const usersTyping = useMemo(() => _.filter(_.keys(userTypingStatuses), (loginOrAccountID) => userTypingStatuses[loginOrAccountID]), [userTypingStatuses]); + const usersTyping = useMemo(() => Object.keys(userTypingStatuses ?? {}).filter((loginOrAccountID) => userTypingStatuses?.[loginOrAccountID]), [userTypingStatuses]); const firstUserTyping = usersTyping[0]; const isUserTypingADisplayName = Number.isNaN(Number(firstUserTyping)); // If we are offline, the user typing statuses are not up-to-date so do not show them - if (isOffline || !firstUserTyping) { + if (!!isOffline || !firstUserTyping) { return null; } @@ -40,6 +41,7 @@ function ReportTypingIndicator({userTypingStatuses}) { if (usersTyping.length === 1) { return ( ({ userTypingStatuses: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, - initialValue: {}, }, })(ReportTypingIndicator); From 87f22f7e925b169a76d74e9a3eed6643283306c9 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 24 Jan 2024 18:26:51 +0100 Subject: [PATCH 055/213] fix: migrate ParticipanLocalTime and ReportDropUI --- ...tLocalTime.js => ParticipantLocalTime.tsx} | 35 ++++++++++--------- .../{ReportDropUI.js => ReportDropUI.tsx} | 9 ++--- .../home/report/ReportTypingIndicator.tsx | 2 +- src/types/onyx/IOU.ts | 2 ++ 4 files changed, 25 insertions(+), 23 deletions(-) rename src/pages/home/report/{ParticipantLocalTime.js => ParticipantLocalTime.tsx} (66%) rename src/pages/home/report/{ReportDropUI.js => ReportDropUI.tsx} (87%) diff --git a/src/pages/home/report/ParticipantLocalTime.js b/src/pages/home/report/ParticipantLocalTime.tsx similarity index 66% rename from src/pages/home/report/ParticipantLocalTime.js rename to src/pages/home/report/ParticipantLocalTime.tsx index 1992953c959e..c8bbaf031e32 100644 --- a/src/pages/home/report/ParticipantLocalTime.js +++ b/src/pages/home/report/ParticipantLocalTime.tsx @@ -1,36 +1,39 @@ -import lodashGet from 'lodash/get'; import React, {useEffect, useState} from 'react'; import {View} from 'react-native'; -import participantPropTypes from '@components/participantPropTypes'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import Timers from '@libs/Timers'; import CONST from '@src/CONST'; +import type {PersonalDetails} from '@src/types/onyx'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; -const propTypes = { +type Locales = DeepValueOf; + +type ParticipantLocalTimeProps = { /** Personal details of the participant */ - participant: participantPropTypes.isRequired, + participant: PersonalDetails; - ...withLocalizePropTypes, + preferredLocale: Locales; }; -function getParticipantLocalTime(participant, preferredLocale) { - const reportRecipientTimezone = lodashGet(participant, 'timezone', CONST.DEFAULT_TIME_ZONE); - const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale, null, reportRecipientTimezone.selected); +function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: Locales) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const reportRecipientTimezone = participant.timezone || CONST.DEFAULT_TIME_ZONE; + const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale, undefined, reportRecipientTimezone.selected); const currentTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale); - const reportRecipientDay = DateUtils.formatToDayOfWeek(reportTimezone); - const currentUserDay = DateUtils.formatToDayOfWeek(currentTimezone); + const reportRecipientDay = DateUtils.formatToDayOfWeek(reportTimezone.toDateString()); + const currentUserDay = DateUtils.formatToDayOfWeek(currentTimezone.toDateString()); if (reportRecipientDay !== currentUserDay) { return `${DateUtils.formatToLocalTime(reportTimezone)} ${reportRecipientDay}`; } return `${DateUtils.formatToLocalTime(reportTimezone)}`; } -function ParticipantLocalTime(props) { +function ParticipantLocalTime({participant, preferredLocale}: ParticipantLocalTimeProps) { + const {translate} = useLocalize(); const styles = useThemeStyles(); - const {participant, preferredLocale, translate} = props; const [localTime, setLocalTime] = useState(() => getParticipantLocalTime(participant, preferredLocale)); useEffect(() => { @@ -44,7 +47,8 @@ function ParticipantLocalTime(props) { }; }, [participant, preferredLocale]); - const reportRecipientDisplayName = lodashGet(props, 'participant.firstName') || lodashGet(props, 'participant.displayName'); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const reportRecipientDisplayName = participant.firstName || participant.displayName; if (!reportRecipientDisplayName) { return null; @@ -65,7 +69,6 @@ function ParticipantLocalTime(props) { ); } -ParticipantLocalTime.propTypes = propTypes; ParticipantLocalTime.displayName = 'ParticipantLocalTime'; -export default withLocalize(ParticipantLocalTime); +export default ParticipantLocalTime; diff --git a/src/pages/home/report/ReportDropUI.js b/src/pages/home/report/ReportDropUI.tsx similarity index 87% rename from src/pages/home/report/ReportDropUI.js rename to src/pages/home/report/ReportDropUI.tsx index c1c3b8e506ab..d147d0c0c03d 100644 --- a/src/pages/home/report/ReportDropUI.js +++ b/src/pages/home/report/ReportDropUI.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import DragAndDropConsumer from '@components/DragAndDrop/Consumer'; @@ -8,12 +7,11 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -const propTypes = { +type ReportDropUIProps = { /** Callback to execute when a file is dropped. */ - onDrop: PropTypes.func.isRequired, + onDrop: () => void; }; - -function ReportDropUI({onDrop}) { +function ReportDropUI({onDrop}: ReportDropUIProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); return ( @@ -33,6 +31,5 @@ function ReportDropUI({onDrop}) { } ReportDropUI.displayName = 'ReportDropUI'; -ReportDropUI.propTypes = propTypes; export default ReportDropUI; diff --git a/src/pages/home/report/ReportTypingIndicator.tsx b/src/pages/home/report/ReportTypingIndicator.tsx index f7f135e7374e..f62db8bf0337 100755 --- a/src/pages/home/report/ReportTypingIndicator.tsx +++ b/src/pages/home/report/ReportTypingIndicator.tsx @@ -16,7 +16,7 @@ type ReportTypingIndicatorOnyxProps = { }; type ReportTypingIndicatorProps = { - // eslint-disable-next-line react/no-unused-prop-types + // eslint-disable-next-line react/no-unused-prop-types -- This is used by withOnyx reportID: string; } & ReportTypingIndicatorOnyxProps; diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index a89b0d4530ef..220af7005c45 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -23,3 +23,5 @@ type IOU = { }; export default IOU; + +export type {Participant}; From 63a63ad6df7a87ceeb99de66e8316200d30721cf Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 25 Jan 2024 09:36:42 +0100 Subject: [PATCH 056/213] fix: typecheck --- src/components/LocaleContextProvider.tsx | 2 +- src/pages/home/report/ParticipantLocalTime.tsx | 12 +++++------- src/pages/home/report/ReportDropUI.tsx | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/LocaleContextProvider.tsx b/src/components/LocaleContextProvider.tsx index 7313bb4aa7bb..25b468181b87 100644 --- a/src/components/LocaleContextProvider.tsx +++ b/src/components/LocaleContextProvider.tsx @@ -132,4 +132,4 @@ Provider.displayName = 'withOnyx(LocaleContextProvider)'; export {Provider as LocaleContextProvider, LocaleContext}; -export type {LocaleContextProps}; +export type {LocaleContextProps, Locale}; diff --git a/src/pages/home/report/ParticipantLocalTime.tsx b/src/pages/home/report/ParticipantLocalTime.tsx index c8bbaf031e32..5d90659fb96a 100644 --- a/src/pages/home/report/ParticipantLocalTime.tsx +++ b/src/pages/home/report/ParticipantLocalTime.tsx @@ -5,24 +5,22 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import Timers from '@libs/Timers'; +import type {Locale} from '@src/components/LocaleContextProvider'; import CONST from '@src/CONST'; import type {PersonalDetails} from '@src/types/onyx'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; - -type Locales = DeepValueOf; type ParticipantLocalTimeProps = { /** Personal details of the participant */ participant: PersonalDetails; - preferredLocale: Locales; + preferredLocale?: Locale; }; -function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: Locales) { +function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: Locale | undefined) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const reportRecipientTimezone = participant.timezone || CONST.DEFAULT_TIME_ZONE; - const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale, undefined, reportRecipientTimezone.selected); - const currentTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale); + const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT, undefined, reportRecipientTimezone.selected); + const currentTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT); const reportRecipientDay = DateUtils.formatToDayOfWeek(reportTimezone.toDateString()); const currentUserDay = DateUtils.formatToDayOfWeek(currentTimezone.toDateString()); if (reportRecipientDay !== currentUserDay) { diff --git a/src/pages/home/report/ReportDropUI.tsx b/src/pages/home/report/ReportDropUI.tsx index d147d0c0c03d..fad58d60bbfa 100644 --- a/src/pages/home/report/ReportDropUI.tsx +++ b/src/pages/home/report/ReportDropUI.tsx @@ -9,7 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; type ReportDropUIProps = { /** Callback to execute when a file is dropped. */ - onDrop: () => void; + onDrop: (event: DragEvent) => void; }; function ReportDropUI({onDrop}: ReportDropUIProps) { const styles = useThemeStyles(); From 1225d237f1123cb89b8e29cdc36a022042e1ad1e Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 25 Jan 2024 13:47:27 +0100 Subject: [PATCH 057/213] fix: minor fixes --- src/pages/home/report/ParticipantLocalTime.tsx | 5 +++-- .../ComposerWithSuggestions/ComposerWithSuggestions.tsx | 2 +- src/pages/home/report/ReportActionCompose/SendButton.tsx | 3 ++- .../home/report/ReportActionCompose/SuggestionMention.tsx | 6 ++++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pages/home/report/ParticipantLocalTime.tsx b/src/pages/home/report/ParticipantLocalTime.tsx index 5d90659fb96a..e97fad8f20f0 100644 --- a/src/pages/home/report/ParticipantLocalTime.tsx +++ b/src/pages/home/report/ParticipantLocalTime.tsx @@ -13,11 +13,12 @@ type ParticipantLocalTimeProps = { /** Personal details of the participant */ participant: PersonalDetails; + /** The user's preferred locale e.g. 'en', 'es-ES' */ preferredLocale?: Locale; }; function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: Locale | undefined) { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null const reportRecipientTimezone = participant.timezone || CONST.DEFAULT_TIME_ZONE; const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT, undefined, reportRecipientTimezone.selected); const currentTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT); @@ -45,7 +46,7 @@ function ParticipantLocalTime({participant, preferredLocale}: ParticipantLocalTi }; }, [participant, preferredLocale]); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null const reportRecipientDisplayName = participant.firstName || participant.displayName; if (!reportRecipientDisplayName) { diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 8c09f53d3b27..96d2e1a26f6b 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -209,7 +209,7 @@ function ComposerWithSuggestions( const syncSelectionWithOnChangeTextRef = useRef(null); - const suggestions = suggestionsRef.current?.getSuggestions(); + const suggestions = suggestionsRef.current?.getSuggestions() ?? (() => []); const hasEnoughSpaceForLargeSuggestion = SuggestionUtils.hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, suggestions?.length ?? 0); diff --git a/src/pages/home/report/ReportActionCompose/SendButton.tsx b/src/pages/home/report/ReportActionCompose/SendButton.tsx index b6c6200fc7c0..453ee9310f46 100644 --- a/src/pages/home/report/ReportActionCompose/SendButton.tsx +++ b/src/pages/home/report/ReportActionCompose/SendButton.tsx @@ -24,7 +24,8 @@ function SendButton({isDisabled: isDisabledProp, handleSendMessage}: SendButtonP const {translate} = useLocalize(); const Tap = Gesture.Tap() - .enabled(!isDisabledProp) + // @ts-expect-error Enabled require argument but when passing something button is not working + .enabled() .onEnd(() => { handleSendMessage(); }); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index d5a3a8983467..02bcd27093e7 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -145,8 +145,10 @@ function SuggestionMention( }); const sortedPersonalDetails = filteredPersonalDetails.sort((a, b) => { - const nameA = a?.displayName ?? a?.login ?? ''; - const nameB = b?.displayName ?? b?.login ?? ''; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null + const nameA = a?.displayName || a?.login || ''; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null + const nameB = b?.displayName || b?.login || ''; if (nameA < nameB) { return -1; From 4f907d25135d3bdc12e7a5b7971e1d71c69b558a Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 25 Jan 2024 13:55:20 +0100 Subject: [PATCH 058/213] fix: removed unused export --- src/types/onyx/IOU.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 220af7005c45..a89b0d4530ef 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -23,5 +23,3 @@ type IOU = { }; export default IOU; - -export type {Participant}; From 6981075dff4b466e784ee280878d6b39eb35a1c4 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 26 Jan 2024 16:07:20 +0700 Subject: [PATCH 059/213] resolve comments --- src/libs/actions/CachedPDFPaths.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libs/actions/CachedPDFPaths.ts b/src/libs/actions/CachedPDFPaths.ts index 28626dc34856..4eefb9a7982e 100644 --- a/src/libs/actions/CachedPDFPaths.ts +++ b/src/libs/actions/CachedPDFPaths.ts @@ -2,6 +2,10 @@ import {exists, unlink} from 'react-native-fs'; import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; +/* + * We need to save the paths of PDF files so we can delete them later. + * This is to remove the cached PDFs when an attachment is deleted or the user logs out. + */ let pdfPaths: Record = {}; Onyx.connect({ key: ONYXKEYS.CACHED_PDF_PATHS, @@ -11,6 +15,9 @@ Onyx.connect({ }); function add(reportActionID: string, path: string): Promise { + if (pdfPaths[reportActionID]) { + return Promise.resolve(); + } return Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[reportActionID]: path}); } From 5c5b0ab9df2e4193c11e9218de31ce087c894360 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 29 Jan 2024 11:07:00 +0700 Subject: [PATCH 060/213] change params order --- src/libs/actions/IOU.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 2de0ccef71b0..4b90d5fd33d4 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -377,7 +377,7 @@ function buildOnyxDataForMoneyRequest( needsToBeManuallySubmitted = true, ) { const isScanRequest = TransactionUtils.isScanRequest(transaction); - const outstandingChildRequest = getOutstandingChildRequest(needsToBeManuallySubmitted, policy, iouReport); + const outstandingChildRequest = getOutstandingChildRequest(policy, iouReport, needsToBeManuallySubmitted); const optimisticData = [ { // Use SET for new reports because it doesn't exist yet, is faster and we need the data to be available when we navigate to the chat page From 56824378197d9893f86bab6568db2eb70d58ed2d Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 30 Jan 2024 01:31:35 +0700 Subject: [PATCH 061/213] fix variable cap --- src/components/CustomStatusBarAndBackground/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx index fa1ab2362151..33a9003d1f64 100644 --- a/src/components/CustomStatusBarAndBackground/index.tsx +++ b/src/components/CustomStatusBarAndBackground/index.tsx @@ -60,9 +60,9 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack // Updates the status bar style and background color depending on the current route and theme // This callback is triggered everytime the route changes or the theme changes const updateStatusBarStyle = useCallback( - (listenerId?: number) => { + (listenerID?: number) => { // Check if this function is either called through the current navigation listener or the general useEffect which listens for theme changes. - if (listenerId !== undefined && listenerId !== listenerCount.current) { + if (listenerID !== undefined && listenerID !== listenerCount.current) { return; } @@ -125,8 +125,8 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack // Add navigation state listeners to update the status bar every time the route changes // We have to pass a count as the listener id, because "react-navigation" somehow doesn't remove listeners properly - const listenerId = ++listenerCount.current; - const listener = () => updateStatusBarStyle(listenerId); + const listenerID = ++listenerCount.current; + const listener = () => updateStatusBarStyle(listenerID); navigationRef.addListener('state', listener); return () => navigationRef.removeListener('state', listener); From 0229c977b32f2f6b1c054098ae8cb8308ce470bd Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 30 Jan 2024 10:52:31 +0700 Subject: [PATCH 062/213] fix conflict main --- src/pages/ProfilePage.js | 47 ++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 590a4635f9e5..f20055190e3e 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -5,7 +5,6 @@ import React, {useEffect} from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import AttachmentModal from '@components/AttachmentModal'; import AutoUpdateTime from '@components/AutoUpdateTime'; import Avatar from '@components/Avatar'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -101,7 +100,6 @@ function ProfilePage(props) { const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(details); const avatar = lodashGet(details, 'avatar', UserUtils.getDefaultAvatar()); const fallbackIcon = lodashGet(details, 'fallbackIcon', ''); - const originalFileName = lodashGet(details, 'originalFileName', ''); const login = lodashGet(details, 'login', ''); const timezone = lodashGet(details, 'timezone', {}); @@ -122,6 +120,9 @@ function ProfilePage(props) { const hasMinimumDetails = !_.isEmpty(details.avatar); const isLoading = lodashGet(details, 'isLoading', false) || _.isEmpty(details); + // If the API returns an error for some reason there won't be any details and isLoading will get set to false, so we want to show a blocking screen + const shouldShowBlockingView = !hasMinimumDetails && !isLoading; + const statusEmojiCode = lodashGet(details, 'status.emojiCode', ''); const statusText = lodashGet(details, 'status.text', ''); const hasStatus = !!statusEmojiCode; @@ -141,7 +142,7 @@ function ProfilePage(props) { return ( - + Navigation.goBack(navigateBackTo)} @@ -150,32 +151,22 @@ function ProfilePage(props) { {hasMinimumDetails && ( - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} + accessibilityLabel={props.translate('common.profile')} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} > - {({show}) => ( - - - - - - )} - + + + + {Boolean(displayName) && ( Date: Tue, 30 Jan 2024 10:57:10 +0100 Subject: [PATCH 063/213] fix: typecheck --- .../MoneyRequestConfirmationList.tsx | 39 ++++++++++++++----- src/libs/OptionsListUtils.ts | 19 +++++++-- src/types/onyx/index.ts | 3 +- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 3712e928a5e1..3a6dd5964741 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -28,7 +28,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import type {PaymentType} from '@src/types/onyx/OriginalMessage'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type {MileageRate} from '@src/types/onyx/Policy'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import ConfirmedRoute from './ConfirmedRoute'; @@ -36,6 +36,7 @@ import FormHelpMessage from './FormHelpMessage'; import Image from './Image'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import OptionsSelector from './OptionsSelector'; +import ReceiptEmptyState from './ReceiptEmptyState'; import SettlementButton from './SettlementButton'; import ShowMoreButton from './ShowMoreButton'; import Switch from './Switch'; @@ -78,7 +79,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & onConfirm?: (selectedParticipants: Participant[]) => void; /** Callback to parent modal to send money */ - onSendMoney?: (paymentMethod: PaymentType) => void; + onSendMoney?: (paymentMethod: PaymentMethodType) => void; /** Callback to inform a participant is selected */ onSelectParticipant?: (option: Participant) => void; @@ -232,7 +233,7 @@ function MoneyRequestConfirmationList({ const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(policyCategories ?? {})); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -249,7 +250,7 @@ function MoneyRequestConfirmationList({ const policyTagList = policyTag?.tags ?? {}; const policyTagListName = policyTag?.name ?? translate('common.tag'); // A flag for showing the tags field - const shouldShowTags = isPolicyExpenseChat && (iouTag || OptionsListUtils.hasEnabledOptions(Object.values(policyTagList))); + const shouldShowTags = isPolicyExpenseChat && (iouTag || OptionsListUtils.hasEnabledOptions(policyTagList)); // A flag for showing tax fields - tax rate and tax amount const shouldShowTax = isPolicyExpenseChat && policy?.isTaxTrackingEnabled; @@ -322,8 +323,7 @@ function MoneyRequestConfirmationList({ return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( participantsList, calculatedIouAmount > 0 ? CurrencyUtils.convertToDisplayString(calculatedIouAmount, iouCurrencyCode) : '', - // TODO: Remove the assertion once OptionsListUtils (https://github.com/Expensify/App/issues/24921) is migrated to TypeScript. - ) as Participant[]; + ); }, [iouAmount, iouCurrencyCode], ); @@ -467,7 +467,7 @@ function MoneyRequestConfirmationList({ }; const confirm = useCallback( - (paymentMethod: PaymentType) => { + (paymentMethod: PaymentMethodType) => { if (selectedParticipantsMemo.length === 0) { return; } @@ -552,7 +552,7 @@ function MoneyRequestConfirmationList({ pressOnEnter isDisabled={shouldDisableButton} // eslint-disable-next-line @typescript-eslint/naming-convention - onPress={(_, value) => confirm(value as PaymentType)} + onPress={(_, value) => confirm(value as PaymentMethodType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} /> @@ -612,7 +612,7 @@ function MoneyRequestConfirmationList({ )} - {(receiptData.image || receiptData.thumbnail) && ( + {receiptData.image || receiptData.thumbnail ? ( + ) : ( + // The empty receipt component should only show for IOU Requests of a paid policy ("Team" or "Corporate") + PolicyUtils.isPaidGroupPolicy(policy) && + !isDistanceRequest && + iouType === CONST.IOU.TYPE.REQUEST && ( + + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) + } + /> + ) )} {shouldShowSmartScanFields && ( { diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 812ebb051624..a1714629c21c 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -10,7 +10,20 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, PolicyCategories, Report, ReportAction, ReportActions, Transaction, TransactionViolation} from '@src/types/onyx'; +import type { + Beta, + Login, + PersonalDetails, + PersonalDetailsList, + Policy, + PolicyCategories, + PolicyTags, + Report, + ReportAction, + ReportActions, + Transaction, + TransactionViolation, +} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {PolicyTaxRate, PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; @@ -768,7 +781,7 @@ function getEnabledCategoriesCount(options: PolicyCategories): number { /** * Verifies that there is at least one enabled option */ -function hasEnabledOptions(options: PolicyCategories): boolean { +function hasEnabledOptions(options: PolicyCategories | PolicyTags): boolean { return Object.values(options).some((option) => option.enabled); } @@ -1699,7 +1712,7 @@ function getSearchOptions(reports: Record, personalDetails: Onyx /** * Build the IOUConfirmation options for showing the payee personalDetail */ -function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails, amountText: string): PayeePersonalDetails { +function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails | EmptyObject, amountText: string): PayeePersonalDetails { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login ?? ''); return { text: PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, formattedLogin), diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 011242b2ba38..81bca425f78e 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -33,8 +33,7 @@ import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; import type {PolicyTag, PolicyTags} from './PolicyTag'; -import type {PolicyTaxRates} from './PolicyTaxRates'; -import type PolicyTaxRate from './PolicyTaxRates'; +import type {PolicyTaxRate, PolicyTaxRates} from './PolicyTaxRates'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; From 2c030a3d49b2fe430e61eeb4461567db56353575 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 30 Jan 2024 11:02:40 +0100 Subject: [PATCH 064/213] fix: remove unnecessary default values --- src/components/MoneyRequestConfirmationList.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 3a6dd5964741..74ac5e878395 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -187,12 +187,12 @@ function MoneyRequestConfirmationList({ payeePersonalDetails, canModifyParticipants = false, isReadOnly = false, - bankAccountRoute = '', - policyID = '', - reportID = '', - receiptPath = '', - receiptFilename = '', - transactionID = '', + bankAccountRoute, + policyID, + reportID, + receiptPath, + receiptFilename, + transactionID, mileageRate, isDistanceRequest = false, isScanRequest = false, From efa41789b32f559b5618d316be79d8b1398e53e4 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 30 Jan 2024 11:16:15 +0100 Subject: [PATCH 065/213] fix: typecheck --- src/libs/ComposerUtils/index.ts | 4 ++-- src/libs/EmojiUtils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index 94bba5d0d00c..de937c0ba915 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -18,8 +18,8 @@ function insertText(text: string, selection: Selection, textToInsert: string): s * Insert a white space at given index of text * @param text - text that needs whitespace to be appended to */ -function insertWhiteSpaceAtIndex(text: string, index: number) { - return `${text.slice(0, index)} ${text.slice(index)}`; +function insertWhiteSpaceAtIndex(text: string | null, index: number) { + return `${text?.slice(0, index)} ${text?.slice(index)}`; } /** diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 7971e6147c19..ecbed9c87eff 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -370,7 +370,7 @@ function replaceEmojis(text: string, preferredSkinTone: number = CONST.EMOJI_DEF /** * Find all emojis in a text and replace them with their code. */ -function replaceAndExtractEmojis(text: string, preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE, lang: Locale = CONST.LOCALES.DEFAULT): ReplacedEmoji { +function replaceAndExtractEmojis(text: string | null, preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE, lang: Locale = CONST.LOCALES.DEFAULT): ReplacedEmoji { const {text: convertedText = '', emojis = [], cursorPosition} = replaceEmojis(text, preferredSkinTone, lang); return { From 99a4110c3e4dde03061ee0872b51949b4caec0f8 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 30 Jan 2024 11:58:11 +0100 Subject: [PATCH 066/213] fix: typecheck --- src/libs/E2E/tests/chatOpeningTest.e2e.ts | 3 ++- src/libs/E2E/tests/reportTypingTest.e2e.ts | 3 ++- src/libs/E2E/types.ts | 2 +- src/libs/EmojiUtils.ts | 4 ++-- .../ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx | 3 ++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libs/E2E/tests/chatOpeningTest.e2e.ts b/src/libs/E2E/tests/chatOpeningTest.e2e.ts index ef380f847c3f..2e2ce76b348d 100644 --- a/src/libs/E2E/tests/chatOpeningTest.e2e.ts +++ b/src/libs/E2E/tests/chatOpeningTest.e2e.ts @@ -1,3 +1,4 @@ +import type {NativeConfig} from 'react-native-config'; import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; @@ -12,7 +13,7 @@ const test = (config: TestConfig) => { // check for login (if already logged in the action will simply resolve) console.debug('[E2E] Logging in for chat opening'); - const reportID = getConfigValueOrThrow('reportID', config); + const reportID = getConfigValueOrThrow('reportID', config as NativeConfig); E2ELogin().then((neededLogin) => { if (neededLogin) { diff --git a/src/libs/E2E/tests/reportTypingTest.e2e.ts b/src/libs/E2E/tests/reportTypingTest.e2e.ts index 4e0678aeb020..17464f7eb8d6 100644 --- a/src/libs/E2E/tests/reportTypingTest.e2e.ts +++ b/src/libs/E2E/tests/reportTypingTest.e2e.ts @@ -1,3 +1,4 @@ +import type {NativeConfig} from 'react-native-config'; import Config from 'react-native-config'; import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; @@ -16,7 +17,7 @@ const test = (config: TestConfig) => { // check for login (if already logged in the action will simply resolve) console.debug('[E2E] Logging in for typing'); - const reportID = getConfigValueOrThrow('reportID', config); + const reportID = getConfigValueOrThrow('reportID', config as NativeConfig); E2ELogin().then((neededLogin) => { if (neededLogin) { diff --git a/src/libs/E2E/types.ts b/src/libs/E2E/types.ts index 2d48813fa115..93640fbb4ce8 100644 --- a/src/libs/E2E/types.ts +++ b/src/libs/E2E/types.ts @@ -20,7 +20,7 @@ type NetworkCacheMap = Record< type TestConfig = { name: string; - [key: string]: string; + [key: string]: string | {autoFocus: boolean}; }; export type {SigninParams, IsE2ETestSession, NetworkCacheMap, NetworkCacheEntry, TestConfig}; diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index ecbed9c87eff..12319e342fb8 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -371,11 +371,11 @@ function replaceEmojis(text: string, preferredSkinTone: number = CONST.EMOJI_DEF * Find all emojis in a text and replace them with their code. */ function replaceAndExtractEmojis(text: string | null, preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE, lang: Locale = CONST.LOCALES.DEFAULT): ReplacedEmoji { - const {text: convertedText = '', emojis = [], cursorPosition} = replaceEmojis(text, preferredSkinTone, lang); + const {text: convertedText = '', emojis = [], cursorPosition} = replaceEmojis(text ?? '', preferredSkinTone, lang); return { text: convertedText, - emojis: emojis.concat(extractEmojis(text)), + emojis: emojis.concat(extractEmojis(text ?? '')), cursorPosition, }; } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx index 87352981ba9a..94e65a48e46b 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx @@ -19,7 +19,8 @@ function IncrementRenderCount() { function ComposerWithSuggestionsE2e(props: ComposerWithSuggestionsProps, ref: ForwardedRef) { // Eventually Auto focus on e2e tests useEffect(() => { - if ((E2EClient.getCurrentActiveTestConfig()?.reportScreen?.autoFocus ?? false) === false) { + const testConfig = E2EClient.getCurrentActiveTestConfig(); + if (testConfig?.reportScreen && typeof testConfig.reportScreen !== 'string' && (testConfig?.reportScreen.autoFocus ?? false) === false) { return; } From b037ab91daadf1b1dcb610f942f7ab385d4f499e Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 30 Jan 2024 13:38:04 +0100 Subject: [PATCH 067/213] fix: typecheck --- .../MoneyRequestConfirmationList.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 74ac5e878395..933d604fd3cb 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -337,7 +337,7 @@ function MoneyRequestConfirmationList({ let text; if (isSplitBill && iouAmount === 0) { text = translate('iou.split'); - } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithoutRoute) { + } else if (!!(receiptPath && isTypeRequest) || isDistanceRequestWithoutRoute) { text = translate('iou.request'); if (iouAmount !== 0) { text = translate('iou.requestAmount', {amount: Number(formattedAmount)}); @@ -439,7 +439,7 @@ function MoneyRequestConfirmationList({ translate, toLocaleDigit, ); - IOU.setMoneyRequestMerchant_temporaryForRefactor(transactionID, distanceMerchant); + IOU.setMoneyRequestMerchant_temporaryForRefactor(transactionID ?? '', distanceMerchant); }, [hasRoute, distance, mileageRate?.unit, mileageRate?.rate, mileageRate?.currency, translate, toLocaleDigit, isDistanceRequest, transactionID]); const selectParticipant = useCallback( @@ -633,7 +633,7 @@ function MoneyRequestConfirmationList({ CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', - reportID, + reportID ?? '', Navigation.getActiveRouteWithoutParams(), ), ) @@ -652,7 +652,7 @@ function MoneyRequestConfirmationList({ return; } if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.AMOUNT)); + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.AMOUNT)); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_AMOUNT.getRoute(iouType, reportID)); @@ -671,7 +671,7 @@ function MoneyRequestConfirmationList({ description={translate('common.description')} onPress={() => { if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.DESCRIPTION)); + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.DESCRIPTION)); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_DESCRIPTION.getRoute(iouType, reportID)); @@ -700,7 +700,7 @@ function MoneyRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => { if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.DATE)); + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.DATE)); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(iouType, reportID)); @@ -732,7 +732,7 @@ function MoneyRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => { if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.MERCHANT)); + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.MERCHANT)); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_MERCHANT.getRoute(iouType, reportID)); @@ -755,7 +755,7 @@ function MoneyRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => { if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.CATEGORY)); + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.CATEGORY)); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_CATEGORY.getRoute(iouType, reportID)); @@ -775,7 +775,7 @@ function MoneyRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => { if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.TAG)); + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.TAG)); return; } Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(iouType, reportID)); @@ -796,7 +796,7 @@ function MoneyRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction?.transactionID ?? '', reportID ?? '', Navigation.getActiveRouteWithoutParams()), ) } disabled={didConfirm} @@ -813,7 +813,7 @@ function MoneyRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction?.transactionID ?? '', reportID ?? '', Navigation.getActiveRouteWithoutParams()), ) } disabled={didConfirm} From eb50d450940d30b3d946f705ad795e695cbf8448 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 30 Jan 2024 20:00:30 +0700 Subject: [PATCH 068/213] add missing mock functions for navigationRef --- tests/perf-test/SignInPage.perf-test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/perf-test/SignInPage.perf-test.tsx b/tests/perf-test/SignInPage.perf-test.tsx index 80964c3c49cd..c7c3eebc25a4 100644 --- a/tests/perf-test/SignInPage.perf-test.tsx +++ b/tests/perf-test/SignInPage.perf-test.tsx @@ -25,6 +25,8 @@ jest.mock('../../src/libs/Navigation/Navigation', () => { navigationRef: { addListener: () => jest.fn(), removeListener: () => jest.fn(), + isReady: () => jest.fn(), + getCurrentRoute: () => jest.fn(), }, } as typeof Navigation; }); From 086a705a923bbffe7e4960b7335d8aff588ce2be Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 2 Feb 2024 10:52:24 +0700 Subject: [PATCH 069/213] resolve conflict --- src/pages/ProfilePage.js | 45 ++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index f20055190e3e..16ace231eceb 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -5,6 +5,7 @@ import React, {useEffect} from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import AttachmentModal from '@components/AttachmentModal'; import AutoUpdateTime from '@components/AutoUpdateTime'; import Avatar from '@components/Avatar'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -103,6 +104,8 @@ function ProfilePage(props) { const login = lodashGet(details, 'login', ''); const timezone = lodashGet(details, 'timezone', {}); + const originalFileName = lodashGet(details, 'originalFileName', ''); + // If we have a reportID param this means that we // arrived here via the ParticipantsPage and should be allowed to navigate back to it const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyAccountIDs([accountID]) && !_.isEmpty(timezone); @@ -128,7 +131,7 @@ function ProfilePage(props) { const hasStatus = !!statusEmojiCode; const statusContent = `${statusEmojiCode} ${statusText}`; - const navigateBackTo = lodashGet(props.route, 'params.backTo', ROUTES.HOME); + const navigateBackTo = lodashGet(props.route, 'params.backTo'); const shouldShowNotificationPreference = !_.isEmpty(props.report) && props.report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const notificationPreference = shouldShowNotificationPreference ? props.translate(`notificationPreferencesPage.notificationPreferences.${props.report.notificationPreference}`) : ''; @@ -151,22 +154,32 @@ function ProfilePage(props) { {hasMinimumDetails && ( - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} - accessibilityLabel={props.translate('common.profile')} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} + - - - - + {({show}) => ( + + + + + + )} + {Boolean(displayName) && ( Date: Fri, 2 Feb 2024 14:35:33 +0100 Subject: [PATCH 070/213] fix: sync with main --- src/components/MoneyRequestConfirmationList.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 933d604fd3cb..30cd28541181 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -660,8 +660,14 @@ function MoneyRequestConfirmationList({ style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? translate('common.error.enterAmount') : ''} + brickRoadIndicator={ + isPolicyExpenseChat && shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined + } + error={ + shouldDisplayMerchantError || (isPolicyExpenseChat && shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null)) + ? translate('common.error.enterMerchant') + : '' + } /> )} Date: Fri, 2 Feb 2024 15:00:47 +0100 Subject: [PATCH 071/213] fix: resolve comments --- src/components/MoneyRequestConfirmationList.tsx | 7 +++++-- src/libs/ReceiptUtils.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 30cd28541181..685559835897 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -423,7 +423,10 @@ function MoneyRequestConfirmationList({ return []; } const myIOUAmount = IOUUtils.calculateAmount(selectedParticipantsMemo.length, iouAmount, iouCurrencyCode ?? '', true); - return [...selectedParticipantsMemo, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetailsMemo, String(myIOUAmount))]; + return [ + ...selectedParticipantsMemo, + OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetailsMemo, CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode)), + ]; }, [hasMultipleParticipants, selectedParticipantsMemo, iouAmount, iouCurrencyCode, payeePersonalDetailsMemo]); useEffect(() => { @@ -560,7 +563,7 @@ function MoneyRequestConfirmationList({ return ( <> - {formError && ( + {!!formError && ( , receiptPa // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg // If there're errors, we need to display them in preview. We can store many files in errors, but we just need to get the last one - const errors = _.findLast(transaction.errors) as ReceiptError | undefined; + const errors = _.findLast(transaction?.errors) as ReceiptError | undefined; const path = errors?.source ?? transaction?.receipt?.source ?? receiptPath ?? ''; // filename of uploaded image or last part of remote URI const filename = errors?.filename ?? transaction?.filename ?? receiptFileName ?? ''; From 45c6530265b83384f1b06b8f58725e266aa7d315 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 2 Feb 2024 16:57:35 +0100 Subject: [PATCH 072/213] fix: typecheck --- src/components/AttachmentModal.tsx | 8 ++++---- .../API/parameters/AddCommentOrAttachementParams.ts | 4 +++- src/libs/ReportUtils.ts | 5 +++-- src/libs/actions/Report.ts | 5 +++-- .../AttachmentPickerWithMenuItems.tsx | 3 ++- .../ComposerWithSuggestions.tsx | 7 +++++-- .../ReportActionCompose/ReportActionCompose.tsx | 12 +++++------- .../SilentCommentUpdater/types.ts | 4 ---- src/types/onyx/ReportAction.ts | 3 ++- 9 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 90954c63b751..f4b016b790de 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -80,7 +80,7 @@ type ImagePickerResponse = { type FileObject = File | ImagePickerResponse; type ChildrenProps = { - displayFileInModal: (data: FileObject) => void; + displayFileInModal: (data: FileObject | undefined) => void; show: () => void; }; @@ -317,8 +317,8 @@ function AttachmentModal({ }, []); const validateAndDisplayFileToUpload = useCallback( - (data: FileObject) => { - if (!isDirectoryCheck(data)) { + (data: FileObject | undefined) => { + if (!data || !isDirectoryCheck(data)) { return; } let fileObject = data; @@ -617,4 +617,4 @@ export default withOnyx({ }, })(memo(AttachmentModal)); -export type {Attachment}; +export type {Attachment, FileObject}; diff --git a/src/libs/API/parameters/AddCommentOrAttachementParams.ts b/src/libs/API/parameters/AddCommentOrAttachementParams.ts index 58faf9fdfc9c..4eab35be7dd2 100644 --- a/src/libs/API/parameters/AddCommentOrAttachementParams.ts +++ b/src/libs/API/parameters/AddCommentOrAttachementParams.ts @@ -1,9 +1,11 @@ +import type {FileObject} from '@components/AttachmentModal'; + type AddCommentOrAttachementParams = { reportID: string; reportActionID?: string; commentReportActionID?: string | null; reportComment?: string; - file?: File; + file?: Partial; timezone?: string; shouldAllowActionableMentionWhispers?: boolean; clientCreatedTime?: string; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5f3efcbcdbb0..651cd1f6513d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8,6 +8,7 @@ import lodashIsEqual from 'lodash/isEqual'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import type {FileObject} from '@components/AttachmentModal'; import * as Expensicons from '@components/Icon/Expensicons'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; import CONST from '@src/CONST'; @@ -1290,7 +1291,7 @@ function getRoomWelcomeMessage(report: OnyxEntry, isUserPolicyAdmin: boo /** * Returns true if Concierge is one of the chat participants (1:1 as well as group chats) */ -function chatIncludesConcierge(report: OnyxEntry): boolean { +function chatIncludesConcierge(report: Partial>): boolean { return Boolean(report?.participantAccountIDs?.length && report?.participantAccountIDs?.includes(CONST.ACCOUNT_ID.CONCIERGE)); } @@ -2591,7 +2592,7 @@ function getParsedComment(text: string): string { return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : lodashEscape(text); } -function buildOptimisticAddCommentReportAction(text?: string, file?: File, actorAccountID?: number): OptimisticReportAction { +function buildOptimisticAddCommentReportAction(text?: string, file?: Partial, actorAccountID?: number): OptimisticReportAction { const parser = new ExpensiMark(); const commentText = getParsedComment(text ?? ''); const isAttachment = !text && file !== undefined; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 782cf2b174c2..a508325a3206 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -7,6 +7,7 @@ import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-nat import Onyx from 'react-native-onyx'; import type {PartialDeep, ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; +import type {FileObject} from '@components/AttachmentModal'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; import type { @@ -352,7 +353,7 @@ function notifyNewAction(reportID: string, accountID?: number, reportActionID?: * - Adding one attachment * - Add both a comment and attachment simultaneously */ -function addActions(reportID: string, text = '', file?: File) { +function addActions(reportID: string, text = '', file?: Partial) { let reportCommentText = ''; let reportCommentAction: OptimisticAddCommentReportAction | undefined; let attachmentAction: OptimisticAddCommentReportAction | undefined; @@ -511,7 +512,7 @@ function addActions(reportID: string, text = '', file?: File) { } /** Add an attachment and optional comment. */ -function addAttachment(reportID: string, file: File, text = '') { +function addAttachment(reportID: string, file: Partial, text = '') { addActions(reportID, text, file); } diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 0dbfbba2759d..9511498ea699 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -6,6 +6,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; import type {ValueOf} from 'type-fest'; +import type {FileObject} from '@components/AttachmentModal'; import AttachmentPicker from '@components/AttachmentPicker'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -46,7 +47,7 @@ type AttachmentPickerWithMenuItemsProps = { report: OnyxEntry; /** Callback to open the file in the modal */ - displayFileInModal: (url: string) => void; + displayFileInModal: (url: FileObject | undefined) => void; /** Whether or not the full size composer is available */ isFullComposerAvailable: boolean; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 074383897dac..76388372dd27 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -16,6 +16,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {useAnimatedRef} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; +import type {FileObject} from '@components/AttachmentModal'; import Composer from '@components/Composer'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; @@ -85,7 +86,7 @@ type ComposerWithSuggestionsProps = { isComposerFullSize: boolean; isMenuVisible: boolean; inputPlaceholder: string; - displayFileInModal: (file: File | undefined) => void; + displayFileInModal: (file: FileObject | undefined) => void; textInputShouldClear: boolean; setTextInputShouldClear: (shouldClear: boolean) => void; isBlockedFromConcierge: boolean; @@ -107,6 +108,8 @@ type ComposerWithSuggestionsProps = { lastReportAction?: OnyxTypes.ReportAction; includeChronos?: boolean; parentReportActionID?: string; + // eslint-disable-next-line react/no-unused-prop-types + parentReportID: string | undefined; } & ComposerWithSuggestionsOnyxProps & Partial; @@ -748,7 +751,7 @@ export default withOnyx `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`, + key: ({parentReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, canEvict: false, initWithStoredValues: false, }, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index a5646f9c99a3..02d4105c3500 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -76,9 +76,6 @@ type ReportActionComposeProps = { /** The ID of the report actions will be created for */ reportID: string; - /** Array of report actions for this report */ - reportActions?: OnyxTypes.ReportAction[]; - /** The report currently being looked at */ report: OnyxEntry; @@ -97,9 +94,11 @@ type ReportActionComposeProps = { /** Whether the report is ready for display */ isReportReadyForDisplay?: boolean; + /** Whether the chat is empty */ isEmptyChat?: boolean; - lastReportAction?: any; + /** The last report action */ + lastReportAction?: OnyxTypes.ReportAction; } & ReportActionComposeOnyxProps & WithCurrentUserPersonalDetailsProps; @@ -118,7 +117,6 @@ function ReportActionCompose({ pendingAction, report, reportID, - reportActions, listHeight = 0, shouldShowComposeInput = true, isReportReadyForDisplay = true, @@ -269,7 +267,7 @@ function ReportActionCompose({ }, []); const addAttachment = useCallback( - (file: FileObject) => { + (file: Partial) => { const newComment = composerRef.current?.prepareCommentAndResetComposer(); Report.addAttachment(reportID, file, newComment); setTextInputShouldClear(false); @@ -437,7 +435,7 @@ function ReportActionCompose({ reportID={reportID} parentReportID={report?.parentReportID} parentReportActionID={report?.parentReportActionID} - includesChronos={ReportUtils.chatIncludesChronos(report)} + includeChronos={ReportUtils.chatIncludesChronos(report)} isEmptyChat={isEmptyChat} lastReportAction={lastReportAction} isMenuVisible={isMenuVisible} diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts index 591ee43ce6cd..1ba1689e7d6d 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts @@ -1,5 +1,4 @@ import type {OnyxEntry} from 'react-native-onyx'; -import type {Report} from '@src/types/onyx'; type SilentCommentUpdaterOnyxProps = { /** The comment of the report */ @@ -13,9 +12,6 @@ type SilentCommentUpdaterProps = { /** The ID of the report associated with the comment */ reportID: string; - /** The report associated with the comment */ - report: OnyxEntry; - /** The value of the comment */ value: string; diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index d2f0afad5b7a..59fb785234eb 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -1,4 +1,5 @@ import type {ValueOf} from 'type-fest'; +import type {FileObject} from '@components/AttachmentModal'; import type {AvatarSource} from '@libs/UserUtils'; import type CONST from '@src/CONST'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -165,7 +166,7 @@ type ReportActionBase = { isFirstItem?: boolean; /** Informations about attachments of report action */ - attachmentInfo?: File | EmptyObject; + attachmentInfo?: Partial | EmptyObject; /** Receipt tied to report action */ receipt?: Receipt; From 61ad4769e0215b48706a74615e843fb765eeab58 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 2 Feb 2024 18:51:38 +0100 Subject: [PATCH 073/213] fix: resolve comments --- src/components/Composer/types.ts | 4 +- src/components/LocaleContextProvider.tsx | 4 +- src/libs/ComposerUtils/index.ts | 2 +- src/libs/DateUtils.ts | 2 +- src/libs/E2E/tests/chatOpeningTest.e2e.ts | 5 +- src/libs/E2E/tests/reportTypingTest.e2e.ts | 5 +- src/libs/EmojiUtils.ts | 7 +- .../home/report/ParticipantLocalTime.tsx | 4 +- .../AttachmentPickerWithMenuItems.tsx | 2 +- .../ComposerWithSuggestions.tsx | 73 +++++++++++++++++-- .../ComposerWithSuggestions/index.e2e.tsx | 8 +- .../ReportActionCompose.tsx | 46 ++++++------ .../report/ReportActionCompose/SendButton.tsx | 2 - .../SilentCommentUpdater/index.android.tsx | 2 +- .../SilentCommentUpdater/index.tsx | 4 +- .../SilentCommentUpdater/types.ts | 6 +- .../ReportActionCompose/SuggestionEmoji.tsx | 13 ++-- .../ReportActionCompose/SuggestionMention.tsx | 10 +-- .../ReportActionCompose/Suggestions.tsx | 21 ++++++ .../home/report/ReportTypingIndicator.tsx | 4 +- 20 files changed, 147 insertions(+), 77 deletions(-) diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index 9565eaf6208f..f19feb94dd0a 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -5,7 +5,7 @@ type TextSelection = { end?: number; }; -type ComposerProps = { +type ComposerProps = TextInputProps & { /** identify id in the text input */ id?: string; @@ -76,6 +76,6 @@ type ComposerProps = { /** Should make the input only scroll inside the element avoid scroll out to parent */ shouldContainScroll?: boolean; -} & TextInputProps; +}; export type {TextSelection, ComposerProps}; diff --git a/src/components/LocaleContextProvider.tsx b/src/components/LocaleContextProvider.tsx index 25b468181b87..6d819f4d6eaa 100644 --- a/src/components/LocaleContextProvider.tsx +++ b/src/components/LocaleContextProvider.tsx @@ -45,7 +45,7 @@ type LocaleContextProps = { /** Returns a locally converted phone number for numbers from the same region * and an internationally converted phone number with the country code for numbers from other regions */ - formatPhoneNumber: (phoneNumber: string) => string; + formatPhoneNumber: (phoneNumber: string | undefined) => string; /** Gets the locale digit corresponding to a standard digit */ toLocaleDigit: (digit: string) => string; @@ -94,7 +94,7 @@ function LocaleContextProvider({preferredLocale, currentUserPersonalDetails = {} const updateLocale = useMemo(() => () => DateUtils.setLocale(locale), [locale]); - const formatPhoneNumber = useMemo(() => (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber), []); + const formatPhoneNumber = useMemo(() => (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber ?? ''), []); const toLocaleDigit = useMemo(() => (digit) => LocaleDigitUtils.toLocaleDigit(locale, digit), [locale]); diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index de937c0ba915..6018cad86e47 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -18,7 +18,7 @@ function insertText(text: string, selection: Selection, textToInsert: string): s * Insert a white space at given index of text * @param text - text that needs whitespace to be appended to */ -function insertWhiteSpaceAtIndex(text: string | null, index: number) { +function insertWhiteSpaceAtIndex(text: string, index: number) { return `${text?.slice(0, index)} ${text?.slice(index)}`; } diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 9cb08556f082..c10d6b90128b 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -265,7 +265,7 @@ function formatToLongDateWithWeekday(datetime: string | Date): string { * * @returns Sunday */ -function formatToDayOfWeek(datetime: string): string { +function formatToDayOfWeek(datetime: Date): string { return format(new Date(datetime), CONST.DATE.WEEKDAY_TIME_FORMAT); } diff --git a/src/libs/E2E/tests/chatOpeningTest.e2e.ts b/src/libs/E2E/tests/chatOpeningTest.e2e.ts index 2e2ce76b348d..17d9dfa1cb4d 100644 --- a/src/libs/E2E/tests/chatOpeningTest.e2e.ts +++ b/src/libs/E2E/tests/chatOpeningTest.e2e.ts @@ -2,18 +2,17 @@ import type {NativeConfig} from 'react-native-config'; import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; -import type {TestConfig} from '@libs/E2E/types'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; -const test = (config: TestConfig) => { +const test = (config: NativeConfig) => { // check for login (if already logged in the action will simply resolve) console.debug('[E2E] Logging in for chat opening'); - const reportID = getConfigValueOrThrow('reportID', config as NativeConfig); + const reportID = getConfigValueOrThrow('reportID', config); E2ELogin().then((neededLogin) => { if (neededLogin) { diff --git a/src/libs/E2E/tests/reportTypingTest.e2e.ts b/src/libs/E2E/tests/reportTypingTest.e2e.ts index 17464f7eb8d6..817bda941611 100644 --- a/src/libs/E2E/tests/reportTypingTest.e2e.ts +++ b/src/libs/E2E/tests/reportTypingTest.e2e.ts @@ -4,7 +4,6 @@ import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import waitForKeyboard from '@libs/E2E/actions/waitForKeyboard'; import E2EClient from '@libs/E2E/client'; -import type {TestConfig} from '@libs/E2E/types'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; @@ -13,11 +12,11 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import * as NativeCommands from '../../../../tests/e2e/nativeCommands/NativeCommandsAction'; -const test = (config: TestConfig) => { +const test = (config: NativeConfig) => { // check for login (if already logged in the action will simply resolve) console.debug('[E2E] Logging in for typing'); - const reportID = getConfigValueOrThrow('reportID', config as NativeConfig); + const reportID = getConfigValueOrThrow('reportID', config); E2ELogin().then((neededLogin) => { if (neededLogin) { diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index a7063e204efe..819aecf09d43 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -10,7 +10,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {FrequentlyUsedEmoji, Locale} from '@src/types/onyx'; import type {ReportActionReaction, UsersReactions} from '@src/types/onyx/ReportActionReactions'; import type IconAsset from '@src/types/utils/IconAsset'; -import type {SupportedLanguage} from './EmojiTrie'; type HeaderIndice = {code: string; index: number; icon: IconAsset}; type EmojiSpacer = {code: string; spacer: boolean}; @@ -370,12 +369,12 @@ function replaceEmojis(text: string, preferredSkinTone: number = CONST.EMOJI_DEF /** * Find all emojis in a text and replace them with their code. */ -function replaceAndExtractEmojis(text: string | null, preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE, lang: Locale = CONST.LOCALES.DEFAULT): ReplacedEmoji { +function replaceAndExtractEmojis(text: string, preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE, lang: Locale = CONST.LOCALES.DEFAULT): ReplacedEmoji { const {text: convertedText = '', emojis = [], cursorPosition} = replaceEmojis(text ?? '', preferredSkinTone, lang); return { text: convertedText, - emojis: emojis.concat(extractEmojis(text ?? '')), + emojis: emojis.concat(extractEmojis(text)), cursorPosition, }; } @@ -384,7 +383,7 @@ function replaceAndExtractEmojis(text: string | null, preferredSkinTone: number * Suggest emojis when typing emojis prefix after colon * @param [limit] - matching emojis limit */ -function suggestEmojis(text: string, lang: SupportedLanguage, limit: number = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { +function suggestEmojis(text: string, lang: Locale, limit: number = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { // emojisTrie is importing the emoji JSON file on the app starting and we want to avoid it const emojisTrie = require('./EmojiTrie').default; diff --git a/src/pages/home/report/ParticipantLocalTime.tsx b/src/pages/home/report/ParticipantLocalTime.tsx index e97fad8f20f0..58d85c0662b2 100644 --- a/src/pages/home/report/ParticipantLocalTime.tsx +++ b/src/pages/home/report/ParticipantLocalTime.tsx @@ -22,8 +22,8 @@ function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: const reportRecipientTimezone = participant.timezone || CONST.DEFAULT_TIME_ZONE; const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT, undefined, reportRecipientTimezone.selected); const currentTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT); - const reportRecipientDay = DateUtils.formatToDayOfWeek(reportTimezone.toDateString()); - const currentUserDay = DateUtils.formatToDayOfWeek(currentTimezone.toDateString()); + const reportRecipientDay = DateUtils.formatToDayOfWeek(reportTimezone); + const currentUserDay = DateUtils.formatToDayOfWeek(currentTimezone); if (reportRecipientDay !== currentUserDay) { return `${DateUtils.formatToLocalTime(reportTimezone)} ${reportRecipientDay}`; } diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 9511498ea699..e6a75e6f157b 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -194,7 +194,7 @@ function AttachmentPickerWithMenuItems({ return ( - {/* @ts-expect-error TODO: Remove this once SettlementButton (https://github.com/Expensify/App/issues/25134) is migrated to TypeScript. */} + {/* @ts-expect-error TODO: Remove this once AttachmentPicker (https://github.com/Expensify/App/issues/25134) is migrated to TypeScript. */} {({openPicker}) => { const triggerAttachmentPicker = () => { onTriggerAttachmentPicker(); diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 76388372dd27..96989b2a647b 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -1,6 +1,6 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; import lodashDebounce from 'lodash/debounce'; -import type {ForwardedRef, RefAttributes, RefObject} from 'react'; +import type {ForwardedRef, MutableRefObject, RefAttributes, RefObject} from 'react'; import React, {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type { LayoutChangeEvent, @@ -79,35 +79,94 @@ type ComposerWithSuggestionsOnyxProps = { }; type ComposerWithSuggestionsProps = { + /** Report ID */ reportID: string; + + /** Callback to focus composer */ onFocus: () => void; + + /** Callback to blur composer */ onBlur: (event: NativeSyntheticEvent) => void; + + /** Callback to update the value of the composer */ onValueChange: (value: string) => void; + + /** Whether the composer is full size */ isComposerFullSize: boolean; + + /** Whether the menu is visible */ isMenuVisible: boolean; + + /** The placeholder for the input */ inputPlaceholder: string; + + /** Function to display a file in a modal */ displayFileInModal: (file: FileObject | undefined) => void; + + /** Whether the text input should clear */ textInputShouldClear: boolean; + + /** Function to set the text input should clear */ setTextInputShouldClear: (shouldClear: boolean) => void; + + /** Whether the user is blocked from concierge */ isBlockedFromConcierge: boolean; + + /** Whether the input is disabled */ disabled: boolean; + + /** Whether the full composer is available */ isFullComposerAvailable: boolean; + + /** Function to set whether the full composer is available */ setIsFullComposerAvailable: (isFullComposerAvailable: boolean) => void; + + /** Function to set whether the comment is empty */ setIsCommentEmpty: (isCommentEmpty: boolean) => void; + + /** Function to handle sending a message */ handleSendMessage: () => void; + + /** Whether the compose input should show */ shouldShowComposeInput: OnyxEntry; + + /** Function to measure the parent container */ measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; + + /** The height of the list */ listHeight: number; + + /** Whether the scroll is likely to trigger a layout */ isScrollLikelyLayoutTriggered: RefObject; + + /** Function to raise the scroll is likely layout triggered */ raiseIsScrollLikelyLayoutTriggered: () => void; + + /** The ref to the suggestions */ suggestionsRef: React.RefObject; + + /** The ref to the animated input */ animatedRef: AnimatedRef; - isNextModalWillOpenRef: RefObject; + + /** The ref to the next modal will open */ + isNextModalWillOpenRef: MutableRefObject; + + /** Whether the edit is focused */ editFocused: boolean; + + /** Wheater chat is empty */ isEmptyChat?: boolean; + + /** The last report action */ lastReportAction?: OnyxTypes.ReportAction; + + /** Whether to include chronos */ includeChronos?: boolean; + + /** The parent report action ID */ parentReportActionID?: string; + + /** The parent report ID */ // eslint-disable-next-line react/no-unused-prop-types parentReportID: string | undefined; } & ComposerWithSuggestionsOnyxProps & @@ -275,13 +334,13 @@ function ComposerWithSuggestions( * @property diff - The newly added characters. */ const findNewlyAddedChars = useCallback( - (prevText: string, newText: string | null): NewlyAddedChars => { + (prevText: string, newText: string): NewlyAddedChars => { let startIndex = -1; let endIndex = -1; let currentIndex = 0; // Find the first character mismatch with newText - while (currentIndex < (newText?.length ?? 0) && prevText.charAt(currentIndex) === newText?.charAt(currentIndex) && selection.start > currentIndex) { + while (currentIndex < newText.length && prevText.charAt(currentIndex) === newText?.charAt(currentIndex) && selection.start > currentIndex) { currentIndex++; } @@ -295,7 +354,6 @@ function ComposerWithSuggestions( endIndex = currentIndex + (newText?.length ?? 0); } } - return { startIndex, endIndex, @@ -309,7 +367,7 @@ function ComposerWithSuggestions( * Update the value of the comment in Onyx */ const updateComment = useCallback( - (commentValue: string | null, shouldDebounceSaveComment?: boolean) => { + (commentValue: string, shouldDebounceSaveComment?: boolean) => { raiseIsScrollLikelyLayoutTriggered(); const {startIndex, endIndex, diff} = findNewlyAddedChars(lastTextRef.current, commentValue); const isEmojiInserted = diff.length && endIndex > startIndex && diff.trim() === diff && EmojiUtils.containsOnlyEmojis(diff); @@ -317,7 +375,7 @@ function ComposerWithSuggestions( text: newComment, emojis, cursorPosition, - } = EmojiUtils.replaceAndExtractEmojis(isEmojiInserted ? ComposerUtils.insertWhiteSpaceAtIndex(commentValue, endIndex) : commentValue, preferredSkinTone, preferredLocale); + } = EmojiUtils.replaceAndExtractEmojis(isEmojiInserted ? ComposerUtils.insertWhiteSpaceAtIndex(commentValue, endIndex) : commentValue ?? '', preferredSkinTone, preferredLocale); if (emojis.length) { const newEmojis = EmojiUtils.getAddedEmojis(emojis, emojisPresentBefore.current); if (newEmojis.length) { @@ -595,7 +653,6 @@ function ComposerWithSuggestions( const prevIsFocused = usePrevious(isFocused); useEffect(() => { if (modal?.isVisible && !prevIsModalVisible) { - // @ts-expect-error need to reassign this ref // eslint-disable-next-line no-param-reassign isNextModalWillOpenRef.current = false; } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx index 94e65a48e46b..0fa535329d33 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx @@ -1,4 +1,4 @@ -import type {ForwardedRef, RefObject} from 'react'; +import type {ForwardedRef} from 'react'; import React, {forwardRef, useEffect} from 'react'; import E2EClient from '@libs/E2E/client'; import type {ComposerRef} from '@pages/home/report/ReportActionCompose/ReportActionCompose'; @@ -20,17 +20,17 @@ function ComposerWithSuggestionsE2e(props: ComposerWithSuggestionsProps, ref: Fo // Eventually Auto focus on e2e tests useEffect(() => { const testConfig = E2EClient.getCurrentActiveTestConfig(); - if (testConfig?.reportScreen && typeof testConfig.reportScreen !== 'string' && (testConfig?.reportScreen.autoFocus ?? false) === false) { + if (testConfig?.reportScreen && typeof testConfig.reportScreen !== 'string' && !testConfig?.reportScreen.autoFocus) { return; } // We need to wait for the component to be mounted before focusing setTimeout(() => { - if (!(ref as RefObject)?.current) { + if (!(ref && 'current' in ref)) { return; } - (ref as RefObject).current?.focus(true); + ref.current?.focus(true); }, 1); }, [ref]); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 02d4105c3500..98630ff88b23 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -69,38 +69,38 @@ type ReportActionComposeOnyxProps = { shouldShowComposeInput: OnyxEntry; }; -type ReportActionComposeProps = { - /** A method to call when the form is submitted */ - onSubmit: (newComment: string | undefined) => void; +type ReportActionComposeProps = ReportActionComposeOnyxProps & + WithCurrentUserPersonalDetailsProps & { + /** A method to call when the form is submitted */ + onSubmit: (newComment: string | undefined) => void; - /** The ID of the report actions will be created for */ - reportID: string; + /** The ID of the report actions will be created for */ + reportID: string; - /** The report currently being looked at */ - report: OnyxEntry; + /** The report currently being looked at */ + report: OnyxEntry; - /** Is composer full size */ - isComposerFullSize?: boolean; + /** Is composer full size */ + isComposerFullSize?: boolean; - /** Whether user interactions should be disabled */ - disabled?: boolean; + /** Whether user interactions should be disabled */ + disabled?: boolean; - /** Height of the list which the composer is part of */ - listHeight?: number; + /** Height of the list which the composer is part of */ + listHeight?: number; - /** The type of action that's pending */ - pendingAction?: OnyxCommon.PendingAction; + /** The type of action that's pending */ + pendingAction?: OnyxCommon.PendingAction; - /** Whether the report is ready for display */ - isReportReadyForDisplay?: boolean; + /** Whether the report is ready for display */ + isReportReadyForDisplay?: boolean; - /** Whether the chat is empty */ - isEmptyChat?: boolean; + /** Whether the chat is empty */ + isEmptyChat?: boolean; - /** The last report action */ - lastReportAction?: OnyxTypes.ReportAction; -} & ReportActionComposeOnyxProps & - WithCurrentUserPersonalDetailsProps; + /** The last report action */ + lastReportAction?: OnyxTypes.ReportAction; + }; // We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will // prevent auto focus on existing chat for mobile device diff --git a/src/pages/home/report/ReportActionCompose/SendButton.tsx b/src/pages/home/report/ReportActionCompose/SendButton.tsx index feeff49ceb55..4726c1638f42 100644 --- a/src/pages/home/report/ReportActionCompose/SendButton.tsx +++ b/src/pages/home/report/ReportActionCompose/SendButton.tsx @@ -46,8 +46,6 @@ function SendButton({isDisabled: isDisabledProp, handleSendMessage}: SendButtonP ]} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.send')} - accessible - onPress={() => {}} > {({pressed}) => ( { - updateComment(comment); + updateComment(comment ?? ''); // eslint-disable-next-line react-hooks/exhaustive-deps -- We need to run this on mount }, []); diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx index a1d591c97297..c84bd3786610 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx @@ -17,7 +17,7 @@ function SilentCommentUpdater({comment, commentRef, reportID, value, updateComme const prevPreferredLocale = usePrevious(preferredLocale); useEffect(() => { - updateComment(comment ?? null); + updateComment(comment ?? ''); // eslint-disable-next-line react-hooks/exhaustive-deps -- We need to run this on mount }, []); @@ -32,7 +32,7 @@ function SilentCommentUpdater({comment, commentRef, reportID, value, updateComme return; } - updateComment(comment); + updateComment(comment ?? ''); }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, reportID, updateComment, value, commentRef]); return null; diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts index 1ba1689e7d6d..dbc23b0279c3 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/types.ts @@ -5,9 +5,9 @@ type SilentCommentUpdaterOnyxProps = { comment: OnyxEntry; }; -type SilentCommentUpdaterProps = { +type SilentCommentUpdaterProps = SilentCommentUpdaterOnyxProps & { /** Updates the comment */ - updateComment: (comment: OnyxEntry) => void; + updateComment: (comment: string) => void; /** The ID of the report associated with the comment */ reportID: string; @@ -17,6 +17,6 @@ type SilentCommentUpdaterProps = { /** The ref of the comment */ commentRef: React.RefObject; -} & SilentCommentUpdaterOnyxProps; +}; export type {SilentCommentUpdaterProps, SilentCommentUpdaterOnyxProps}; diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index d343616a206d..206c91ed9b08 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -6,7 +6,6 @@ import type {Emoji} from '@assets/emojis/types'; import EmojiSuggestions from '@components/EmojiSuggestions'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useLocalize from '@hooks/useLocalize'; -import type {SupportedLanguage} from '@libs/EmojiTrie'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import CONST from '@src/CONST'; @@ -26,11 +25,11 @@ type SuggestionEmojiOnyxProps = { preferredSkinTone: number; }; -type SuggestionEmojiProps = { - /** Function to clear the input */ - resetKeyboardInput: (() => void) | undefined; -} & SuggestionEmojiOnyxProps & - SuggestionProps; +type SuggestionEmojiProps = SuggestionProps & + SuggestionEmojiOnyxProps & { + /** Function to clear the input */ + resetKeyboardInput: (() => void) | undefined; + }; /** * Check if this piece of string looks like an emoji @@ -166,7 +165,7 @@ function SuggestionEmoji( colonIndex, shouldShowSuggestionMenu: false, }; - const newSuggestedEmojis = EmojiUtils.suggestEmojis(leftString, preferredLocale as SupportedLanguage); + const newSuggestedEmojis = EmojiUtils.suggestEmojis(leftString, preferredLocale); if (newSuggestedEmojis?.length && isCurrentlyShowingEmojiSuggestion) { nextState.suggestedEmojis = newSuggestedEmojis; diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 02bcd27093e7..1e3e80ad9d7e 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -15,8 +15,6 @@ import type {PersonalDetailsList} from '@src/types/onyx'; import type {SuggestionsRef} from './ReportActionCompose'; import type {SuggestionProps} from './Suggestions'; -type SuggestionMentionProps = {isAutoSuggestionPickerLarge: boolean} & SuggestionProps; - type SuggestionValues = { suggestedMentions: Mention[]; atSignIndex: number; @@ -37,7 +35,7 @@ const defaultSuggestionsValues: SuggestionValues = { }; function SuggestionMention( - {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused}: SuggestionMentionProps, + {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused}: SuggestionProps, ref: ForwardedRef, ) { const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; @@ -161,7 +159,7 @@ function SuggestionMention( sortedPersonalDetails.slice(0, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length).forEach((detail) => { suggestions.push({ text: PersonalDetailsUtils.getDisplayNameOrDefault(detail), - alternateText: formatPhoneNumber(detail?.login ?? ''), + alternateText: formatPhoneNumber(detail?.login), login: detail?.login, icons: [ { @@ -200,7 +198,7 @@ function SuggestionMention( const leftString = value.substring(0, suggestionEndIndex); const words = leftString.split(CONST.REGEX.SPACE_OR_EMOJI); - const lastWord = words.at(-1) ?? ''; + const lastWord: string = words.at(-1) ?? ''; const secondToLastWord = words[words.length - 3]; let atSignIndex; @@ -297,7 +295,7 @@ function SuggestionMention( mentions={suggestionValues.suggestedMentions} prefix={suggestionValues.mentionPrefix} onSelect={insertSelectedMention} - isMentionPickerLarge={isAutoSuggestionPickerLarge} + isMentionPickerLarge={!!isAutoSuggestionPickerLarge} measureParentContainer={measureParentContainer} /> ); diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.tsx b/src/pages/home/report/ReportActionCompose/Suggestions.tsx index 0968a61c3abb..61026a792919 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -14,16 +14,37 @@ type Selection = { }; type SuggestionProps = { + /** The current input value */ value: string; + + /** Callback to update the current input value */ setValue: (newValue: string) => void; + + /** The current selection value */ selection: Selection; + + /** Callback to update the current selection */ setSelection: (newSelection: Selection) => void; + + /** Callback to update the comment draft */ updateComment: (newComment: string, shouldDebounceSaveComment?: boolean) => void; + + /** Meaures the parent container's position and dimensions. */ measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; + + /** Whether the composer is expanded */ isComposerFullSize: boolean; + + /** Report composer focus state */ isComposerFocused?: boolean; + + /** Callback to reset the keyboard input */ resetKeyboardInput?: () => void; + + /** Whether the auto suggestion picker is large */ isAutoSuggestionPickerLarge?: boolean; + + /** The height of the composer */ composerHeight?: number; }; diff --git a/src/pages/home/report/ReportTypingIndicator.tsx b/src/pages/home/report/ReportTypingIndicator.tsx index 6b00a4520295..e484ce9886a7 100755 --- a/src/pages/home/report/ReportTypingIndicator.tsx +++ b/src/pages/home/report/ReportTypingIndicator.tsx @@ -15,10 +15,10 @@ type ReportTypingIndicatorOnyxProps = { userTypingStatuses: OnyxEntry; }; -type ReportTypingIndicatorProps = { +type ReportTypingIndicatorProps = ReportTypingIndicatorOnyxProps & { // eslint-disable-next-line react/no-unused-prop-types -- This is used by withOnyx reportID: string; -} & ReportTypingIndicatorOnyxProps; +}; function ReportTypingIndicator({userTypingStatuses}: ReportTypingIndicatorProps) { const {translate} = useLocalize(); From 3f10fe467b1bd17408eb8ffa1a8bb628a2429e46 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 17:48:58 +0700 Subject: [PATCH 074/213] fix update amount --- src/libs/actions/IOU.js | 73 ++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f5167eed9afa..bd9773dcbfa2 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -312,15 +312,33 @@ function getReceiptError(receipt, filename, isScanRequest = true) { : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source, filename}); } +/** + * @param {Object} iouReport + * + * @returns {Boolean} + */ +function needsToBeManuallySubmitted(iouReport) { + const isPolicyExpenseChat = ReportUtils.isExpenseReport(iouReport); + + if (isPolicyExpenseChat) { + const policy = ReportUtils.getPolicy(iouReport.policyID); + const isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); + + // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN + return isFromPaidPolicy && !(policy.isHarvestingEnabled || false); + } + + return true; +} + /** * Return the object to update hasOutstandingChildRequest * @param {Object} [policy] * @param {Object} iouReport - * @param {Boolean} needsToBeManuallySubmitted * @returns {Object} */ -function getOutstandingChildRequest(policy, iouReport, needsToBeManuallySubmitted) { - if (!needsToBeManuallySubmitted) { +function getOutstandingChildRequest(policy, iouReport) { + if (!needsToBeManuallySubmitted(iouReport)) { return { hasOutstandingChildRequest: false, }; @@ -355,7 +373,6 @@ function getOutstandingChildRequest(policy, iouReport, needsToBeManuallySubmitte * @param {Object} policy - May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) * @param {Array} policyTags * @param {Array} policyCategories - * @param {Boolean} needsToBeManuallySubmitted * @returns {Array} - An array containing the optimistic data, success data, and failure data. */ function buildOnyxDataForMoneyRequest( @@ -374,10 +391,9 @@ function buildOnyxDataForMoneyRequest( policy, policyTags, policyCategories, - needsToBeManuallySubmitted = true, ) { const isScanRequest = TransactionUtils.isScanRequest(transaction); - const outstandingChildRequest = getOutstandingChildRequest(policy, iouReport, needsToBeManuallySubmitted); + const outstandingChildRequest = getOutstandingChildRequest(policy, iouReport); const optimisticData = [ { // Use SET for new reports because it doesn't exist yet, is faster and we need the data to be available when we navigate to the chat page @@ -722,15 +738,10 @@ function getMoneyRequestInformation( iouReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${chatReport.iouReportID}`]; } - // Check if the Scheduled Submit is enabled in case of expense report - let needsToBeManuallySubmitted = true; let isFromPaidPolicy = false; if (isPolicyExpenseChat) { isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); - // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN - needsToBeManuallySubmitted = isFromPaidPolicy && !(lodashGet(policy, 'harvesting.enabled', policy.isHarvestingEnabled) || false); - // If the linked expense report on paid policy is not draft, we need to create a new draft expense report if (iouReport && isFromPaidPolicy && !ReportUtils.isDraftExpenseReport(iouReport)) { iouReport = null; @@ -859,7 +870,6 @@ function getMoneyRequestInformation( policy, policyTags, policyCategories, - needsToBeManuallySubmitted, ); return { @@ -1033,7 +1043,7 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t // from the server with the currency conversion let updatedMoneyRequestReport = {...iouReport}; if (updatedTransaction.currency === iouReport.currency && updatedTransaction.modifiedAmount) { - const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true); + const diff = TransactionUtils.getAmount(updatedTransaction, isFromExpenseReport) - TransactionUtils.getAmount(transaction, isFromExpenseReport); if (ReportUtils.isExpenseReport(iouReport)) { updatedMoneyRequestReport.total += diff; } else { @@ -1041,11 +1051,23 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t } updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedTransaction.currency); - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: updatedMoneyRequestReport, - }); + optimisticData.push( + ...[ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: updatedMoneyRequestReport, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.parentReportID}`, + value: { + hasOutstandingChildRequest: + needsToBeManuallySubmitted(iouReport) && updatedMoneyRequestReport.managerID === userAccountID && updatedMoneyRequestReport.total !== 0, + }, + }, + ], + ); successData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, @@ -2653,18 +2675,7 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView const updatedReportPreviewAction = {...reportPreviewAction}; updatedReportPreviewAction.pendingAction = shouldDeleteIOUReport ? CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE : CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; - const isPolicyExpenseChat = ReportUtils.isExpenseReport(iouReport); - - let needsToBeManuallySubmitted = true; - if (isPolicyExpenseChat) { - const policy = ReportUtils.getPolicy(iouReport.policyID); - const isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); - - // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN - needsToBeManuallySubmitted = isFromPaidPolicy && !(policy.isHarvestingEnabled || false); - } - - if (isPolicyExpenseChat) { + if (ReportUtils.isExpenseReport(iouReport)) { updatedIOUReport = {...iouReport}; // Because of the Expense reports are stored as negative values, we add the total from the amount @@ -2770,7 +2781,7 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, value: { - hasOutstandingChildRequest: needsToBeManuallySubmitted && updatedIOUReport.managerID === userAccountID && updatedIOUReport.total !== 0, + hasOutstandingChildRequest: needsToBeManuallySubmitted(iouReport) && updatedIOUReport.managerID === userAccountID && updatedIOUReport.total !== 0, }, }, ]; From fad94907fbdbd62dc75a4668055132d0dec9825b Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 5 Feb 2024 12:02:47 +0100 Subject: [PATCH 075/213] fix: resolve comments --- .../ReportActionCompose/SuggestionMention.tsx | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 1e3e80ad9d7e..2b5080e788ab 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -1,3 +1,4 @@ +import lodashSortBy from 'lodash/sortBy'; import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -142,20 +143,8 @@ function SuggestionMention( return true; }); - const sortedPersonalDetails = filteredPersonalDetails.sort((a, b) => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null - const nameA = a?.displayName || a?.login || ''; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null - const nameB = b?.displayName || b?.login || ''; - - if (nameA < nameB) { - return -1; - } - if (nameA > nameB) { - return 1; - } - return 0; - }); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing cannot be used if left side can be empty string + const sortedPersonalDetails = lodashSortBy(filteredPersonalDetails, (detail) => detail?.displayName || detail?.login); sortedPersonalDetails.slice(0, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length).forEach((detail) => { suggestions.push({ text: PersonalDetailsUtils.getDisplayNameOrDefault(detail), @@ -201,9 +190,9 @@ function SuggestionMention( const lastWord: string = words.at(-1) ?? ''; const secondToLastWord = words[words.length - 3]; - let atSignIndex; + let atSignIndex: number | undefined; let suggestionWord = ''; - let prefix; + let prefix: string; // Detect if the last two words contain a mention (two words are needed to detect a mention with a space in it) if (lastWord.startsWith('@')) { From 937946954bcce86a8af2b7b9eeca12336e155635 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 22:52:12 +0700 Subject: [PATCH 076/213] seperate platform logic --- .../index.native.ts} | 0 src/libs/actions/CachedPDFPaths/index.ts | 9 +++++++++ 2 files changed, 9 insertions(+) rename src/libs/actions/{CachedPDFPaths.ts => CachedPDFPaths/index.native.ts} (100%) create mode 100644 src/libs/actions/CachedPDFPaths/index.ts diff --git a/src/libs/actions/CachedPDFPaths.ts b/src/libs/actions/CachedPDFPaths/index.native.ts similarity index 100% rename from src/libs/actions/CachedPDFPaths.ts rename to src/libs/actions/CachedPDFPaths/index.native.ts diff --git a/src/libs/actions/CachedPDFPaths/index.ts b/src/libs/actions/CachedPDFPaths/index.ts new file mode 100644 index 000000000000..8c5c71df7a49 --- /dev/null +++ b/src/libs/actions/CachedPDFPaths/index.ts @@ -0,0 +1,9 @@ +function add(reportActionID: string, path: string): Promise { + return Promise.resolve(); +} + +function clearByKey(reportActionID: string) {} + +function clearAll() {} + +export {add, clearByKey, clearAll}; From 6eb146a455a902b3c7fcad8fdd2c93362150a107 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 5 Feb 2024 23:07:20 +0700 Subject: [PATCH 077/213] add types for CachedPDFPaths --- .../Attachments/AttachmentView/index.js | 2 +- src/libs/actions/CachedPDFPaths/index.native.ts | 17 +++++++++-------- src/libs/actions/CachedPDFPaths/index.ts | 10 +++++----- src/libs/actions/CachedPDFPaths/types.ts | 6 ++++++ 4 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 src/libs/actions/CachedPDFPaths/types.ts diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 39f83820d207..cf8cc6f017d9 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -143,7 +143,7 @@ function AttachmentView({ const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; const onPDFLoadComplete = (path) => { - if (isUsedInCarousel && reportActionID) { + if (isUsedInCarousel && reportActionID && path) { CachedPDFPaths.add(reportActionID, path); } if (!loadComplete) { diff --git a/src/libs/actions/CachedPDFPaths/index.native.ts b/src/libs/actions/CachedPDFPaths/index.native.ts index 4eefb9a7982e..6c4456f7a528 100644 --- a/src/libs/actions/CachedPDFPaths/index.native.ts +++ b/src/libs/actions/CachedPDFPaths/index.native.ts @@ -1,6 +1,7 @@ import {exists, unlink} from 'react-native-fs'; import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Add, Clear, ClearAll, ClearByKey} from './types'; /* * We need to save the paths of PDF files so we can delete them later. @@ -14,14 +15,14 @@ Onyx.connect({ }, }); -function add(reportActionID: string, path: string): Promise { +const add: Add = (reportActionID: string, path: string) => { if (pdfPaths[reportActionID]) { return Promise.resolve(); } return Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[reportActionID]: path}); -} +}; -function clear(path: string): Promise { +const clear: Clear = (path: string) => { if (!path) { return Promise.resolve(); } @@ -33,14 +34,14 @@ function clear(path: string): Promise { return unlink(path); }); }); -} +}; -function clearByKey(reportActionID: string) { +const clearByKey: ClearByKey = (reportActionID: string) => { clear(pdfPaths[reportActionID] ?? '').then(() => Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[reportActionID]: null})); -} +}; -function clearAll() { +const clearAll: ClearAll = () => { Promise.all(Object.values(pdfPaths).map(clear)).then(() => Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {})); -} +}; export {add, clearByKey, clearAll}; diff --git a/src/libs/actions/CachedPDFPaths/index.ts b/src/libs/actions/CachedPDFPaths/index.ts index 8c5c71df7a49..3cac21bf3c25 100644 --- a/src/libs/actions/CachedPDFPaths/index.ts +++ b/src/libs/actions/CachedPDFPaths/index.ts @@ -1,9 +1,9 @@ -function add(reportActionID: string, path: string): Promise { - return Promise.resolve(); -} +import type {Add, ClearAll, ClearByKey} from './types'; -function clearByKey(reportActionID: string) {} +const add: Add = () => Promise.resolve(); -function clearAll() {} +const clearByKey: ClearByKey = () => {}; + +const clearAll: ClearAll = () => {}; export {add, clearByKey, clearAll}; diff --git a/src/libs/actions/CachedPDFPaths/types.ts b/src/libs/actions/CachedPDFPaths/types.ts new file mode 100644 index 000000000000..8c4843aaeaec --- /dev/null +++ b/src/libs/actions/CachedPDFPaths/types.ts @@ -0,0 +1,6 @@ +type Add = (reportActionID: string, path: string) => Promise; +type Clear = (path: string) => Promise; +type ClearAll = () => void; +type ClearByKey = (reportActionID: string) => void; + +export type {Add, Clear, ClearAll, ClearByKey}; From 4d0c1b1704d30f2eee6bd85bbc8e4ddca4433e67 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 6 Feb 2024 16:26:36 +0700 Subject: [PATCH 078/213] resolve conflict --- src/pages/ProfilePage.js | 43 ++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 16ace231eceb..426232b2f68d 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -5,7 +5,6 @@ import React, {useEffect} from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import AttachmentModal from '@components/AttachmentModal'; import AutoUpdateTime from '@components/AutoUpdateTime'; import Avatar from '@components/Avatar'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -104,8 +103,6 @@ function ProfilePage(props) { const login = lodashGet(details, 'login', ''); const timezone = lodashGet(details, 'timezone', {}); - const originalFileName = lodashGet(details, 'originalFileName', ''); - // If we have a reportID param this means that we // arrived here via the ParticipantsPage and should be allowed to navigate back to it const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyAccountIDs([accountID]) && !_.isEmpty(timezone); @@ -154,32 +151,22 @@ function ProfilePage(props) { {hasMinimumDetails && ( - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} + accessibilityLabel={props.translate('common.profile')} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} > - {({show}) => ( - - - - - - )} - + + + + {Boolean(displayName) && ( Date: Tue, 6 Feb 2024 14:34:37 +0100 Subject: [PATCH 079/213] fix: typecheck --- src/components/ButtonWithDropdownMenu.tsx | 21 +++++++-------- .../MoneyRequestConfirmationList.tsx | 27 ++++++++++++------- .../API/parameters/PayMoneyRequestParams.ts | 5 ++-- src/libs/API/parameters/SendMoneyParams.ts | 5 ++-- src/libs/actions/IOU.ts | 26 +++++++++--------- src/types/onyx/OriginalMessage.ts | 3 +-- 6 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/components/ButtonWithDropdownMenu.tsx b/src/components/ButtonWithDropdownMenu.tsx index 0e41da57b409..8aa3a5f0b9f0 100644 --- a/src/components/ButtonWithDropdownMenu.tsx +++ b/src/components/ButtonWithDropdownMenu.tsx @@ -10,17 +10,14 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import type {AnchorPosition} from '@styles/index'; import CONST from '@src/CONST'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type IconAsset from '@src/types/utils/IconAsset'; import Button from './Button'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import PopoverMenu from './PopoverMenu'; -type PaymentType = DeepValueOf; - -type DropdownOption = { - value: PaymentType; +type DropdownOption = { + value: T; text: string; icon?: IconAsset; iconWidth?: number; @@ -28,15 +25,15 @@ type DropdownOption = { iconDescription?: string; }; -type ButtonWithDropdownMenuProps = { +type ButtonWithDropdownMenuProps = { /** Text to display for the menu header */ menuHeaderText?: string; /** Callback to execute when the main button is pressed */ - onPress: (event: GestureResponderEvent | KeyboardEvent | undefined, value: PaymentType) => void; + onPress: (event: GestureResponderEvent | KeyboardEvent | undefined, value: T) => void; /** Callback to execute when a dropdown option is selected */ - onOptionSelected?: (option: DropdownOption) => void; + onOptionSelected?: (option: DropdownOption) => void; /** Call the onPress function on main button when Enter key is pressed */ pressOnEnter?: boolean; @@ -55,19 +52,19 @@ type ButtonWithDropdownMenuProps = { /** Menu options to display */ /** e.g. [{text: 'Pay with Expensify', icon: Wallet}] */ - options: DropdownOption[]; + options: Array>; /** The anchor alignment of the popover menu */ anchorAlignment?: AnchorAlignment; /* ref for the button */ - buttonRef: RefObject; + buttonRef?: RefObject; /** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */ enterKeyEventListenerPriority?: number; }; -function ButtonWithDropdownMenu({ +function ButtonWithDropdownMenu({ isLoading = false, isDisabled = false, pressOnEnter = false, @@ -83,7 +80,7 @@ function ButtonWithDropdownMenu({ options, onOptionSelected, enterKeyEventListenerPriority = 0, -}: ButtonWithDropdownMenuProps) { +}: ButtonWithDropdownMenuProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index d26bdc139f69..1fd94d4a5f4b 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -25,11 +25,12 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type {MileageRate} from '@src/types/onyx/Policy'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import ConfirmedRoute from './ConfirmedRoute'; import FormHelpMessage from './FormHelpMessage'; @@ -44,6 +45,11 @@ import Text from './Text'; import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; +type DropdownOption = { + text: string; + value: DeepValueOf; +}; + type Option = Partial; type CategorySection = { @@ -79,7 +85,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & onConfirm?: (selectedParticipants: Participant[]) => void; /** Callback to parent modal to send money */ - onSendMoney?: (paymentMethod: PaymentMethodType) => void; + onSendMoney?: (paymentMethod: IOU.PaymentMethodType) => void; /** Callback to inform a participant is selected */ onSelectParticipant?: (option: Participant) => void; @@ -130,7 +136,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & isReadOnly?: boolean; /** Depending on expense report or personal IOU report, respective bank account route */ - bankAccountRoute?: string; + bankAccountRoute?: Route; /** The policyID of the request */ policyID?: string; @@ -233,7 +239,7 @@ function MoneyRequestConfirmationList({ const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(policyCategories ?? {})); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -250,7 +256,7 @@ function MoneyRequestConfirmationList({ const policyTagList = policyTag?.tags ?? {}; const policyTagListName = policyTag?.name ?? translate('common.tag'); // A flag for showing the tags field - const shouldShowTags = isPolicyExpenseChat && (iouTag || OptionsListUtils.hasEnabledOptions(policyTagList)); + const shouldShowTags = isPolicyExpenseChat && (iouTag || OptionsListUtils.hasEnabledOptions(Object.values(policyTagList ?? {}))); // A flag for showing tax fields - tax rate and tax amount const shouldShowTax = isPolicyExpenseChat && policy?.isTaxTrackingEnabled; @@ -333,7 +339,7 @@ function MoneyRequestConfirmationList({ setDidConfirm(false); } - const splitOrRequestOptions = useMemo(() => { + const splitOrRequestOptions: DropdownOption[] = useMemo(() => { let text; if (isSplitBill && iouAmount === 0) { text = translate('iou.split'); @@ -368,7 +374,7 @@ function MoneyRequestConfirmationList({ if (!canModifyParticipantsValue) { formattedParticipantsList = formattedParticipantsList.map((participant) => ({ ...participant, - isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID), + isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), })); } @@ -396,7 +402,7 @@ function MoneyRequestConfirmationList({ } else { const formattedSelectedParticipants = selectedParticipants.map((participant) => ({ ...participant, - isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID), + isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), })); sections.push({ title: translate('common.to'), @@ -470,7 +476,7 @@ function MoneyRequestConfirmationList({ }; const confirm = useCallback( - (paymentMethod: PaymentMethodType) => { + (paymentMethod: IOU.PaymentMethodType | undefined) => { if (selectedParticipantsMemo.length === 0) { return; } @@ -555,7 +561,7 @@ function MoneyRequestConfirmationList({ pressOnEnter isDisabled={shouldDisableButton} // eslint-disable-next-line @typescript-eslint/naming-convention - onPress={(_, value) => confirm(value as PaymentMethodType)} + onPress={(_, value) => confirm(value as IOU.PaymentMethodType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} @@ -796,6 +802,7 @@ function MoneyRequestConfirmationList({ ); return; } + Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(iouType, reportID)); }} style={styles.moneyRequestMenuItem} diff --git a/src/libs/API/parameters/PayMoneyRequestParams.ts b/src/libs/API/parameters/PayMoneyRequestParams.ts index 94f62bcca065..93117467f630 100644 --- a/src/libs/API/parameters/PayMoneyRequestParams.ts +++ b/src/libs/API/parameters/PayMoneyRequestParams.ts @@ -1,11 +1,10 @@ -import type CONST from '@src/CONST'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import type {PaymentMethodType} from '@libs/actions/IOU'; type PayMoneyRequestParams = { iouReportID: string; chatReportID: string; reportActionID: string; - paymentMethodType: DeepValueOf; + paymentMethodType: PaymentMethodType; }; export default PayMoneyRequestParams; diff --git a/src/libs/API/parameters/SendMoneyParams.ts b/src/libs/API/parameters/SendMoneyParams.ts index c32287d5b4ec..e4faa8da5b8f 100644 --- a/src/libs/API/parameters/SendMoneyParams.ts +++ b/src/libs/API/parameters/SendMoneyParams.ts @@ -1,11 +1,10 @@ -import type CONST from '@src/CONST'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import type {PaymentMethodType} from '@libs/actions/IOU'; type SendMoneyParams = { iouReportID: string; chatReportID: string; reportActionID: string; - paymentMethodType: DeepValueOf; + paymentMethodType: PaymentMethodType; transactionID: string; newIOUReportDetails: string; createdReportActionID: string; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 569b6b26b728..3444030240d2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -63,7 +63,7 @@ type MoneyRequestRoute = StackScreenProps; -type PaymentMethodType = DeepValueOf; +type PaymentMethodType = DeepValueOf | typeof CONST.IOU.REPORT_ACTION_TYPE.APPROVE; type OneOnOneIOUReport = OnyxTypes.Report | undefined | null; @@ -3255,14 +3255,14 @@ function sendMoneyWithWallet(report: OnyxTypes.Report, amount: number, currency: Report.notifyNewAction(params.chatReportID, managerID); } -function approveMoneyRequest(expenseReport: OnyxTypes.Report) { - const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; +function approveMoneyRequest(expenseReport: OnyxEntry | EmptyObject) { + const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport?.reportID}`] ?? null; - const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID); + const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(expenseReport?.total ?? 0, expenseReport?.currency ?? '', expenseReport?.reportID ?? ''); const optimisticReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport?.reportID}`, value: { [optimisticApprovedReportAction.reportActionID]: { ...(optimisticApprovedReportAction as OnyxTypes.ReportAction), @@ -3272,7 +3272,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report) { }; const optimisticIOUReportData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.reportID}`, value: { ...expenseReport, lastMessageText: optimisticApprovedReportAction.message?.[0].text, @@ -3286,7 +3286,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report) { const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport?.reportID}`, value: { [optimisticApprovedReportAction.reportActionID]: { pendingAction: null, @@ -3298,9 +3298,9 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report) { const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport?.reportID}`, value: { - [expenseReport.reportActionID ?? '']: { + [expenseReport?.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), }, }, @@ -3310,18 +3310,18 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report) { if (currentNextStep) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport?.reportID}`, value: null, }); failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport?.reportID}`, value: currentNextStep, }); } const parameters: ApproveMoneyRequestParams = { - reportID: expenseReport.reportID, + reportID: expenseReport?.reportID ?? '', approvedReportActionID: optimisticApprovedReportAction.reportActionID, }; @@ -3730,3 +3730,5 @@ export { navigateToStartStepIfScanFileCannotBeRead, savePreferredPaymentMethod, }; + +export type {PaymentMethodType}; diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index fd28699e7b4c..34ac84989632 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -1,9 +1,8 @@ import type {ValueOf} from 'type-fest'; +import type {PaymentMethodType} from '@libs/actions/IOU'; import type CONST from '@src/CONST'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; -type PaymentMethodType = DeepValueOf; - type ActionName = DeepValueOf; type OriginalMessageActionName = | 'ADDCOMMENT' From 1da3d098f240ba878e7d737a7e32597ca04b1ae9 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 8 Feb 2024 14:30:53 +0100 Subject: [PATCH 080/213] fix: typecheck --- src/components/MoneyRequestConfirmationList.tsx | 4 ++-- src/components/SettlementButton.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 98d177a19e67..dbfce22030a2 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -85,7 +85,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & onConfirm?: (selectedParticipants: Participant[]) => void; /** Callback to parent modal to send money */ - onSendMoney?: (paymentMethod: IOU.PaymentMethodType) => void; + onSendMoney?: (paymentMethod: OnyxTypes.PaymentMethodType) => void; /** Callback to inform a participant is selected */ onSelectParticipant?: (option: Participant) => void; @@ -567,7 +567,7 @@ function MoneyRequestConfirmationList({ pressOnEnter isDisabled={shouldDisableButton} // eslint-disable-next-line @typescript-eslint/naming-convention - onPress={(_, value) => confirm(value as IOU.PaymentMethodType)} + onPress={(_, value) => confirm(value as OnyxTypes.PaymentMethodType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 058def7a34ad..4e96684a65ec 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -226,10 +226,10 @@ function SettlementButton({ buttonRef={buttonRef} isDisabled={isDisabled} isLoading={isLoading} - onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} + onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType as PaymentMethodType, triggerKYCFlow)} pressOnEnter={pressOnEnter} options={paymentButtonOptions} - onOptionSelected={(option) => savePreferredPaymentMethod(policyID, option.value)} + onOptionSelected={(option) => savePreferredPaymentMethod(policyID, option.value as PaymentMethodType)} style={style} buttonSize={buttonSize} anchorAlignment={paymentMethodDropdownAnchorAlignment} From 5735f6163034bff9ad5b24adfbda21caaa132b9c Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 8 Feb 2024 18:40:53 +0100 Subject: [PATCH 081/213] fix: resolve comments --- src/components/AttachmentModal.tsx | 4 +- src/components/Composer/types.ts | 2 +- src/components/LocaleContextProvider.tsx | 2 +- src/libs/ComposerUtils/index.ts | 2 +- src/libs/EmojiUtils.ts | 2 +- .../home/report/ParticipantLocalTime.tsx | 13 +- .../AttachmentPickerWithMenuItems.tsx | 21 +-- .../ComposerWithSuggestions.tsx | 146 +++++++++--------- .../ComposerWithSuggestions/index.e2e.tsx | 2 +- .../ReportActionCompose.tsx | 4 +- .../report/ReportActionCompose/SendButton.tsx | 9 +- .../SilentCommentUpdater/index.android.tsx | 2 +- .../ReportActionCompose/SuggestionEmoji.tsx | 2 +- .../home/report/ReportTypingIndicator.tsx | 2 +- 14 files changed, 99 insertions(+), 114 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index adf5b7b232a2..f97da9fcd9b7 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -80,7 +80,7 @@ type ImagePickerResponse = { type FileObject = File | ImagePickerResponse; type ChildrenProps = { - displayFileInModal: (data: FileObject | undefined) => void; + displayFileInModal: (data: FileObject) => void; show: () => void; }; @@ -317,7 +317,7 @@ function AttachmentModal({ }, []); const validateAndDisplayFileToUpload = useCallback( - (data: FileObject | undefined) => { + (data: FileObject) => { if (!data || !isDirectoryCheck(data)) { return; } diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index f19feb94dd0a..6bc44aba69cd 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -31,7 +31,7 @@ type ComposerProps = TextInputProps & { onNumberOfLinesChange?: (numberOfLines: number) => void; /** Callback method to handle pasting a file */ - onPasteFile?: (file?: File) => void; + onPasteFile?: (file: File) => void; /** General styles to apply to the text input */ // eslint-disable-next-line react/forbid-prop-types diff --git a/src/components/LocaleContextProvider.tsx b/src/components/LocaleContextProvider.tsx index 6d819f4d6eaa..eca433025f71 100644 --- a/src/components/LocaleContextProvider.tsx +++ b/src/components/LocaleContextProvider.tsx @@ -94,7 +94,7 @@ function LocaleContextProvider({preferredLocale, currentUserPersonalDetails = {} const updateLocale = useMemo(() => () => DateUtils.setLocale(locale), [locale]); - const formatPhoneNumber = useMemo(() => (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber ?? ''), []); + const formatPhoneNumber = useMemo(() => (phoneNumber) => LocalePhoneNumber.formatPhoneNumber(phoneNumber), []); const toLocaleDigit = useMemo(() => (digit) => LocaleDigitUtils.toLocaleDigit(locale, digit), [locale]); diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index 6018cad86e47..94bba5d0d00c 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -19,7 +19,7 @@ function insertText(text: string, selection: Selection, textToInsert: string): s * @param text - text that needs whitespace to be appended to */ function insertWhiteSpaceAtIndex(text: string, index: number) { - return `${text?.slice(0, index)} ${text?.slice(index)}`; + return `${text.slice(0, index)} ${text.slice(index)}`; } /** diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 05a1cb29141b..cab0f48d75fd 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -370,7 +370,7 @@ function replaceEmojis(text: string, preferredSkinTone: number = CONST.EMOJI_DEF * Find all emojis in a text and replace them with their code. */ function replaceAndExtractEmojis(text: string, preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE, lang: Locale = CONST.LOCALES.DEFAULT): ReplacedEmoji { - const {text: convertedText = '', emojis = [], cursorPosition} = replaceEmojis(text ?? '', preferredSkinTone, lang); + const {text: convertedText = '', emojis = [], cursorPosition} = replaceEmojis(text, preferredSkinTone, lang); return { text: convertedText, diff --git a/src/pages/home/report/ParticipantLocalTime.tsx b/src/pages/home/report/ParticipantLocalTime.tsx index 58d85c0662b2..f51032690a33 100644 --- a/src/pages/home/report/ParticipantLocalTime.tsx +++ b/src/pages/home/report/ParticipantLocalTime.tsx @@ -12,16 +12,13 @@ import type {PersonalDetails} from '@src/types/onyx'; type ParticipantLocalTimeProps = { /** Personal details of the participant */ participant: PersonalDetails; - - /** The user's preferred locale e.g. 'en', 'es-ES' */ - preferredLocale?: Locale; }; -function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: Locale | undefined) { +function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: Locale) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null const reportRecipientTimezone = participant.timezone || CONST.DEFAULT_TIME_ZONE; - const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT, undefined, reportRecipientTimezone.selected); - const currentTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale ?? CONST.LOCALES.DEFAULT); + const reportTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale, undefined, reportRecipientTimezone.selected); + const currentTimezone = DateUtils.getLocalDateFromDatetime(preferredLocale); const reportRecipientDay = DateUtils.formatToDayOfWeek(reportTimezone); const currentUserDay = DateUtils.formatToDayOfWeek(currentTimezone); if (reportRecipientDay !== currentUserDay) { @@ -30,8 +27,8 @@ function getParticipantLocalTime(participant: PersonalDetails, preferredLocale: return `${DateUtils.formatToLocalTime(reportTimezone)}`; } -function ParticipantLocalTime({participant, preferredLocale}: ParticipantLocalTimeProps) { - const {translate} = useLocalize(); +function ParticipantLocalTime({participant}: ParticipantLocalTimeProps) { + const {translate, preferredLocale} = useLocalize(); const styles = useThemeStyles(); const [localTime, setLocalTime] = useState(() => getParticipantLocalTime(participant, preferredLocale)); diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index e6a75e6f157b..1ae8e63bd0f1 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,15 +1,14 @@ import {useIsFocused} from '@react-navigation/native'; -import type {FC} from 'react'; import React, {useCallback, useEffect, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import type {SvgProps} from 'react-native-svg'; import type {ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentPicker from '@components/AttachmentPicker'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; +import type {PopoverMenuItem} from '@components/PopoverMenu'; import PopoverMenu from '@components/PopoverMenu'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; @@ -29,25 +28,19 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; -type MoneyRequestOption = { - icon: FC; - text: string; - onSelected: () => void; -}; - -type MoneyRequestOptions = Record, MoneyRequestOption>; +type MoneyRequestOptions = Record, PopoverMenuItem>; type AttachmentPickerWithMenuItemsOnyxProps = { /** The policy tied to the report */ policy: OnyxEntry; }; -type AttachmentPickerWithMenuItemsProps = { +type AttachmentPickerWithMenuItemsProps = AttachmentPickerWithMenuItemsOnyxProps & { /** The report currently being looked at */ report: OnyxEntry; /** Callback to open the file in the modal */ - displayFileInModal: (url: FileObject | undefined) => void; + displayFileInModal: (url: FileObject) => void; /** Whether or not the full size composer is available */ isFullComposerAvailable: boolean; @@ -59,7 +52,7 @@ type AttachmentPickerWithMenuItemsProps = { isBlockedFromConcierge: boolean; /** Whether or not the attachment picker is disabled */ - disabled: boolean; + disabled?: boolean; /** Sets the menu visibility */ setMenuVisibility: (isVisible: boolean) => void; @@ -93,7 +86,7 @@ type AttachmentPickerWithMenuItemsProps = { /** The personal details of everyone in the report */ reportParticipantIDs?: number[]; -} & AttachmentPickerWithMenuItemsOnyxProps; +}; /** * This includes the popover of options you see when pressing the + button in the composer. @@ -155,7 +148,7 @@ function AttachmentPickerWithMenuItems({ /** * Determines if we can show the task option */ - const taskOption: MoneyRequestOption[] = useMemo(() => { + const taskOption: PopoverMenuItem[] = useMemo(() => { if (!ReportUtils.canCreateTaskInReport(report)) { return []; } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index b54ccaf20518..6b89a591055a 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -78,99 +78,99 @@ type ComposerWithSuggestionsOnyxProps = { editFocused: OnyxEntry; }; -type ComposerWithSuggestionsProps = { - /** Report ID */ - reportID: string; +type ComposerWithSuggestionsProps = ComposerWithSuggestionsOnyxProps & + Partial & { + /** Report ID */ + reportID: string; - /** Callback to focus composer */ - onFocus: () => void; + /** Callback to focus composer */ + onFocus: () => void; - /** Callback to blur composer */ - onBlur: (event: NativeSyntheticEvent) => void; + /** Callback to blur composer */ + onBlur: (event: NativeSyntheticEvent) => void; - /** Callback to update the value of the composer */ - onValueChange: (value: string) => void; + /** Callback to update the value of the composer */ + onValueChange: (value: string) => void; - /** Whether the composer is full size */ - isComposerFullSize: boolean; + /** Whether the composer is full size */ + isComposerFullSize: boolean; - /** Whether the menu is visible */ - isMenuVisible: boolean; + /** Whether the menu is visible */ + isMenuVisible: boolean; - /** The placeholder for the input */ - inputPlaceholder: string; + /** The placeholder for the input */ + inputPlaceholder: string; - /** Function to display a file in a modal */ - displayFileInModal: (file: FileObject | undefined) => void; + /** Function to display a file in a modal */ + displayFileInModal: (file: FileObject) => void; - /** Whether the text input should clear */ - textInputShouldClear: boolean; + /** Whether the text input should clear */ + textInputShouldClear: boolean; - /** Function to set the text input should clear */ - setTextInputShouldClear: (shouldClear: boolean) => void; + /** Function to set the text input should clear */ + setTextInputShouldClear: (shouldClear: boolean) => void; - /** Whether the user is blocked from concierge */ - isBlockedFromConcierge: boolean; + /** Whether the user is blocked from concierge */ + isBlockedFromConcierge: boolean; - /** Whether the input is disabled */ - disabled: boolean; + /** Whether the input is disabled */ + disabled: boolean; - /** Whether the full composer is available */ - isFullComposerAvailable: boolean; + /** Whether the full composer is available */ + isFullComposerAvailable: boolean; - /** Function to set whether the full composer is available */ - setIsFullComposerAvailable: (isFullComposerAvailable: boolean) => void; + /** Function to set whether the full composer is available */ + setIsFullComposerAvailable: (isFullComposerAvailable: boolean) => void; - /** Function to set whether the comment is empty */ - setIsCommentEmpty: (isCommentEmpty: boolean) => void; + /** Function to set whether the comment is empty */ + setIsCommentEmpty: (isCommentEmpty: boolean) => void; - /** Function to handle sending a message */ - handleSendMessage: () => void; + /** Function to handle sending a message */ + handleSendMessage: () => void; - /** Whether the compose input should show */ - shouldShowComposeInput: OnyxEntry; + /** Whether the compose input should show */ + shouldShowComposeInput: OnyxEntry; - /** Function to measure the parent container */ - measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; + /** Function to measure the parent container */ + measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; - /** The height of the list */ - listHeight: number; + /** The height of the list */ + listHeight: number; - /** Whether the scroll is likely to trigger a layout */ - isScrollLikelyLayoutTriggered: RefObject; + /** Whether the scroll is likely to trigger a layout */ + isScrollLikelyLayoutTriggered: RefObject; - /** Function to raise the scroll is likely layout triggered */ - raiseIsScrollLikelyLayoutTriggered: () => void; + /** Function to raise the scroll is likely layout triggered */ + raiseIsScrollLikelyLayoutTriggered: () => void; - /** The ref to the suggestions */ - suggestionsRef: React.RefObject; + /** The ref to the suggestions */ + suggestionsRef: React.RefObject; - /** The ref to the animated input */ - animatedRef: AnimatedRef; + /** The ref to the animated input */ + animatedRef: AnimatedRef; - /** The ref to the next modal will open */ - isNextModalWillOpenRef: MutableRefObject; + /** The ref to the next modal will open */ + isNextModalWillOpenRef: MutableRefObject; - /** Whether the edit is focused */ - editFocused: boolean; + /** Whether the edit is focused */ + editFocused: boolean; - /** Wheater chat is empty */ - isEmptyChat?: boolean; + /** Wheater chat is empty */ + isEmptyChat?: boolean; - /** The last report action */ - lastReportAction?: OnyxTypes.ReportAction; + /** The last report action */ + lastReportAction?: OnyxTypes.ReportAction; - /** Whether to include chronos */ - includeChronos?: boolean; + /** Whether to include chronos */ + includeChronos?: boolean; - /** The parent report action ID */ - parentReportActionID?: string; + /** The parent report action ID */ + parentReportActionID?: string; - /** The parent report ID */ - // eslint-disable-next-line react/no-unused-prop-types - parentReportID: string | undefined; -} & ComposerWithSuggestionsOnyxProps & - Partial; + /** The parent report ID */ + // eslint-disable-next-line react/no-unused-prop-types -- its used in the withOnyx HOC + parentReportID: string | undefined; + }; const {RNTextInputReset} = NativeModules; @@ -240,7 +240,7 @@ function ComposerWithSuggestions( // For testing children, }: ComposerWithSuggestionsProps, - ref: ForwardedRef, + ref: ForwardedRef, ) { const {isKeyboardShown} = useKeyboardState(); const theme = useTheme(); @@ -340,24 +340,24 @@ function ComposerWithSuggestions( let currentIndex = 0; // Find the first character mismatch with newText - while (currentIndex < newText.length && prevText.charAt(currentIndex) === newText?.charAt(currentIndex) && selection.start > currentIndex) { + while (currentIndex < newText.length && prevText.charAt(currentIndex) === newText.charAt(currentIndex) && selection.start > currentIndex) { currentIndex++; } - if (currentIndex < (newText?.length ?? 0)) { + if (currentIndex < newText.length) { startIndex = currentIndex; - const commonSuffixLength = ComposerUtils.findCommonSuffixLength(prevText, newText ?? '', selection.end); + const commonSuffixLength = ComposerUtils.findCommonSuffixLength(prevText, newText, selection.end); // if text is getting pasted over find length of common suffix and subtract it from new text length if (commonSuffixLength > 0 || selection.end - selection.start > 0) { - endIndex = (newText?.length ?? 0) - commonSuffixLength; + endIndex = newText.length - commonSuffixLength; } else { - endIndex = currentIndex + (newText?.length ?? 0); + endIndex = currentIndex + newText.length; } } return { startIndex, endIndex, - diff: newText?.substring(startIndex, endIndex) ?? '', + diff: newText.substring(startIndex, endIndex), }; }, [selection.start, selection.end], @@ -375,7 +375,7 @@ function ComposerWithSuggestions( text: newComment, emojis, cursorPosition, - } = EmojiUtils.replaceAndExtractEmojis(isEmojiInserted ? ComposerUtils.insertWhiteSpaceAtIndex(commentValue, endIndex) : commentValue ?? '', preferredSkinTone, preferredLocale); + } = EmojiUtils.replaceAndExtractEmojis(isEmojiInserted ? ComposerUtils.insertWhiteSpaceAtIndex(commentValue, endIndex) : commentValue, preferredSkinTone, preferredLocale); if (emojis.length) { const newEmojis = EmojiUtils.getAddedEmojis(emojis, emojisPresentBefore.current); if (newEmojis.length) { @@ -748,7 +748,7 @@ function ComposerWithSuggestions( isComposerFullSize={isComposerFullSize} value={value} testID="composer" - numberOfLines={numberOfLines ?? 0} + numberOfLines={numberOfLines ?? undefined} onNumberOfLinesChange={updateNumberOfLines} shouldCalculateCaretPosition onLayout={onLayout} diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx index 0fa535329d33..7f169ef15918 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx @@ -16,7 +16,7 @@ function IncrementRenderCount() { return null; } -function ComposerWithSuggestionsE2e(props: ComposerWithSuggestionsProps, ref: ForwardedRef) { +function ComposerWithSuggestionsE2e(props: ComposerWithSuggestionsProps, ref: ForwardedRef) { // Eventually Auto focus on e2e tests useEffect(() => { const testConfig = E2EClient.getCurrentActiveTestConfig(); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 98630ff88b23..283f0dfab18f 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -289,9 +289,7 @@ function ReportActionCompose({ */ const submitForm = useCallback( (event?: SyntheticEvent) => { - if (event) { - event.preventDefault(); - } + event?.preventDefault(); const newComment = composerRef.current?.prepareCommentAndResetComposer(); if (!newComment) { diff --git a/src/pages/home/report/ReportActionCompose/SendButton.tsx b/src/pages/home/report/ReportActionCompose/SendButton.tsx index 4726c1638f42..c505eb0e32e7 100644 --- a/src/pages/home/report/ReportActionCompose/SendButton.tsx +++ b/src/pages/home/report/ReportActionCompose/SendButton.tsx @@ -23,12 +23,9 @@ function SendButton({isDisabled: isDisabledProp, handleSendMessage}: SendButtonP const styles = useThemeStyles(); const {translate} = useLocalize(); - const Tap = Gesture.Tap() - // @ts-expect-error Enabled require argument but when passing something button is not working - .enabled() - .onEnd(() => { - handleSendMessage(); - }); + const Tap = Gesture.Tap().onEnd(() => { + handleSendMessage(); + }); return ( { updateComment(comment ?? ''); // eslint-disable-next-line react-hooks/exhaustive-deps -- We need to run this on mount diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index 206c91ed9b08..0ae45d2d705d 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -28,7 +28,7 @@ type SuggestionEmojiOnyxProps = { type SuggestionEmojiProps = SuggestionProps & SuggestionEmojiOnyxProps & { /** Function to clear the input */ - resetKeyboardInput: (() => void) | undefined; + resetKeyboardInput?: () => void; }; /** diff --git a/src/pages/home/report/ReportTypingIndicator.tsx b/src/pages/home/report/ReportTypingIndicator.tsx index e484ce9886a7..3ff8f2b0eb8e 100755 --- a/src/pages/home/report/ReportTypingIndicator.tsx +++ b/src/pages/home/report/ReportTypingIndicator.tsx @@ -41,7 +41,7 @@ function ReportTypingIndicator({userTypingStatuses}: ReportTypingIndicatorProps) if (usersTyping.length === 1) { return ( Date: Fri, 9 Feb 2024 16:32:51 +0100 Subject: [PATCH 082/213] fix: conflicts --- src/libs/NextStepUtils.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index 3b42382b10f9..e99f2c2ef8a1 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -1,5 +1,6 @@ import {format, lastDayOfMonth, setDate} from 'date-fns'; import Str from 'expensify-common/lib/str'; +import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; @@ -62,18 +63,21 @@ type BuildNextStepParameters = { * @param parameters.isPaidWithWallet - Whether a report has been paid with the wallet or outside of Expensify * @returns nextStep */ -function buildNextStep(report: Report | EmptyObject, predictedNextStatus: ValueOf, {isPaidWithWallet}: BuildNextStepParameters = {}): ReportNextStep | null { +function buildNextStep( + report: OnyxEntry | EmptyObject, + predictedNextStatus: ValueOf, + {isPaidWithWallet}: BuildNextStepParameters = {}, +): ReportNextStep | null { if (!ReportUtils.isExpenseReport(report)) { return null; } - const {policyID = '', ownerAccountID = -1, managerID = -1} = report; - const policy = ReportUtils.getPolicy(policyID); + const policy = ReportUtils.getPolicy(report?.policyID); const {submitsTo, harvesting, isPreventSelfApprovalEnabled, autoReportingFrequency, autoReportingOffset} = policy; - const isOwner = currentUserAccountID === ownerAccountID; - const isManager = currentUserAccountID === managerID; + const isOwner = currentUserAccountID === report?.ownerAccountID; + const isManager = currentUserAccountID === report?.managerID; const isSelfApproval = currentUserAccountID === submitsTo; - const ownerLogin = PersonalDetailsUtils.getLoginsByAccountIDs([ownerAccountID])[0] ?? ''; + const ownerLogin = PersonalDetailsUtils.getLoginsByAccountIDs([report?.ownerAccountID ?? -1])[0] ?? ''; const managerDisplayName = isSelfApproval ? 'you' : ReportUtils.getDisplayNameForParticipant(submitsTo) ?? ''; const type: ReportNextStep['type'] = 'neutral'; let optimisticNextStep: ReportNextStep | null; From f69fce5038eb5366ec3ada2f96050edc13382306 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 12 Feb 2024 09:52:40 +0100 Subject: [PATCH 083/213] fix: typecheck --- src/libs/OptionsListUtils.ts | 1 + src/types/onyx/index.ts | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 8a96c2a8705c..863970dbda30 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -30,6 +30,7 @@ import type {Participant} from '@src/types/onyx/IOU'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {PolicyTaxRate, PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import times from '@src/utils/times'; import Timing from './actions/Timing'; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 6343b9a1b2ec..51e772c0d173 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -50,6 +50,7 @@ import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; import type {PolicyTag, PolicyTagList, PolicyTags} from './PolicyTag'; +import type {PolicyTaxRate, PolicyTaxRates} from './PolicyTaxRates'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; @@ -169,8 +170,6 @@ export type { PolicyReportField, PolicyReportFields, RecentlyUsedReportFields, - PolicyTaxRate, - PolicyTaxRates, LastPaymentMethod, NewRoomForm, IKnowATeacherForm, @@ -180,4 +179,6 @@ export type { ReportFieldEditForm, RoomNameForm, PaymentMethodType, + PolicyTaxRate, + PolicyTaxRates, }; From 7786efc13997918b27fa488be2a19d854140c5af Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Tue, 13 Feb 2024 12:27:21 +0100 Subject: [PATCH 084/213] feat: upgrade react-native-reanimated --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11099886bfb0..cf598969e55e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,7 @@ "react-native-plaid-link-sdk": "10.8.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "^3.6.1", + "react-native-reanimated": "^3.7.0", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.7.4", "react-native-screens": "3.29.0", @@ -45184,9 +45184,9 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz", - "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.7.0.tgz", + "integrity": "sha512-KM+MKa3CJWqsF4GlOLLKBxTR2NEcrg5/HP9J2b6Dfgvll1sjZPywCOEEIh967SboEU8N9LjYZuoVm2UoXGxp2Q==", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", diff --git a/package.json b/package.json index 71983e0e1679..a92a633f7e15 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "react-native-plaid-link-sdk": "10.8.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "^3.6.1", + "react-native-reanimated": "^3.7.0", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.7.4", "react-native-screens": "3.29.0", From 68f4cf96945713268d8f1d34b9c4c81dd2ca2bba Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 14 Feb 2024 15:03:53 +0100 Subject: [PATCH 085/213] fix: typecheck --- src/ONYXKEYS.ts | 2 +- src/components/MoneyRequestConfirmationList.tsx | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0735bc53e56c..944788d99cb8 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -453,7 +453,7 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; [ONYXKEYS.COLLECTION.NEXT_STEP]: OnyxTypes.ReportNextStep; - [ONYXKEYS.COLLECTION.POLICY_TAX_RATE]: string[]; + [ONYXKEYS.COLLECTION.POLICY_TAX_RATE]: OnyxTypes.PolicyTaxRate; }; type OnyxValuesMapping = { diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 1dd6e9b9f302..de289f1fba3b 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -77,6 +77,9 @@ type MoneyRequestConfirmationListOnyxProps = { /** The policy of root parent report */ policy: OnyxEntry; + + /** The session of the logged in user */ + session: OnyxEntry; }; type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & @@ -818,7 +821,7 @@ function MoneyRequestConfirmationList({ Navigation.navigate( ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( CONST.IOU.ACTION.CREATE, - CONST.IOU.TYPE.SEND, + iouType, transaction?.transactionID ?? '', reportID ?? '', Navigation.getActiveRouteWithoutParams(), @@ -905,5 +908,8 @@ export default withCurrentUserPersonalDetails( iou: { key: ONYXKEYS.IOU, }, + session: { + key: ONYXKEYS.SESSION, + }, })(MoneyRequestConfirmationList), ); From 022db8f5be1264e9fb45a7393c84ab2d309a97af Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 14 Feb 2024 16:45:57 +0100 Subject: [PATCH 086/213] fix: resolve comments --- src/components/AttachmentModal.tsx | 4 ++-- .../AddCommentOrAttachementParams.ts | 2 +- src/libs/ReportUtils.ts | 2 +- src/libs/actions/Report.ts | 4 ++-- .../ComposerWithSuggestions.tsx | 10 ++++---- .../ReportActionCompose.tsx | 24 ++++--------------- .../SilentCommentUpdater/index.tsx | 1 + src/types/modules/pusher.d.ts | 2 +- src/types/onyx/ReportAction.ts | 2 +- 9 files changed, 19 insertions(+), 32 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 2b4afc037796..7f0178863fc9 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -89,7 +89,7 @@ type AttachmentModalProps = AttachmentModalOnyxProps & { source?: AvatarSource; /** Optional callback to fire when we want to preview an image and approve it for use. */ - onConfirm?: ((file: Partial) => void) | null; + onConfirm?: ((file: FileObject) => void) | null; /** Whether the modal should be open by default */ defaultOpen?: boolean; @@ -264,7 +264,7 @@ function AttachmentModal({ } if (onConfirm) { - onConfirm(Object.assign(file ?? {}, {source: sourceState})); + onConfirm(Object.assign(file ?? {}, {source: sourceState} as FileObject)); } setIsModalOpen(false); diff --git a/src/libs/API/parameters/AddCommentOrAttachementParams.ts b/src/libs/API/parameters/AddCommentOrAttachementParams.ts index 4eab35be7dd2..a705c92f7f27 100644 --- a/src/libs/API/parameters/AddCommentOrAttachementParams.ts +++ b/src/libs/API/parameters/AddCommentOrAttachementParams.ts @@ -5,7 +5,7 @@ type AddCommentOrAttachementParams = { reportActionID?: string; commentReportActionID?: string | null; reportComment?: string; - file?: Partial; + file?: FileObject; timezone?: string; shouldAllowActionableMentionWhispers?: boolean; clientCreatedTime?: string; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2b073b110184..ed3dc16b56a4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2631,7 +2631,7 @@ function getReportDescriptionText(report: Report): string { return parser.htmlToText(report.description); } -function buildOptimisticAddCommentReportAction(text?: string, file?: Partial, actorAccountID?: number): OptimisticReportAction { +function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, actorAccountID?: number): OptimisticReportAction { const parser = new ExpensiMark(); const commentText = getParsedComment(text ?? ''); const isAttachment = !text && file !== undefined; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index bfae6ece1fa1..7127178daae7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -353,7 +353,7 @@ function notifyNewAction(reportID: string, accountID?: number, reportActionID?: * - Adding one attachment * - Add both a comment and attachment simultaneously */ -function addActions(reportID: string, text = '', file?: Partial) { +function addActions(reportID: string, text = '', file?: FileObject) { let reportCommentText = ''; let reportCommentAction: OptimisticAddCommentReportAction | undefined; let attachmentAction: OptimisticAddCommentReportAction | undefined; @@ -512,7 +512,7 @@ function addActions(reportID: string, text = '', file?: Partial) { } /** Add an attachment and optional comment. */ -function addAttachment(reportID: string, file: Partial, text = '') { +function addAttachment(reportID: string, file: FileObject, text = '') { addActions(reportID, text, file); } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index cfe45e5a011b..a345926ad7d2 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -214,6 +214,7 @@ function ComposerWithSuggestions( onFocus, onBlur, onValueChange, + // Composer isComposerFullSize, isMenuVisible, @@ -232,11 +233,13 @@ function ComposerWithSuggestions( listHeight, isScrollLikelyLayoutTriggered, raiseIsScrollLikelyLayoutTriggered, + // Refs suggestionsRef, animatedRef, isNextModalWillOpenRef, editFocused, + // For testing children, }: ComposerWithSuggestionsProps, @@ -278,7 +281,7 @@ function ComposerWithSuggestions( const syncSelectionWithOnChangeTextRef = useRef(null); - const suggestions = suggestionsRef.current?.getSuggestions() ?? (() => []); + const suggestions = suggestionsRef.current?.getSuggestions() ?? []; const hasEnoughSpaceForLargeSuggestion = SuggestionUtils.hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, suggestions?.length ?? 0); @@ -457,7 +460,7 @@ function ComposerWithSuggestions( [reportID, numberOfLines], ); - const prepareCommentAndResetComposer = useCallback(() => { + const prepareCommentAndResetComposer = useCallback((): string => { const trimmedComment = commentRef.current.trim(); const commentLength = ReportUtils.getCommentLength(trimmedComment); @@ -575,7 +578,6 @@ function ComposerWithSuggestions( /** * Focus the composer text input * @param [shouldDelay=false] Impose delay before focusing the composer - * @memberof ReportActionCompose */ const focus = useCallback((shouldDelay = false) => { focusComposerWithDelay(textInputRef.current)(shouldDelay); @@ -614,7 +616,7 @@ function ComposerWithSuggestions( } // if we're typing on another input/text area, do not focus - if (['INPUT', 'TEXTAREA'].includes((e.target as Element)?.nodeName)) { + if (['INPUT', 'TEXTAREA'].includes((e.target as Element | null)?.nodeName ?? '')) { return; } diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 735fb323d638..84f7600f8ef0 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -43,6 +43,7 @@ import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import ComposerWithSuggestions from './ComposerWithSuggestions'; +import type {ComposerWithSuggestionsProps} from './ComposerWithSuggestions/ComposerWithSuggestions'; import SendButton from './SendButton'; type ComposerRef = { @@ -71,36 +72,19 @@ type ReportActionComposeOnyxProps = { }; type ReportActionComposeProps = ReportActionComposeOnyxProps & - WithCurrentUserPersonalDetailsProps & { + WithCurrentUserPersonalDetailsProps & + Pick & { /** A method to call when the form is submitted */ onSubmit: (newComment: string | undefined) => void; - /** The ID of the report actions will be created for */ - reportID: string; - /** The report currently being looked at */ report: OnyxEntry; - /** Is composer full size */ - isComposerFullSize?: boolean; - - /** Whether user interactions should be disabled */ - disabled?: boolean; - - /** Height of the list which the composer is part of */ - listHeight?: number; - /** The type of action that's pending */ pendingAction?: OnyxCommon.PendingAction; /** Whether the report is ready for display */ isReportReadyForDisplay?: boolean; - - /** Whether the chat is empty */ - isEmptyChat?: boolean; - - /** The last report action */ - lastReportAction?: OnyxTypes.ReportAction; }; // We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will @@ -268,7 +252,7 @@ function ReportActionCompose({ }, []); const addAttachment = useCallback( - (file: Partial) => { + (file: FileObject) => { playSound(SOUNDS.DONE); const newComment = composerRef?.current?.prepareCommentAndResetComposer(); Report.addAttachment(reportID, file, newComment); diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx index c84bd3786610..1abc6567bc7b 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater/index.tsx @@ -43,5 +43,6 @@ SilentCommentUpdater.displayName = 'SilentCommentUpdater'; export default withOnyx({ comment: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, + initialValue: '', }, })(SilentCommentUpdater); diff --git a/src/types/modules/pusher.d.ts b/src/types/modules/pusher.d.ts index 676d7a7ee2fc..e9aa50085e8d 100644 --- a/src/types/modules/pusher.d.ts +++ b/src/types/modules/pusher.d.ts @@ -9,6 +9,6 @@ declare global { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface File { source: string; - uri: string; + uri?: string; } } diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 7071c211c2e9..d339d666a411 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -169,7 +169,7 @@ type ReportActionBase = { isFirstItem?: boolean; /** Informations about attachments of report action */ - attachmentInfo?: Partial | EmptyObject; + attachmentInfo?: FileObject | EmptyObject; /** Receipt tied to report action */ receipt?: Receipt; From 49a0f0b50036a65c6442e77c15c9adc28e95f612 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 15 Feb 2024 00:53:14 +0800 Subject: [PATCH 087/213] prettier --- 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 e56d15cccc24..ca220965df48 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,3 +1,4 @@ +import {ParamListBase, StackNavigationState} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import fastMerge from 'expensify-common/lib/fastMerge'; @@ -59,7 +60,6 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; -import { ParamListBase, StackNavigationState } from '@react-navigation/native'; type MoneyRequestRoute = StackScreenProps['route']; From e5751da59a3f651050c5f757ebc555f16f246642 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 15 Feb 2024 00:57:34 +0800 Subject: [PATCH 088/213] lint --- 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 ca220965df48..c3a3abc7368a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,4 +1,4 @@ -import {ParamListBase, StackNavigationState} from '@react-navigation/native'; +import type {ParamListBase, StackNavigationState} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import fastMerge from 'expensify-common/lib/fastMerge'; From fe5465074a8ab1acfbc6b207965e4cad4e093e03 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 15 Feb 2024 14:55:53 +0700 Subject: [PATCH 089/213] lint fix --- 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 589bb7bff15b..f6fb82990968 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -377,7 +377,7 @@ function needsToBeManuallySubmitted(iouReport: OnyxTypes.Report) { /** * Return the object to update hasOutstandingChildRequest */ -function getOutstandingChildRequest(policy: OnyxEntry | EmptyObject, iouReport: OnyxTypes.Report) { +function getOutstandingChildRequest(policy: OnyxEntry | EmptyObject, iouReport: OnyxTypes.Report): OutstandingChildRequest { if (!needsToBeManuallySubmitted(iouReport)) { return { hasOutstandingChildRequest: false, From d7708c104d60b0295cb382c617646e3820d9b4e7 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 15 Feb 2024 14:33:50 +0100 Subject: [PATCH 090/213] fix: removed uncessesary condition --- .../MoneyRequestConfirmationList.tsx | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index de289f1fba3b..8430dd66d3c8 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -262,7 +262,7 @@ function MoneyRequestConfirmationList({ const shouldShowTags = isPolicyExpenseChat && (iouTag || OptionsListUtils.hasEnabledOptions(Object.values(policyTagList ?? {}))); // A flag for showing tax fields - tax rate and tax amount - const shouldShowTax = isPolicyExpenseChat && policy?.isTaxTrackingEnabled; + const shouldShowTax = isPolicyExpenseChat && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled); // A flag for showing the billable field const shouldShowBillable = !policy?.disabledFields?.defaultBillable ?? true; @@ -457,7 +457,7 @@ function MoneyRequestConfirmationList({ translate, toLocaleDigit, ); - IOU.setMoneyRequestMerchant_temporaryForRefactor(transactionID ?? '', distanceMerchant); + IOU.setMoneyRequestMerchant(transactionID ?? '', distanceMerchant, false); }, [hasRoute, distance, mileageRate?.unit, mileageRate?.rate, mileageRate?.currency, translate, toLocaleDigit, isDistanceRequest, transactionID, isDistanceRequestWithPendingRoute]); const selectParticipant = useCallback( @@ -762,11 +762,15 @@ function MoneyRequestConfirmationList({ style={styles.moneyRequestMenuItem} titleStyle={styles.flex1} onPress={() => { - if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.MERCHANT)); - return; - } - Navigation.navigate(ROUTES.MONEY_REQUEST_MERCHANT.getRoute(iouType, reportID)); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute( + CONST.IOU.ACTION.EDIT, + iouType, + transactionID ?? '', + reportID ?? '', + Navigation.getActiveRouteWithoutParams(), + ), + ); }} disabled={didConfirm} interactive={!isReadOnly} @@ -805,22 +809,9 @@ function MoneyRequestConfirmationList({ description={policyTagListName} numberOfLinesTitle={2} onPress={() => { - if (isEditingSplitBill) { - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( - CONST.IOU.ACTION.EDIT, - CONST.IOU.TYPE.SPLIT, - transaction?.transactionID ?? '', - reportID ?? '', - Navigation.getActiveRouteWithoutParams(), - ), - ); - return; - } - Navigation.navigate( ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( - CONST.IOU.ACTION.CREATE, + CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID ?? '', reportID ?? '', From b4a2e1b1a750a6d3aed2a2290dae2fadf4e7fd07 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Fri, 16 Feb 2024 12:47:26 +0100 Subject: [PATCH 091/213] Migrate RoomNameInput to TypeScript --- .../{index.native.js => index.native.tsx} | 55 ++++++++++------- .../RoomNameInput/{index.js => index.tsx} | 59 ++++++++++++------- .../RoomNameInput/roomNameInputPropTypes.js | 58 ------------------ src/components/RoomNameInput/types.ts | 22 +++++++ 4 files changed, 94 insertions(+), 100 deletions(-) rename src/components/RoomNameInput/{index.native.js => index.native.tsx} (57%) rename src/components/RoomNameInput/{index.js => index.tsx} (62%) delete mode 100644 src/components/RoomNameInput/roomNameInputPropTypes.js create mode 100644 src/components/RoomNameInput/types.ts diff --git a/src/components/RoomNameInput/index.native.js b/src/components/RoomNameInput/index.native.tsx similarity index 57% rename from src/components/RoomNameInput/index.native.js rename to src/components/RoomNameInput/index.native.tsx index bae347fca3d2..f8055b71fe84 100644 --- a/src/components/RoomNameInput/index.native.js +++ b/src/components/RoomNameInput/index.native.tsx @@ -1,26 +1,41 @@ +import type {ForwardedRef} from 'react'; import React from 'react'; -import _ from 'underscore'; +import type {NativeSyntheticEvent, TextInputChangeEventData} from 'react-native'; import TextInput from '@components/TextInput'; +import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import getOperatingSystem from '@libs/getOperatingSystem'; import * as RoomNameInputUtils from '@libs/RoomNameInputUtils'; import CONST from '@src/CONST'; -import * as roomNameInputPropTypes from './roomNameInputPropTypes'; +import type RoomNameInputProps from './types'; -function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, onSubmitEditing, returnKeyType, shouldDelayFocus}) { +function RoomNameInput({ + isFocused, + autoFocus, + disabled, + errorText, + forwardedRef, + value, + onBlur, + onChangeText, + onInputChange, + onSubmitEditing, + returnKeyType, + shouldDelayFocus, +}: RoomNameInputProps) { const {translate} = useLocalize(); /** * Calls the onChangeText callback with a modified room name - * @param {Event} event + * @param event */ - const setModifiedRoomName = (event) => { + const setModifiedRoomName = (event: NativeSyntheticEvent) => { const roomName = event.nativeEvent.text; const modifiedRoomName = RoomNameInputUtils.modifyRoomName(roomName); - onChangeText(modifiedRoomName); + onChangeText?.(modifiedRoomName); // if custom component has onInputChange, use it to trigger changes (Form input) - if (_.isFunction(onInputChange)) { + if (typeof onInputChange === 'function') { onInputChange(modifiedRoomName); } }; @@ -33,15 +48,15 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, disabled={disabled} label={translate('newRoomPage.roomName')} accessibilityLabel={translate('newRoomPage.roomName')} - role={CONST.ACCESSIBILITY_ROLE.TEXT} + role={CONST.ROLE.PRESENTATION} prefixCharacter={CONST.POLICY.ROOM_PREFIX} placeholder={translate('newRoomPage.social')} onChange={setModifiedRoomName} - value={value.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice. + value={value?.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice. errorText={errorText} maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449 - onBlur={(event) => isFocused && onBlur(event)} + onBlur={(event) => isFocused && onBlur?.(event)} onSubmitEditing={onSubmitEditing} returnKeyType={returnKeyType} autoFocus={isFocused && autoFocus} @@ -51,18 +66,18 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, ); } -RoomNameInput.propTypes = roomNameInputPropTypes.propTypes; -RoomNameInput.defaultProps = roomNameInputPropTypes.defaultProps; RoomNameInput.displayName = 'RoomNameInput'; -const RoomNameInputWithRef = React.forwardRef((props, ref) => ( - -)); +function RoomNameInputWithRef(props: Omit, ref: ForwardedRef) { + return ( + + ); +} RoomNameInputWithRef.displayName = 'RoomNameInputWithRef'; -export default RoomNameInputWithRef; +export default React.forwardRef(RoomNameInputWithRef); diff --git a/src/components/RoomNameInput/index.js b/src/components/RoomNameInput/index.tsx similarity index 62% rename from src/components/RoomNameInput/index.js rename to src/components/RoomNameInput/index.tsx index e3c5a86ff945..b82927322bec 100644 --- a/src/components/RoomNameInput/index.js +++ b/src/components/RoomNameInput/index.tsx @@ -1,27 +1,41 @@ import React, {useState} from 'react'; -import _ from 'underscore'; +import type {ForwardedRef} from 'react'; +import type {NativeSyntheticEvent, TextInputChangeEventData} from 'react-native'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import * as RoomNameInputUtils from '@libs/RoomNameInputUtils'; +import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; import CONST from '@src/CONST'; -import * as roomNameInputPropTypes from './roomNameInputPropTypes'; +import type RoomNameInputProps from './types'; -function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, onSubmitEditing, returnKeyType, shouldDelayFocus}) { +function RoomNameInput({ + isFocused, + autoFocus, + disabled, + errorText, + forwardedRef, + value, + onBlur, + onChangeText, + onInputChange, + onSubmitEditing, + returnKeyType, + shouldDelayFocus, + inputID, +}: RoomNameInputProps) { const {translate} = useLocalize(); - - const [selection, setSelection] = useState(); + const [selection, setSelection] = useState<{start: number; end: number}>({start: 0, end: 0}); /** * Calls the onChangeText callback with a modified room name - * @param {Event} event */ - const setModifiedRoomName = (event) => { + const setModifiedRoomName = (event: NativeSyntheticEvent) => { const roomName = event.nativeEvent.text; const modifiedRoomName = RoomNameInputUtils.modifyRoomName(roomName); - onChangeText(modifiedRoomName); + onChangeText?.(modifiedRoomName); // if custom component has onInputChange, use it to trigger changes (Form input) - if (_.isFunction(onInputChange)) { + if (typeof onInputChange === 'function') { onInputChange(modifiedRoomName); } @@ -30,7 +44,7 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, // If it is, then the room name is valid (does not contain forbidden characters) – no action required // If not, then the room name contains invalid characters, and we must adjust the cursor position manually // Read more: https://github.com/Expensify/App/issues/12741 - const oldRoomNameWithHash = value || ''; + const oldRoomNameWithHash = value ?? ''; const newRoomNameWithHash = `${CONST.POLICY.ROOM_PREFIX}${roomName}`; if (modifiedRoomName !== newRoomNameWithHash) { const offset = modifiedRoomName.length - oldRoomNameWithHash.length; @@ -43,20 +57,21 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, setSelection(event.nativeEvent.selection)} onSubmitEditing={onSubmitEditing} returnKeyType={returnKeyType} errorText={errorText} autoCapitalize="none" - onBlur={(event) => isFocused && onBlur(event)} + onBlur={(event) => isFocused && onBlur?.(event)} shouldDelayFocus={shouldDelayFocus} autoFocus={isFocused && autoFocus} maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} @@ -66,18 +81,18 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, ); } -RoomNameInput.propTypes = roomNameInputPropTypes.propTypes; -RoomNameInput.defaultProps = roomNameInputPropTypes.defaultProps; RoomNameInput.displayName = 'RoomNameInput'; -const RoomNameInputWithRef = React.forwardRef((props, ref) => ( - -)); +function RoomNameInputWithRef(props: Omit, ref: ForwardedRef) { + return ( + + ); +} RoomNameInputWithRef.displayName = 'RoomNameInputWithRef'; -export default RoomNameInputWithRef; +export default React.forwardRef(RoomNameInputWithRef); diff --git a/src/components/RoomNameInput/roomNameInputPropTypes.js b/src/components/RoomNameInput/roomNameInputPropTypes.js deleted file mode 100644 index aa547354a4c5..000000000000 --- a/src/components/RoomNameInput/roomNameInputPropTypes.js +++ /dev/null @@ -1,58 +0,0 @@ -import PropTypes from 'prop-types'; -import refPropTypes from '@components/refPropTypes'; -import {translatableTextPropTypes} from '@libs/Localize'; - -const propTypes = { - /** Callback to execute when the text input is modified correctly */ - onChangeText: PropTypes.func, - - /** Room name to show in input field. This should include the '#' already prefixed to the name */ - value: PropTypes.string, - - /** Whether we should show the input as disabled */ - disabled: PropTypes.bool, - - /** Error text to show */ - errorText: translatableTextPropTypes, - - /** A ref forwarded to the TextInput */ - forwardedRef: refPropTypes, - - /** On submit editing handler provided by the FormProvider */ - onSubmitEditing: PropTypes.func, - - /** Return key type provided to the TextInput */ - returnKeyType: PropTypes.string, - - /** The ID used to uniquely identify the input in a Form */ - inputID: PropTypes.string, - - /** Callback that is called when the text input is blurred */ - onBlur: PropTypes.func, - - /** AutoFocus */ - autoFocus: PropTypes.bool, - - /** Whether we should wait before focusing the TextInput, useful when using transitions on Android */ - shouldDelayFocus: PropTypes.bool, - - /** Whether navigation is focused */ - isFocused: PropTypes.bool.isRequired, -}; - -const defaultProps = { - onChangeText: () => {}, - value: '', - disabled: false, - errorText: '', - forwardedRef: () => {}, - onSubmitEditing: () => {}, - returnKeyType: undefined, - - inputID: undefined, - onBlur: () => {}, - autoFocus: false, - shouldDelayFocus: false, -}; - -export {propTypes, defaultProps}; diff --git a/src/components/RoomNameInput/types.ts b/src/components/RoomNameInput/types.ts new file mode 100644 index 000000000000..6c582e5d07f6 --- /dev/null +++ b/src/components/RoomNameInput/types.ts @@ -0,0 +1,22 @@ +import type {ForwardedRef} from 'react'; +import type {NativeSyntheticEvent, ReturnKeyTypeOptions, TextInputFocusEventData, TextInputSubmitEditingEventData} from 'react-native'; +import type {MaybePhraseKey} from '@libs/Localize'; +import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; + +type RoomNameInputProps = { + value?: string; + disabled?: boolean; + errorText?: MaybePhraseKey; + onChangeText?: (value: string) => void; + forwardedRef?: ForwardedRef; + onSubmitEditing?: (event: NativeSyntheticEvent) => void; + onInputChange?: (value: string) => void; + returnKeyType?: ReturnKeyTypeOptions; + inputID?: string; + onBlur?: (event: NativeSyntheticEvent) => void; + autoFocus?: boolean; + shouldDelayFocus?: boolean; + isFocused?: boolean; +}; + +export default RoomNameInputProps; From c46a4b4c74519f7aa94d083196b7c63b6d88511d Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Fri, 16 Feb 2024 14:45:34 +0100 Subject: [PATCH 092/213] Add missing inputID to native --- src/components/RoomNameInput/index.native.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/RoomNameInput/index.native.tsx b/src/components/RoomNameInput/index.native.tsx index f8055b71fe84..6651acdf2061 100644 --- a/src/components/RoomNameInput/index.native.tsx +++ b/src/components/RoomNameInput/index.native.tsx @@ -22,6 +22,7 @@ function RoomNameInput({ onSubmitEditing, returnKeyType, shouldDelayFocus, + inputID, }: RoomNameInputProps) { const {translate} = useLocalize(); @@ -46,6 +47,7 @@ function RoomNameInput({ Date: Fri, 16 Feb 2024 15:34:36 +0100 Subject: [PATCH 093/213] Add RoomNameInput type to ValidInputs --- src/components/Form/types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index cb2740fdafe5..1e92f106b459 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -7,6 +7,7 @@ import type AmountTextInput from '@components/AmountTextInput'; import type CheckboxWithLabel from '@components/CheckboxWithLabel'; import type CountrySelector from '@components/CountrySelector'; import type Picker from '@components/Picker'; +import type RoomNameInput from '@components/RoomNameInput'; import type SingleChoiceQuestion from '@components/SingleChoiceQuestion'; import type StatePicker from '@components/StatePicker'; import type TextInput from '@components/TextInput'; @@ -32,7 +33,8 @@ type ValidInputs = | typeof CountrySelector | typeof AmountForm | typeof BusinessTypePicker - | typeof StatePicker; + | typeof StatePicker + | typeof RoomNameInput; type ValueTypeKey = 'string' | 'boolean' | 'date'; type ValueTypeMap = { From 6a7e8df74d649463844bf90b9755de2441db904c Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Fri, 16 Feb 2024 16:04:29 +0100 Subject: [PATCH 094/213] Remove forwardedRef from props --- src/components/RoomNameInput/index.native.tsx | 44 +++------------- src/components/RoomNameInput/index.tsx | 50 ++++--------------- src/components/RoomNameInput/types.ts | 3 -- 3 files changed, 17 insertions(+), 80 deletions(-) diff --git a/src/components/RoomNameInput/index.native.tsx b/src/components/RoomNameInput/index.native.tsx index 6651acdf2061..60fc6ade0082 100644 --- a/src/components/RoomNameInput/index.native.tsx +++ b/src/components/RoomNameInput/index.native.tsx @@ -9,21 +9,7 @@ import * as RoomNameInputUtils from '@libs/RoomNameInputUtils'; import CONST from '@src/CONST'; import type RoomNameInputProps from './types'; -function RoomNameInput({ - isFocused, - autoFocus, - disabled, - errorText, - forwardedRef, - value, - onBlur, - onChangeText, - onInputChange, - onSubmitEditing, - returnKeyType, - shouldDelayFocus, - inputID, -}: RoomNameInputProps) { +function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, ref: ForwardedRef) { const {translate} = useLocalize(); /** @@ -45,41 +31,25 @@ function RoomNameInput({ return ( isFocused && onBlur?.(event)} - onSubmitEditing={onSubmitEditing} - returnKeyType={returnKeyType} autoFocus={isFocused && autoFocus} autoCapitalize="none" - shouldDelayFocus={shouldDelayFocus} + onChange={setModifiedRoomName} + keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449 /> ); } RoomNameInput.displayName = 'RoomNameInput'; -function RoomNameInputWithRef(props: Omit, ref: ForwardedRef) { - return ( - - ); -} - -RoomNameInputWithRef.displayName = 'RoomNameInputWithRef'; - -export default React.forwardRef(RoomNameInputWithRef); +export default React.forwardRef(RoomNameInput); diff --git a/src/components/RoomNameInput/index.tsx b/src/components/RoomNameInput/index.tsx index b82927322bec..a6b8e4d83091 100644 --- a/src/components/RoomNameInput/index.tsx +++ b/src/components/RoomNameInput/index.tsx @@ -8,21 +8,7 @@ import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/typ import CONST from '@src/CONST'; import type RoomNameInputProps from './types'; -function RoomNameInput({ - isFocused, - autoFocus, - disabled, - errorText, - forwardedRef, - value, - onBlur, - onChangeText, - onInputChange, - onSubmitEditing, - returnKeyType, - shouldDelayFocus, - inputID, -}: RoomNameInputProps) { +function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, ref: ForwardedRef) { const {translate} = useLocalize(); const [selection, setSelection] = useState<{start: number; end: number}>({start: 0, end: 0}); @@ -55,26 +41,22 @@ function RoomNameInput({ return ( setSelection(event.nativeEvent.selection)} - onSubmitEditing={onSubmitEditing} - returnKeyType={returnKeyType} - errorText={errorText} - autoCapitalize="none" + maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} onBlur={(event) => isFocused && onBlur?.(event)} - shouldDelayFocus={shouldDelayFocus} autoFocus={isFocused && autoFocus} - maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} + autoCapitalize="none" + onChange={setModifiedRoomName} + onSelectionChange={(event) => setSelection(event.nativeEvent.selection)} + selection={selection} spellCheck={false} shouldInterceptSwipe /> @@ -83,16 +65,4 @@ function RoomNameInput({ RoomNameInput.displayName = 'RoomNameInput'; -function RoomNameInputWithRef(props: Omit, ref: ForwardedRef) { - return ( - - ); -} - -RoomNameInputWithRef.displayName = 'RoomNameInputWithRef'; - -export default React.forwardRef(RoomNameInputWithRef); +export default React.forwardRef(RoomNameInput); diff --git a/src/components/RoomNameInput/types.ts b/src/components/RoomNameInput/types.ts index 6c582e5d07f6..f90051de6fd0 100644 --- a/src/components/RoomNameInput/types.ts +++ b/src/components/RoomNameInput/types.ts @@ -1,14 +1,11 @@ -import type {ForwardedRef} from 'react'; import type {NativeSyntheticEvent, ReturnKeyTypeOptions, TextInputFocusEventData, TextInputSubmitEditingEventData} from 'react-native'; import type {MaybePhraseKey} from '@libs/Localize'; -import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; type RoomNameInputProps = { value?: string; disabled?: boolean; errorText?: MaybePhraseKey; onChangeText?: (value: string) => void; - forwardedRef?: ForwardedRef; onSubmitEditing?: (event: NativeSyntheticEvent) => void; onInputChange?: (value: string) => void; returnKeyType?: ReturnKeyTypeOptions; From 5094741a8ea30bab996239e46269cb2b7213ad48 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Fri, 16 Feb 2024 16:14:24 +0100 Subject: [PATCH 095/213] Add default values --- src/components/RoomNameInput/index.native.tsx | 7 ++++++- src/components/RoomNameInput/index.tsx | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/RoomNameInput/index.native.tsx b/src/components/RoomNameInput/index.native.tsx index 60fc6ade0082..2459fd6c8553 100644 --- a/src/components/RoomNameInput/index.native.tsx +++ b/src/components/RoomNameInput/index.native.tsx @@ -9,7 +9,10 @@ import * as RoomNameInputUtils from '@libs/RoomNameInputUtils'; import CONST from '@src/CONST'; import type RoomNameInputProps from './types'; -function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, ref: ForwardedRef) { +function RoomNameInput( + {disabled = false, autoFocus = false, shouldDelayFocus = false, isFocused, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, + ref: ForwardedRef, +) { const {translate} = useLocalize(); /** @@ -34,6 +37,7 @@ function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInp // eslint-disable-next-line react/jsx-props-no-spreading {...props} ref={ref} + disabled={disabled} label={translate('newRoomPage.roomName')} accessibilityLabel={translate('newRoomPage.roomName')} role={CONST.ROLE.PRESENTATION} @@ -43,6 +47,7 @@ function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInp maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} onBlur={(event) => isFocused && onBlur?.(event)} autoFocus={isFocused && autoFocus} + shouldDelayFocus={shouldDelayFocus} autoCapitalize="none" onChange={setModifiedRoomName} keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449 diff --git a/src/components/RoomNameInput/index.tsx b/src/components/RoomNameInput/index.tsx index a6b8e4d83091..2606015116bb 100644 --- a/src/components/RoomNameInput/index.tsx +++ b/src/components/RoomNameInput/index.tsx @@ -8,7 +8,10 @@ import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/typ import CONST from '@src/CONST'; import type RoomNameInputProps from './types'; -function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, ref: ForwardedRef) { +function RoomNameInput( + {disabled = false, autoFocus = false, shouldDelayFocus = false, isFocused, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, + ref: ForwardedRef, +) { const {translate} = useLocalize(); const [selection, setSelection] = useState<{start: number; end: number}>({start: 0, end: 0}); @@ -44,6 +47,7 @@ function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInp // eslint-disable-next-line react/jsx-props-no-spreading {...props} ref={ref} + disabled={disabled} label={translate('newRoomPage.roomName')} accessibilityLabel={translate('newRoomPage.roomName')} role={CONST.ROLE.PRESENTATION} @@ -53,6 +57,7 @@ function RoomNameInput({isFocused, autoFocus, value, onBlur, onChangeText, onInp maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} onBlur={(event) => isFocused && onBlur?.(event)} autoFocus={isFocused && autoFocus} + shouldDelayFocus={shouldDelayFocus} autoCapitalize="none" onChange={setModifiedRoomName} onSelectionChange={(event) => setSelection(event.nativeEvent.selection)} From a7a74e0c4c98af97add0c978c6227c6c416250fd Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 19 Feb 2024 07:55:41 +0100 Subject: [PATCH 096/213] change file extension to .ts --- .../utils/{getCurrentBranchName.js => getCurrentBranchName.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/e2e/utils/{getCurrentBranchName.js => getCurrentBranchName.ts} (100%) diff --git a/tests/e2e/utils/getCurrentBranchName.js b/tests/e2e/utils/getCurrentBranchName.ts similarity index 100% rename from tests/e2e/utils/getCurrentBranchName.js rename to tests/e2e/utils/getCurrentBranchName.ts From 2d4d666d1317fc39cd82e28a407d4f1eb8906f55 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Mon, 19 Feb 2024 09:33:25 +0100 Subject: [PATCH 097/213] fix: add podfile --- ios/Podfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 80933065c450..e4f988173f40 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1434,7 +1434,7 @@ PODS: - React-Core - RNReactNativeHapticFeedback (2.2.0): - React-Core - - RNReanimated (3.6.1): + - RNReanimated (3.7.0): - glog - RCT-Folly (= 2022.05.16.00) - React-Core @@ -1988,7 +1988,7 @@ SPEC CHECKSUMS: rnmapbox-maps: fcf7f1cbdc8bd7569c267d07284e8a5c7bee06ed RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa RNReactNativeHapticFeedback: ec56a5f81c3941206fd85625fa669ffc7b4545f9 - RNReanimated: 57f436e7aa3d277fbfed05e003230b43428157c0 + RNReanimated: 7d6d32f238f914f13d9d6fb45c0aef557f7f901e RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2 RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a From 51f34ab996e756ef41e07702a37a8bf94e596b86 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Mon, 19 Feb 2024 10:34:19 +0100 Subject: [PATCH 098/213] feat: rename patches --- ... react-native-reanimated+3.7.0+001+fix-boost-dependency.patch} | 0 ...reanimated+3.6.1.patch => react-native-reanimated+3.7.0.patch} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename patches/{react-native-reanimated+3.6.1+001+fix-boost-dependency.patch => react-native-reanimated+3.7.0+001+fix-boost-dependency.patch} (100%) rename patches/{react-native-reanimated+3.6.1.patch => react-native-reanimated+3.7.0.patch} (100%) diff --git a/patches/react-native-reanimated+3.6.1+001+fix-boost-dependency.patch b/patches/react-native-reanimated+3.7.0+001+fix-boost-dependency.patch similarity index 100% rename from patches/react-native-reanimated+3.6.1+001+fix-boost-dependency.patch rename to patches/react-native-reanimated+3.7.0+001+fix-boost-dependency.patch diff --git a/patches/react-native-reanimated+3.6.1.patch b/patches/react-native-reanimated+3.7.0.patch similarity index 100% rename from patches/react-native-reanimated+3.6.1.patch rename to patches/react-native-reanimated+3.7.0.patch From 2e8bcc800fd0243db08cc974a7f23011131e164a Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Mon, 19 Feb 2024 11:53:54 +0100 Subject: [PATCH 099/213] Make isFocused required prop and adjust ValidInputs comment --- src/components/Form/types.ts | 2 +- src/components/RoomNameInput/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index 1e92f106b459..6ef1d41118f8 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -21,7 +21,7 @@ import type {BaseForm} from '@src/types/form/Form'; * when adding new inputs or removing old ones. * * TODO: Add remaining inputs here once these components are migrated to Typescript: - * EmojiPickerButtonDropdown | RoomNameInput | ValuePicker + * EmojiPickerButtonDropdown | ValuePicker */ type ValidInputs = | typeof TextInput diff --git a/src/components/RoomNameInput/types.ts b/src/components/RoomNameInput/types.ts index f90051de6fd0..80f08a01e472 100644 --- a/src/components/RoomNameInput/types.ts +++ b/src/components/RoomNameInput/types.ts @@ -13,7 +13,7 @@ type RoomNameInputProps = { onBlur?: (event: NativeSyntheticEvent) => void; autoFocus?: boolean; shouldDelayFocus?: boolean; - isFocused?: boolean; + isFocused: boolean; }; export default RoomNameInputProps; From a4c949471bcebef0e4cb1495c93866f5e6b0d903 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 20 Feb 2024 15:19:10 +0800 Subject: [PATCH 100/213] disable tab only on start page --- src/pages/iou/request/IOURequestStartPage.js | 27 +++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.js index 3d80ab89347d..cb6ff07c819d 100644 --- a/src/pages/iou/request/IOURequestStartPage.js +++ b/src/pages/iou/request/IOURequestStartPage.js @@ -1,3 +1,4 @@ +import {useFocusEffect} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; @@ -88,18 +89,20 @@ function IOURequestStartPage({ [reportID], ); - useEffect(() => { - const handler = (event) => { - if (event.code !== CONST.KEYBOARD_SHORTCUTS.TAB.shortcutKey) { - return; - } - event.preventDefault(); - event.stopPropagation(); - }; - KeyDownPressListener.addKeyDownPressListener(handler); - - return () => KeyDownPressListener.removeKeyDownPressListener(handler); - }, []); + useFocusEffect( + useCallback(() => { + const handler = (event) => { + if (event.code !== CONST.KEYBOARD_SHORTCUTS.TAB.shortcutKey) { + return; + } + event.preventDefault(); + event.stopPropagation(); + }; + KeyDownPressListener.addKeyDownPressListener(handler); + + return () => KeyDownPressListener.removeKeyDownPressListener(handler); + }, []), + ); // Clear out the temporary money request if the reportID in the URL has changed from the transaction's reportID useEffect(() => { From d5aba808e5b9306983bf41a98ed892ffd1945227 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 20 Feb 2024 11:22:00 +0100 Subject: [PATCH 101/213] fix: displaying thumbnail --- src/components/MoneyRequestConfirmationList.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index b61e82d76538..252b0ad44384 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -73,7 +73,7 @@ type MoneyRequestConfirmationListOnyxProps = { policyCategories: OnyxEntry; /** Collection of tags attached to a policy */ - policyTags: OnyxEntry; + policyTags: OnyxEntry; /** The policy of root parent report */ policy: OnyxEntry; @@ -253,7 +253,6 @@ function MoneyRequestConfirmationList({ // In Send Money and Split Bill with Scan flow, we don't allow the Merchant or Date to be edited. For distance requests, don't show the merchant as there's already another "Distance" menu item const shouldShowDate = shouldShowAllFields && !isTypeSend && !isSplitWithScan; const shouldShowMerchant = shouldShowAllFields && !isTypeSend && !isDistanceRequest && !isSplitWithScan; - const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); // A flag for showing the tags field const shouldShowTags = isPolicyExpenseChat && (iouTag || OptionsListUtils.hasEnabledTags(policyTagLists)); @@ -600,7 +599,7 @@ function MoneyRequestConfirmationList({ styles.mb2, ]); - const receiptData = ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename); + const receiptData = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename) : null; return ( // @ts-expect-error TODO: Remove this once OptionsSelector (https://github.com/Expensify/App/issues/25125) is migrated to TypeScript. )} - {receiptData.image || receiptData.thumbnail ? ( + {receiptData?.image ?? receiptData?.thumbnail ? ( Date: Wed, 21 Feb 2024 15:51:17 +0800 Subject: [PATCH 102/213] add concierge page to dismiss modal case --- src/libs/Navigation/dismissModal.ts | 1 + src/libs/Navigation/dismissModalWithReport.ts | 1 + src/pages/ConciergePage.tsx | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/dismissModal.ts b/src/libs/Navigation/dismissModal.ts index 484008d2e070..1f46f5c37193 100644 --- a/src/libs/Navigation/dismissModal.ts +++ b/src/libs/Navigation/dismissModal.ts @@ -25,6 +25,7 @@ function dismissModal(navigationRef: NavigationContainerRef) case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: case SCREENS.NOT_FOUND: case SCREENS.REPORT_ATTACHMENTS: + case SCREENS.CONCIERGE: navigationRef.dispatch({...StackActions.pop(), target: state.key}); break; default: { diff --git a/src/libs/Navigation/dismissModalWithReport.ts b/src/libs/Navigation/dismissModalWithReport.ts index b708819cdf9c..4fc82f76df86 100644 --- a/src/libs/Navigation/dismissModalWithReport.ts +++ b/src/libs/Navigation/dismissModalWithReport.ts @@ -38,6 +38,7 @@ function dismissModalWithReport(targetReport: Report | EmptyObject, navigationRe case NAVIGATORS.RIGHT_MODAL_NAVIGATOR: case SCREENS.NOT_FOUND: case SCREENS.REPORT_ATTACHMENTS: + case SCREENS.CONCIERGE: // If we are not in the target report, we need to navigate to it after dismissing the modal if (targetReport.reportID !== getTopmostReportId(state)) { const reportState = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReport.reportID)); diff --git a/src/pages/ConciergePage.tsx b/src/pages/ConciergePage.tsx index 251728866a54..feb1b9c54f50 100644 --- a/src/pages/ConciergePage.tsx +++ b/src/pages/ConciergePage.tsx @@ -28,8 +28,7 @@ function ConciergePage({session}: ConciergePageProps) { if (session && 'authToken' in session) { // Pop the concierge loading page before opening the concierge report. Navigation.isNavigationReady().then(() => { - Navigation.goBack(); - Report.navigateToConciergeChat(); + Report.navigateToConciergeChat(undefined, true); }); } else { Navigation.navigate(); From 67b39c13e33910a5ca59c7796203dfa229a81792 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 21 Feb 2024 13:20:00 +0100 Subject: [PATCH 103/213] migrate getCurrentBranchName to TypeScript --- tests/e2e/utils/getCurrentBranchName.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/utils/getCurrentBranchName.ts b/tests/e2e/utils/getCurrentBranchName.ts index 55df11010214..7ae958b08e13 100644 --- a/tests/e2e/utils/getCurrentBranchName.ts +++ b/tests/e2e/utils/getCurrentBranchName.ts @@ -1,6 +1,6 @@ import {execSync} from 'child_process'; -const getCurrentBranchName = () => { +const getCurrentBranchName = (): string => { const stdout = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8', }); From 2a71a34beacca8b099be1161eceb072b6aa821f0 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 21 Feb 2024 14:22:53 +0100 Subject: [PATCH 104/213] fix: typecheck --- .../ReportActionCompose/AttachmentPickerWithMenuItems.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 9888c5217e69..68c7f0883683 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -18,14 +18,12 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; -import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as IOU from '@userActions/IOU'; import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; type MoneyRequestOptions = Record, PopoverMenuItem>; @@ -126,12 +124,12 @@ function AttachmentPickerWithMenuItems({ [CONST.IOU.TYPE.SPLIT]: { icon: Expensicons.Receipt, text: translate('iou.splitBill'), - onSelected: () => IOU.startMoneyRequest_temporaryForRefactor(CONST.IOU.TYPE.SPLIT, report.reportID), + onSelected: () => IOU.startMoneyRequest_temporaryForRefactor(CONST.IOU.TYPE.SPLIT, report?.reportID ?? ''), }, [CONST.IOU.TYPE.REQUEST]: { icon: Expensicons.MoneyCircle, text: translate('iou.requestMoney'), - onSelected: () => IOU.startMoneyRequest_temporaryForRefactor(CONST.IOU.TYPE.REQUEST, report.reportID), + onSelected: () => IOU.startMoneyRequest_temporaryForRefactor(CONST.IOU.TYPE.REQUEST, report?.reportID ?? ''), }, [CONST.IOU.TYPE.SEND]: { icon: Expensicons.Send, From 82d5420100e18f3c0ab2bcd366ae1931aadb296d Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 21 Feb 2024 15:38:09 +0100 Subject: [PATCH 105/213] Fix typescript errors and refactor RoomNameInput code --- src/components/Form/InputWrapper.tsx | 6 +++++- src/components/RoomNameInput/index.native.tsx | 1 - src/components/RoomNameInput/index.tsx | 3 ++- src/libs/ComposerUtils/index.ts | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Form/InputWrapper.tsx b/src/components/Form/InputWrapper.tsx index 4d55de008516..b5535a2fe6c1 100644 --- a/src/components/Form/InputWrapper.tsx +++ b/src/components/Form/InputWrapper.tsx @@ -2,12 +2,16 @@ import type {ComponentPropsWithoutRef, ComponentType, ForwardedRef} from 'react' import React, {forwardRef, useContext} from 'react'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; import RoomNameInput from '@components/RoomNameInput'; +import type RoomNameInputProps from '@components/RoomNameInput/types'; import TextInput from '@components/TextInput'; +import type {BaseTextInputProps} from '@components/TextInput/BaseTextInput/types'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import FormContext from './FormContext'; import type {InputComponentBaseProps, InputComponentValueProps, ValidInputs, ValueTypeKey} from './types'; -const textInputBasedComponents: ComponentType[] = [TextInput, RoomNameInput]; +type TextInputBasedComponents = [ComponentType, ComponentType]; + +const textInputBasedComponents: TextInputBasedComponents = [TextInput, RoomNameInput]; type ComputedComponentSpecificRegistrationParams = { shouldSubmitForm: boolean; diff --git a/src/components/RoomNameInput/index.native.tsx b/src/components/RoomNameInput/index.native.tsx index 2459fd6c8553..6598015286e2 100644 --- a/src/components/RoomNameInput/index.native.tsx +++ b/src/components/RoomNameInput/index.native.tsx @@ -17,7 +17,6 @@ function RoomNameInput( /** * Calls the onChangeText callback with a modified room name - * @param event */ const setModifiedRoomName = (event: NativeSyntheticEvent) => { const roomName = event.nativeEvent.text; diff --git a/src/components/RoomNameInput/index.tsx b/src/components/RoomNameInput/index.tsx index 2606015116bb..cf9c1900ea3e 100644 --- a/src/components/RoomNameInput/index.tsx +++ b/src/components/RoomNameInput/index.tsx @@ -3,6 +3,7 @@ import type {ForwardedRef} from 'react'; import type {NativeSyntheticEvent, TextInputChangeEventData} from 'react-native'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; +import type {Selection} from '@libs/ComposerUtils'; import * as RoomNameInputUtils from '@libs/RoomNameInputUtils'; import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; import CONST from '@src/CONST'; @@ -13,7 +14,7 @@ function RoomNameInput( ref: ForwardedRef, ) { const {translate} = useLocalize(); - const [selection, setSelection] = useState<{start: number; end: number}>({start: 0, end: 0}); + const [selection, setSelection] = useState({start: 0, end: 0}); /** * Calls the onChangeText callback with a modified room name diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index 94bba5d0d00c..f2e940abeb73 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -50,3 +50,4 @@ function findCommonSuffixLength(str1: string, str2: string, cursorPosition: numb } export {getNumberOfLines, updateNumberOfLines, insertText, canSkipTriggerHotkeys, insertWhiteSpaceAtIndex, findCommonSuffixLength}; +export type {Selection}; From 59cb2e20b134d82b4c53736acf7809379a5bc871 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 21 Feb 2024 15:59:54 +0000 Subject: [PATCH 106/213] [TS migration] Migrate loginTest into Typescript --- tests/unit/{loginTest.js => loginTest.tsx} | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) rename tests/unit/{loginTest.js => loginTest.tsx} (84%) diff --git a/tests/unit/loginTest.js b/tests/unit/loginTest.tsx similarity index 84% rename from tests/unit/loginTest.js rename to tests/unit/loginTest.tsx index b63564ff2812..d5084299bb08 100644 --- a/tests/unit/loginTest.js +++ b/tests/unit/loginTest.tsx @@ -1,14 +1,12 @@ -/** - * @format - */ import React from 'react'; import 'react-native'; // Note: `react-test-renderer` renderer must be required after react-native. import renderer from 'react-test-renderer'; -import App from '../../src/App'; +import App from '@src/App'; // Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ + // eslint-disable-next-line @typescript-eslint/naming-convention __esModule: true, default: { ignoreLogs: jest.fn(), From 2af88861c50a9f5da5b506d25fe231bf7c806c9f Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 22 Feb 2024 16:01:31 +0700 Subject: [PATCH 107/213] update shouldShow condition correctly --- src/pages/ProfilePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index c6f3796e4c9f..2dc6b7c57251 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -142,7 +142,7 @@ function ProfilePage(props) { return ( - + Navigation.goBack(navigateBackTo)} From 02dab8fb631ee0d5b8419cae8c3d282dcf94a31b Mon Sep 17 00:00:00 2001 From: Tomasz Lesniakiewicz Date: Thu, 22 Feb 2024 14:47:11 +0100 Subject: [PATCH 108/213] fix: remove redundant recursive add call --- src/libs/Trie/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Trie/index.ts b/src/libs/Trie/index.ts index c23c7b814a22..a82c90c15772 100644 --- a/src/libs/Trie/index.ts +++ b/src/libs/Trie/index.ts @@ -31,7 +31,6 @@ class Trie { } if (!newNode.children[newWord[0]]) { newNode.children[newWord[0]] = new TrieNode(); - this.add(newWord.substring(1), metaData, newNode.children[newWord[0]], true); } this.add(newWord.substring(1), metaData, newNode.children[newWord[0]], true); } From 21be35da369fabb68de5590ebc676b5e4173b2a8 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 22 Feb 2024 18:09:52 +0100 Subject: [PATCH 109/213] Add on begin pan gesture event --- .../VideoPlayerControls/VolumeButton/index.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js index 1c1d042d3893..4cba4506dc0e 100644 --- a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js +++ b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js @@ -42,15 +42,20 @@ function VolumeButton({style, small}) { setSliderHeight(e.nativeEvent.layout.height); }; + const gestureEventHandler = (event) => { + const val = Math.floor((1 - event.y / sliderHeight) * 100) / 100; + volume.value = Math.min(Math.max(val, 0), 1); + }; + const pan = Gesture.Pan() - .onBegin(() => { + .onBegin((event) => { runOnJS(setIsSliderBeingUsed)(true); + gestureEventHandler(event); }) .onChange((event) => { - const val = Math.floor((1 - event.y / sliderHeight) * 100) / 100; - volume.value = Math.min(Math.max(val, 0), 1); + gestureEventHandler(event); }) - .onEnd(() => { + .onFinished(() => { runOnJS(setIsSliderBeingUsed)(false); }); From 47264892cf3ca09bceb5e2550dc84e8673e95d3f Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Thu, 22 Feb 2024 18:20:46 +0100 Subject: [PATCH 110/213] Cleanup --- .../VideoPlayer/VideoPlayerControls/VolumeButton/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js index 4cba4506dc0e..b0757eb7df68 100644 --- a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js +++ b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js @@ -55,7 +55,7 @@ function VolumeButton({style, small}) { .onChange((event) => { gestureEventHandler(event); }) - .onFinished(() => { + .onFinalize(() => { runOnJS(setIsSliderBeingUsed)(false); }); From 95f432f162593cae937504f15ebc9f925558b3e8 Mon Sep 17 00:00:00 2001 From: codinggeek2023 Date: Fri, 23 Feb 2024 00:20:36 +0530 Subject: [PATCH 111/213] Change finish to stop to avoid confusion --- src/components/DistanceEReceipt.tsx | 2 -- src/components/DistanceRequest/DistanceRequestRenderItem.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/DistanceEReceipt.tsx b/src/components/DistanceEReceipt.tsx index 9846cfa04257..941d63c1bf94 100644 --- a/src/components/DistanceEReceipt.tsx +++ b/src/components/DistanceEReceipt.tsx @@ -75,8 +75,6 @@ function DistanceEReceipt({transaction}: DistanceEReceiptProps) { let descriptionKey: TranslationPaths = 'distance.waypointDescription.stop'; if (index === 0) { descriptionKey = 'distance.waypointDescription.start'; - } else if (index === Object.keys(waypoints).length - 1) { - descriptionKey = 'distance.waypointDescription.finish'; } return ( diff --git a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx index a1f3efbf0291..57e4fb0b530e 100644 --- a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx +++ b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx @@ -42,7 +42,7 @@ function DistanceRequestRenderItem({waypoints, item = '', onSecondaryInteraction descriptionKey += 'start'; waypointIcon = Expensicons.DotIndicatorUnfilled; } else if (index === lastWaypointIndex) { - descriptionKey += 'finish'; + descriptionKey += 'stop'; waypointIcon = Expensicons.Location; } else { descriptionKey += 'stop'; From 2666da970998077583fedfa28edacb1d28c676dc Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 23 Feb 2024 03:44:49 +0200 Subject: [PATCH 112/213] Fix reimbursement account page back navigation --- src/pages/ReimbursementAccount/ReimbursementAccountPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index cc416a5cfbdd..3048f32ed1d4 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -468,7 +468,7 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} + onBackButtonPress={() => Navigation.goBack()} /> {errorText} From 850e9727c20d66d8f147382f0239a8b10f100e5d Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 23 Feb 2024 08:48:17 +0100 Subject: [PATCH 113/213] [TS migration] Migrate 'index.js' test --- src/libs/E2E/client.ts | 10 +++ tests/e2e/server/{index.js => index.ts} | 108 +++++++++++++----------- tests/e2e/utils/logger.js | 10 +-- 3 files changed, 70 insertions(+), 58 deletions(-) rename tests/e2e/server/{index.js => index.ts} (61%) diff --git a/src/libs/E2E/client.ts b/src/libs/E2E/client.ts index 265c55c4a230..f76bdf2ed9a5 100644 --- a/src/libs/E2E/client.ts +++ b/src/libs/E2E/client.ts @@ -3,10 +3,19 @@ import Routes from '../../../tests/e2e/server/routes'; import type {NetworkCacheMap, TestConfig} from './types'; type TestResult = { + /** Name of the test */ name: string; + + /** The branch where test were running */ branch?: string; + + /** Duration in milliseconds */ duration?: number; + + /** Optional, if set indicates that the test run failed and has no valid results. */ error?: string; + + /** Render count */ renderCount?: number; }; @@ -113,3 +122,4 @@ export default { updateNetworkCache, getNetworkCache, }; +export type {TestResult, NativeCommand}; diff --git a/tests/e2e/server/index.js b/tests/e2e/server/index.ts similarity index 61% rename from tests/e2e/server/index.js rename to tests/e2e/server/index.ts index 82152245d8e2..be3cd20c7213 100644 --- a/tests/e2e/server/index.js +++ b/tests/e2e/server/index.ts @@ -1,15 +1,42 @@ import {createServer} from 'http'; +import type {IncomingMessage, ServerResponse} from 'http'; +import type {NativeCommand, TestResult} from '@libs/E2E/client'; +import type {NetworkCacheMap, TestConfig} from '@libs/E2E/types'; import config from '../config'; import * as nativeCommands from '../nativeCommands'; import * as Logger from '../utils/logger'; import Routes from './routes'; -const PORT = process.env.PORT || config.SERVER_PORT; +type NetworkCache = { + appInstanceId: string; + cache: NetworkCacheMap; +}; + +type RequestData = TestResult | NativeCommand | NetworkCache; + +type TestStartedListener = (testConfig?: TestConfig) => void; + +type TestDoneListener = () => void; + +type TestResultListener = (testResult: TestResult) => void; + +type AddListener = (listener: TListener) => void; + +type ServerInstance = { + setTestConfig: (testConfig: TestConfig) => void; + addTestStartedListener: AddListener; + addTestResultListener: AddListener; + addTestDoneListener: AddListener; + start: () => Promise; + stop: () => Promise; +}; + +const PORT = process.env.PORT ?? config.SERVER_PORT; // Gets the request data as a string -const getReqData = (req) => { +const getReqData = (req: IncomingMessage): Promise => { let data = ''; - req.on('data', (chunk) => { + req.on('data', (chunk: string) => { data += chunk; }); @@ -21,16 +48,16 @@ const getReqData = (req) => { }; // Expects a POST request with JSON data. Returns parsed JSON data. -const getPostJSONRequestData = (req, res) => { +const getPostJSONRequestData = (req: IncomingMessage, res: ServerResponse): Promise | undefined => { if (req.method !== 'POST') { res.statusCode = 400; res.end('Unsupported method'); return; } - return getReqData(req).then((data) => { + return getReqData(req).then((data): TRequestData | undefined => { try { - return JSON.parse(data); + return JSON.parse(data) as TRequestData; } catch (e) { Logger.info('❌ Failed to parse request data', data); res.statusCode = 400; @@ -39,9 +66,9 @@ const getPostJSONRequestData = (req, res) => { }); }; -const createListenerState = () => { - const listeners = []; - const addListener = (listener) => { +const createListenerState = (): [TListener[], AddListener] => { + const listeners: TListener[] = []; + const addListener = (listener: TListener) => { listeners.push(listener); return () => { const index = listeners.indexOf(listener); @@ -54,20 +81,6 @@ const createListenerState = () => { return [listeners, addListener]; }; -/** - * The test result object that a client might submit to the server. - * @typedef TestResult - * @property {string} name - * @property {number} duration Milliseconds - * @property {string} [error] Optional, if set indicates that the test run failed and has no valid results. - */ - -/** - * @callback listener - * @param {TestResult} testResult - */ - -// eslint-disable-next-line valid-jsdoc /** * Creates a new http server. * The server just has two endpoints: @@ -78,35 +91,32 @@ const createListenerState = () => { * * It returns an instance to which you can add listeners for the test results, and test done events. */ -const createServerInstance = () => { - const [testStartedListeners, addTestStartedListener] = createListenerState(); - const [testResultListeners, addTestResultListener] = createListenerState(); - const [testDoneListeners, addTestDoneListener] = createListenerState(); - - let activeTestConfig; - const networkCache = {}; - - /** - * @param {TestConfig} testConfig - */ - const setTestConfig = (testConfig) => { +const createServerInstance = (): ServerInstance => { + const [testStartedListeners, addTestStartedListener] = createListenerState(); + const [testResultListeners, addTestResultListener] = createListenerState(); + const [testDoneListeners, addTestDoneListener] = createListenerState(); + + let activeTestConfig: TestConfig | undefined; + const networkCache: Record = {}; + + const setTestConfig = (testConfig: TestConfig) => { activeTestConfig = testConfig; }; - const server = createServer((req, res) => { + const server = createServer((req, res): ServerResponse | void => { res.statusCode = 200; switch (req.url) { case Routes.testConfig: { testStartedListeners.forEach((listener) => listener(activeTestConfig)); - if (activeTestConfig == null) { + if (!activeTestConfig) { throw new Error('No test config set'); } return res.end(JSON.stringify(activeTestConfig)); } case Routes.testResults: { - getPostJSONRequestData(req, res).then((data) => { - if (data == null) { + getPostJSONRequestData(req, res)?.then((data) => { + if (!data) { // The getPostJSONRequestData function already handled the response return; } @@ -128,9 +138,9 @@ const createServerInstance = () => { } case Routes.testNativeCommand: { - getPostJSONRequestData(req, res) - .then((data) => - nativeCommands.executeFromPayload(data.actionName, data.payload).then((status) => { + getPostJSONRequestData(req, res) + ?.then((data) => + nativeCommands.executeFromPayload(data?.actionName, data?.payload).then((status) => { if (status) { res.end('ok'); return; @@ -148,8 +158,8 @@ const createServerInstance = () => { } case Routes.testGetNetworkCache: { - getPostJSONRequestData(req, res).then((data) => { - const appInstanceId = data && data.appInstanceId; + getPostJSONRequestData(req, res)?.then((data) => { + const appInstanceId = data?.appInstanceId; if (!appInstanceId) { res.statusCode = 400; res.end('Invalid request missing appInstanceId'); @@ -164,9 +174,9 @@ const createServerInstance = () => { } case Routes.testUpdateNetworkCache: { - getPostJSONRequestData(req, res).then((data) => { - const appInstanceId = data && data.appInstanceId; - const cache = data && data.cache; + getPostJSONRequestData(req, res)?.then((data) => { + const appInstanceId = data?.appInstanceId; + const cache = data?.cache; if (!appInstanceId || !cache) { res.statusCode = 400; res.end('Invalid request missing appInstanceId or cache'); @@ -191,8 +201,8 @@ const createServerInstance = () => { addTestStartedListener, addTestResultListener, addTestDoneListener, - start: () => new Promise((resolve) => server.listen(PORT, resolve)), - stop: () => new Promise((resolve) => server.close(resolve)), + start: () => new Promise((resolve) => server.listen(PORT, resolve)), + stop: () => new Promise((resolve) => server.close(resolve)), }; }; diff --git a/tests/e2e/utils/logger.js b/tests/e2e/utils/logger.js index d0770b7aa8e4..6a39b4c10328 100644 --- a/tests/e2e/utils/logger.js +++ b/tests/e2e/utils/logger.js @@ -66,12 +66,4 @@ const error = (...args) => { log(...lines); }; -module.exports = { - log, - info, - warn, - note, - error, - success, - writeToLogFile, -}; +export {log, info, warn, note, error, success, writeToLogFile}; From 2bc705a3ea658f43b12a7d743debcd346413c33f Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 23 Feb 2024 10:30:27 +0100 Subject: [PATCH 114/213] [TS migration] Migrate 'ReimbursementAccountLoadingIndicator.js' component --- ...=> ReimbursementAccountLoadingIndicator.tsx} | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) rename src/components/{ReimbursementAccountLoadingIndicator.js => ReimbursementAccountLoadingIndicator.tsx} (75%) diff --git a/src/components/ReimbursementAccountLoadingIndicator.js b/src/components/ReimbursementAccountLoadingIndicator.tsx similarity index 75% rename from src/components/ReimbursementAccountLoadingIndicator.js rename to src/components/ReimbursementAccountLoadingIndicator.tsx index 141e056afd93..cc9beb513002 100644 --- a/src/components/ReimbursementAccountLoadingIndicator.js +++ b/src/components/ReimbursementAccountLoadingIndicator.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {StyleSheet, View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; @@ -10,14 +9,15 @@ import LottieAnimations from './LottieAnimations'; import ScreenWrapper from './ScreenWrapper'; import Text from './Text'; -const propTypes = { +type ReimbursementAccountLoadingIndicatorProps = { /** Method to trigger when pressing back button of the header */ - onBackButtonPress: PropTypes.func.isRequired, + onBackButtonPress: () => void; }; -function ReimbursementAccountLoadingIndicator(props) { +function ReimbursementAccountLoadingIndicator({onBackButtonPress}: ReimbursementAccountLoadingIndicatorProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + return ( - + - - {translate('reimbursementAccountLoadingAnimation.explanationLine')} + + {translate('reimbursementAccountLoadingAnimation.explanationLine')} @@ -46,7 +46,6 @@ function ReimbursementAccountLoadingIndicator(props) { ); } -ReimbursementAccountLoadingIndicator.propTypes = propTypes; ReimbursementAccountLoadingIndicator.displayName = 'ReimbursementAccountLoadingIndicator'; export default ReimbursementAccountLoadingIndicator; From 2e829a169702d72ec6a526ed432b917576baa810 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 23 Feb 2024 11:11:33 +0100 Subject: [PATCH 115/213] fix: typecheck --- src/pages/home/report/ReportActionCompose/SuggestionMention.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index f284596040b7..54d21d637d09 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -152,7 +152,7 @@ function SuggestionMention( sortedPersonalDetails.slice(0, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length).forEach((detail) => { suggestions.push({ text: PersonalDetailsUtils.getDisplayNameOrDefault(detail), - alternateText: formatPhoneNumber(detail?.login), + alternateText: formatPhoneNumber(detail?.login ?? ''), login: detail?.login, icons: [ { From e4fcff92722b528a546ce72578149cddbe76ccf3 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Fri, 23 Feb 2024 14:05:15 +0100 Subject: [PATCH 116/213] Fix selection bug --- src/components/RoomNameInput/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/RoomNameInput/index.tsx b/src/components/RoomNameInput/index.tsx index cf9c1900ea3e..7025543e479d 100644 --- a/src/components/RoomNameInput/index.tsx +++ b/src/components/RoomNameInput/index.tsx @@ -10,11 +10,11 @@ import CONST from '@src/CONST'; import type RoomNameInputProps from './types'; function RoomNameInput( - {disabled = false, autoFocus = false, shouldDelayFocus = false, isFocused, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, + {disabled = false, autoFocus = false, shouldDelayFocus = false, isFocused, value = '', onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps, ref: ForwardedRef, ) { const {translate} = useLocalize(); - const [selection, setSelection] = useState({start: 0, end: 0}); + const [selection, setSelection] = useState({start: value.length - 1, end: value.length - 1}); /** * Calls the onChangeText callback with a modified room name From 9bf8099eec0ff2e0456c27626b7c021ecddf246e Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 23 Feb 2024 17:01:38 +0100 Subject: [PATCH 117/213] [TS migration] Migrate 'EmojiTest.js' test --- src/libs/EmojiUtils.ts | 2 +- tests/unit/{EmojiTest.js => EmojiTest.ts} | 73 ++++++++++++----------- 2 files changed, 38 insertions(+), 37 deletions(-) rename tests/unit/{EmojiTest.js => EmojiTest.ts} (88%) diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 730f2a5d3295..2b9e4c6fcd8a 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -384,7 +384,7 @@ function replaceAndExtractEmojis(text: string, preferredSkinTone: number = CONST * Suggest emojis when typing emojis prefix after colon * @param [limit] - matching emojis limit */ -function suggestEmojis(text: string, lang: keyof SupportedLanguage, limit = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { +function suggestEmojis(text: string, lang: SupportedLanguage, limit: number = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { // emojisTrie is importing the emoji JSON file on the app starting and we want to avoid it const emojisTrie = require('./EmojiTrie').default; diff --git a/tests/unit/EmojiTest.js b/tests/unit/EmojiTest.ts similarity index 88% rename from tests/unit/EmojiTest.js rename to tests/unit/EmojiTest.ts index 40474d0331fe..954d561598e8 100644 --- a/tests/unit/EmojiTest.js +++ b/tests/unit/EmojiTest.ts @@ -1,20 +1,20 @@ import {getUnixTime} from 'date-fns'; -import lodashGet from 'lodash/get'; import Onyx from 'react-native-onyx'; -import _ from 'underscore'; -import Emoji from '../../assets/emojis'; -import CONST from '../../src/CONST'; -import * as User from '../../src/libs/actions/User'; -import * as EmojiUtils from '../../src/libs/EmojiUtils'; -import ONYXKEYS from '../../src/ONYXKEYS'; +import Emojis from '@assets/emojis'; +import type {Emoji} from '@assets/emojis/types'; +import * as User from '@libs/actions/User'; +import * as EmojiUtils from '@libs/EmojiUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {FrequentlyUsedEmoji} from '@src/types/onyx'; import * as TestHelper from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; describe('EmojiTest', () => { it('matches all the emojis in the list', () => { // Given the set of Emojis available in the application - const emojiMatched = _.every(Emoji, (emoji) => { - if (emoji.header === true || emoji.spacer) { + const emojiMatched = Emojis.every((emoji) => { + if (('header' in emoji && emoji.header) || ('spacer' in emoji && emoji.spacer)) { return true; } @@ -22,9 +22,9 @@ describe('EmojiTest', () => { const isEmojiMatched = EmojiUtils.containsOnlyEmojis(emoji.code); let skinToneMatched = true; - if (emoji.types) { + if ('types' in emoji && emoji.types) { // and every skin tone variant of the Emoji code - skinToneMatched = _.every(emoji.types, (emojiWithSkinTone) => EmojiUtils.containsOnlyEmojis(emojiWithSkinTone)); + skinToneMatched = emoji.types.every((emojiWithSkinTone) => EmojiUtils.containsOnlyEmojis(emojiWithSkinTone)); } return skinToneMatched && isEmojiMatched; }); @@ -103,42 +103,42 @@ describe('EmojiTest', () => { it('replaces an emoji code with an emoji and a space', () => { const text = 'Hi :smile:'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄 '); + expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄 '); }); it('will add a space after the last emoji', () => { const text = 'Hi :smile::wave:'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 '); + expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 '); }); it('will add a space after the last emoji if there is text after it', () => { const text = 'Hi :smile::wave:space after last emoji'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 space after last emoji'); + expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 space after last emoji'); }); it('will add a space after the last emoji if there is invalid emoji after it', () => { const text = 'Hi :smile::wave:space when :invalidemoji: present'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 space when :invalidemoji: present'); + expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 space when :invalidemoji: present'); }); it('will not add a space after the last emoji if there if last emoji is immediately followed by a space', () => { const text = 'Hi :smile::wave: space after last emoji'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 space after last emoji'); + expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 space after last emoji'); }); it('will return correct cursor position', () => { const text = 'Hi :smile: there :wave:!'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'cursorPosition')).toBe(15); + expect(EmojiUtils.replaceEmojis(text).cursorPosition).toBe(15); }); it('will return correct cursor position when space is not added by space follows last emoji', () => { const text = 'Hi :smile: there!'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'cursorPosition')).toBe(6); + expect(EmojiUtils.replaceEmojis(text).cursorPosition).toBe(6); }); it('will return undefined cursor position when no emoji is replaced', () => { const text = 'Hi there!'; - expect(lodashGet(EmojiUtils.replaceEmojis(text), 'cursorPosition')).toBe(undefined); + expect(EmojiUtils.replaceEmojis(text).cursorPosition).toBe(undefined); }); it('suggests emojis when typing emojis prefix after colon', () => { @@ -149,11 +149,11 @@ describe('EmojiTest', () => { it('suggests a limited number of matching emojis', () => { const text = 'Hi :face'; const limit = 3; - expect(EmojiUtils.suggestEmojis(text, 'en', limit).length).toBe(limit); + expect(EmojiUtils.suggestEmojis(text, 'en', limit)?.length).toBe(limit); }); it('correct suggests emojis accounting for keywords', () => { - const thumbEmojis = [ + const thumbEmojis: Emoji[] = [ { code: '👍', name: '+1', @@ -190,10 +190,11 @@ describe('EmojiTest', () => { }); describe('update frequently used emojis', () => { - let spy; + let spy: jest.SpyInstance; beforeAll(() => { Onyx.init({keys: ONYXKEYS}); + // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. global.fetch = TestHelper.getGlobalFetchMock(); spy = jest.spyOn(User, 'updateFrequentlyUsedEmojis'); }); @@ -205,7 +206,7 @@ describe('EmojiTest', () => { it('should put a less frequent and recent used emoji behind', () => { // Given an existing frequently used emojis list with count > 1 - const frequentlyEmojisList = [ + const frequentlyEmojisList: FrequentlyUsedEmoji[] = [ { code: '👋', name: 'wave', @@ -237,12 +238,12 @@ describe('EmojiTest', () => { return waitForBatchedUpdates().then(() => { // When add a new emoji const currentTime = getUnixTime(new Date()); - const smileEmoji = {code: '😄', name: 'smile'}; + const smileEmoji: Emoji = {code: '😄', name: 'smile'}; const newEmoji = [smileEmoji]; User.updateFrequentlyUsedEmojis(EmojiUtils.getFrequentlyUsedEmojis(newEmoji)); // Then the new emoji should be at the last item of the list - const expectedSmileEmoji = {...smileEmoji, count: 1, lastUpdatedAt: currentTime}; + const expectedSmileEmoji: FrequentlyUsedEmoji = {...smileEmoji, count: 1, lastUpdatedAt: currentTime}; const expectedFrequentlyEmojisList = [...frequentlyEmojisList, expectedSmileEmoji]; expect(spy).toBeCalledWith(expectedFrequentlyEmojisList); @@ -251,8 +252,8 @@ describe('EmojiTest', () => { it('should put more frequent and recent used emoji to the front', () => { // Given an existing frequently used emojis list - const smileEmoji = {code: '😄', name: 'smile'}; - const frequentlyEmojisList = [ + const smileEmoji: Emoji = {code: '😄', name: 'smile'}; + const frequentlyEmojisList: FrequentlyUsedEmoji[] = [ { code: '😠', name: 'angry', @@ -296,10 +297,10 @@ describe('EmojiTest', () => { it('should sorted descending by count and lastUpdatedAt for multiple emoji added', () => { // Given an existing frequently used emojis list - const smileEmoji = {code: '😄', name: 'smile'}; - const zzzEmoji = {code: '💤', name: 'zzz'}; - const impEmoji = {code: '👿', name: 'imp'}; - const frequentlyEmojisList = [ + const smileEmoji: Emoji = {code: '😄', name: 'smile'}; + const zzzEmoji: Emoji = {code: '💤', name: 'zzz'}; + const impEmoji: Emoji = {code: '👿', name: 'imp'}; + const frequentlyEmojisList: FrequentlyUsedEmoji[] = [ { code: '😠', name: 'angry', @@ -345,11 +346,11 @@ describe('EmojiTest', () => { it('make sure the most recent new emoji is added to the list even it is full with count > 1', () => { // Given an existing full (24 items) frequently used emojis list - const smileEmoji = {code: '😄', name: 'smile'}; - const zzzEmoji = {code: '💤', name: 'zzz'}; - const impEmoji = {code: '👿', name: 'imp'}; - const bookEmoji = {code: '📚', name: 'books'}; - const frequentlyEmojisList = [ + const smileEmoji: Emoji = {code: '😄', name: 'smile'}; + const zzzEmoji: Emoji = {code: '💤', name: 'zzz'}; + const impEmoji: Emoji = {code: '👿', name: 'imp'}; + const bookEmoji: Emoji = {code: '📚', name: 'books'}; + const frequentlyEmojisList: FrequentlyUsedEmoji[] = [ { code: '😠', name: 'angry', From 903527ff2ac49138d5248b140d43c3819c2f2500 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Fri, 23 Feb 2024 20:28:20 +0100 Subject: [PATCH 118/213] Use formula for the optimistic expense report --- src/libs/ReportUtils.ts | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e0c474e3d8fb..6b22c48ac534 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1,3 +1,4 @@ +import {format} from 'date-fns'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import {isEmpty} from 'lodash'; @@ -1996,6 +1997,13 @@ function getFormulaTypeReportField(reportFields: PolicyReportFields) { return Object.values(reportFields).find((field) => field.type === 'formula'); } +/** + * Given a set of report fields, return the field that refers to title + */ +function getTitleReportField(reportFields: PolicyReportFields) { + return Object.values(reportFields).find((field) => isReportFieldOfTypeTitle(field)); +} + /** * Get the report fields attached to the policy given policyID */ @@ -2904,6 +2912,26 @@ function buildOptimisticIOUReport(payeeAccountID: number, payerAccountID: number }; } +/** + * Populates the report field formula with the values from the report and policy. + * Currently, is supports only optimistic expense reports. + * Each formula field is either replaced with a value, or removed. + * If after all replacements the formula is empty, the original formula is returned. + */ +function populateOptimisticReportFormula(formula: string, report: OptimisticExpenseReport, policy: Policy | EmptyObject): string { + const result = formula + .replaceAll('{report:id}', report.reportID) + // We don't translate because the server response is always in English + .replaceAll('{report:type}', 'Expense Report') + .replaceAll('{report:startdate}', report.lastVisibleActionCreated ? format(new Date(report.lastVisibleActionCreated), CONST.DATE.FNS_FORMAT_STRING) : '') + .replaceAll('{report:total}', report.total?.toString() ?? '') + .replaceAll('{report:currency}', report.currency ?? '') + .replaceAll('{report:policyname}', policy.name ?? '') + .replaceAll(/\{report:(.+)}/g, ''); + + return result.trim().length ? result : formula; +} + /** * Builds an optimistic Expense report with a randomly generated reportID * @@ -2913,7 +2941,6 @@ function buildOptimisticIOUReport(payeeAccountID: number, payerAccountID: number * @param total - Amount in cents * @param currency */ - function buildOptimisticExpenseReport(chatReportID: string, policyID: string, payeeAccountID: number, total: number, currency: string): OptimisticExpenseReport { // The amount for Expense reports are stored as negative value in the database const storedTotal = total * -1; @@ -2934,7 +2961,6 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa type: CONST.REPORT.TYPE.EXPENSE, ownerAccountID: payeeAccountID, currency, - // We don't translate reportName because the server response is always in English reportName: `${policyName} owes ${formattedTotal}`, stateNum, @@ -2950,6 +2976,11 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa expenseReport.managerID = policy.submitsTo; } + const titleReportField = getTitleReportField(getReportFieldsByPolicyID(policyID) ?? {}); + if (!!titleReportField && reportFieldsEnabled(expenseReport)) { + expenseReport.reportName = populateOptimisticReportFormula(titleReportField.defaultValue, expenseReport, policy); + } + return expenseReport; } From 55884df73116a71ed4ef78645a907f5ece5c872a Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 24 Feb 2024 06:14:50 +0700 Subject: [PATCH 119/213] fix lint --- src/components/PDFView/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/PDFView/index.native.js b/src/components/PDFView/index.native.js index f47aeafd5316..f5a74011b6cd 100644 --- a/src/components/PDFView/index.native.js +++ b/src/components/PDFView/index.native.js @@ -104,8 +104,8 @@ function PDFView({onToggleKeyboard, onLoadComplete, fileName, onPress, isFocused /** * After the PDF is successfully loaded hide PDFPasswordForm and the loading * indicator. - * @param numberOfPages - * @param path - Path to cache location + * @param {Number} numberOfPages + * @param {Number} path - Path to cache location */ const finishPDFLoad = (numberOfPages, path) => { setShouldRequestPassword(false); From 3cc9dd5064296fc15dd76b8772cfdb776b7194d8 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Sun, 25 Feb 2024 21:35:49 +0100 Subject: [PATCH 120/213] feat: upgrade reanimated to v3.7.1 --- ios/Podfile.lock | 4 ++-- package-lock.json | 8 ++++---- package.json | 2 +- ...ative-reanimated+3.7.1+001+fix-boost-dependency.patch} | 0 ...ed+3.7.0.patch => react-native-reanimated+3.7.1.patch} | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename patches/{react-native-reanimated+3.7.0+001+fix-boost-dependency.patch => react-native-reanimated+3.7.1+001+fix-boost-dependency.patch} (100%) rename patches/{react-native-reanimated+3.7.0.patch => react-native-reanimated+3.7.1.patch} (100%) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e4f988173f40..93c4f6bbc2a9 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1434,7 +1434,7 @@ PODS: - React-Core - RNReactNativeHapticFeedback (2.2.0): - React-Core - - RNReanimated (3.7.0): + - RNReanimated (3.7.1): - glog - RCT-Folly (= 2022.05.16.00) - React-Core @@ -1988,7 +1988,7 @@ SPEC CHECKSUMS: rnmapbox-maps: fcf7f1cbdc8bd7569c267d07284e8a5c7bee06ed RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa RNReactNativeHapticFeedback: ec56a5f81c3941206fd85625fa669ffc7b4545f9 - RNReanimated: 7d6d32f238f914f13d9d6fb45c0aef557f7f901e + RNReanimated: beb07f7f900543928467da8107c175d1e57a1049 RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2 RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a diff --git a/package-lock.json b/package-lock.json index 3e4dfd77bccf..fc5c898f9b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -106,7 +106,7 @@ "react-native-plaid-link-sdk": "10.8.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "^3.7.0", + "react-native-reanimated": "^3.7.1", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.8.2", "react-native-screens": "3.29.0", @@ -45270,9 +45270,9 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.7.0.tgz", - "integrity": "sha512-KM+MKa3CJWqsF4GlOLLKBxTR2NEcrg5/HP9J2b6Dfgvll1sjZPywCOEEIh967SboEU8N9LjYZuoVm2UoXGxp2Q==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.7.1.tgz", + "integrity": "sha512-bapCxhnS58+GZynQmA/f5U8vRlmhXlI/WhYg0dqnNAGXHNIc+38ahRWcG8iK8e0R2v9M8Ky2ZWObEC6bmweofg==", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", diff --git a/package.json b/package.json index aac474b0f4a2..0f05b48572eb 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "react-native-plaid-link-sdk": "10.8.0", "react-native-qrcode-svg": "^6.2.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "^3.7.0", + "react-native-reanimated": "^3.7.1", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.8.2", "react-native-screens": "3.29.0", diff --git a/patches/react-native-reanimated+3.7.0+001+fix-boost-dependency.patch b/patches/react-native-reanimated+3.7.1+001+fix-boost-dependency.patch similarity index 100% rename from patches/react-native-reanimated+3.7.0+001+fix-boost-dependency.patch rename to patches/react-native-reanimated+3.7.1+001+fix-boost-dependency.patch diff --git a/patches/react-native-reanimated+3.7.0.patch b/patches/react-native-reanimated+3.7.1.patch similarity index 100% rename from patches/react-native-reanimated+3.7.0.patch rename to patches/react-native-reanimated+3.7.1.patch From 2ed6de812940bd8e025cca90c7f892eb4504400e Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Sun, 25 Feb 2024 22:50:12 +0100 Subject: [PATCH 121/213] Fix: VBA - Background page is reloading when proceeding through VBA flow --- src/pages/workspace/WorkspacePageWithSections.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index b9b14e27d01d..32b76ef46202 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -1,3 +1,4 @@ +import {useIsFocused} from '@react-navigation/native'; import type {ReactNode} from 'react'; import React, {useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; @@ -121,6 +122,7 @@ function WorkspacePageWithSections({ const content = typeof children === 'function' ? children(hasVBA, policyID, isUsingECard) : children; const {isSmallScreenWidth} = useWindowDimensions(); const firstRender = useRef(true); + const isFocused = useIsFocused(); useEffect(() => { // Because isLoading is false before merging in Onyx, we need firstRender ref to display loading page as well before isLoading is change to true @@ -163,7 +165,7 @@ function WorkspacePageWithSections({ onBackButtonPress={() => Navigation.goBack(backButtonRoute ?? ROUTES.WORKSPACE_INITIAL.getRoute(policyID))} icon={icon ?? undefined} /> - {(isLoading || firstRender.current) && shouldShowLoading ? ( + {(isLoading || firstRender.current) && shouldShowLoading && isFocused ? ( ) : ( <> From 637af8a0521244a4bc5e94818701ed05677c923a Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 26 Feb 2024 13:56:16 +0800 Subject: [PATCH 122/213] remove unused param --- src/libs/actions/Report.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f29f8a4fbaab..b1405acad98e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1719,7 +1719,7 @@ function updateWriteCapabilityAndNavigate(report: Report, newValue: WriteCapabil /** * Navigates to the 1:1 report with Concierge */ -function navigateToConciergeChat(shouldDismissModal = false, shouldPopCurrentScreen = false, checkIfCurrentPageActive = () => true) { +function navigateToConciergeChat(shouldDismissModal = false, checkIfCurrentPageActive = () => true) { // If conciergeChatReportID contains a concierge report ID, we navigate to the concierge chat using the stored report ID. // Otherwise, we would find the concierge chat and navigate to it. if (!conciergeChatReportID) { @@ -1730,17 +1730,11 @@ function navigateToConciergeChat(shouldDismissModal = false, shouldPopCurrentScr if (!checkIfCurrentPageActive()) { return; } - if (shouldPopCurrentScreen && !shouldDismissModal) { - Navigation.goBack(); - } navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], shouldDismissModal); }); } else if (shouldDismissModal) { Navigation.dismissModal(conciergeChatReportID); } else { - if (shouldPopCurrentScreen) { - Navigation.goBack(); - } Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeChatReportID)); } } From f5a0bd4d4a892a80e7150150f790811e06ea8460 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 26 Feb 2024 13:56:21 +0800 Subject: [PATCH 123/213] fix param --- src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx | 2 +- .../ConnectBankAccount/ConnectBankAccount.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx b/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx index 025dcafd9740..9ad19482a4a7 100644 --- a/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx +++ b/src/pages/OnboardEngagement/PurposeForUsingExpensifyPage.tsx @@ -90,7 +90,7 @@ function PurposeForUsingExpensifyModal() { } Report.completeEngagementModal(message, choice); - Report.navigateToConciergeChat(false, true); + Report.navigateToConciergeChat(true); }, []); const menuItems: MenuItemProps[] = useMemo( diff --git a/src/pages/ReimbursementAccount/ConnectBankAccount/ConnectBankAccount.tsx b/src/pages/ReimbursementAccount/ConnectBankAccount/ConnectBankAccount.tsx index 1682cb66f7c8..078c216d836c 100644 --- a/src/pages/ReimbursementAccount/ConnectBankAccount/ConnectBankAccount.tsx +++ b/src/pages/ReimbursementAccount/ConnectBankAccount/ConnectBankAccount.tsx @@ -39,7 +39,7 @@ function ConnectBankAccount({reimbursementAccount, onBackButtonPress, account, p const styles = useThemeStyles(); const {translate} = useLocalize(); - const handleNavigateToConciergeChat = () => Report.navigateToConciergeChat(false, true); + const handleNavigateToConciergeChat = () => Report.navigateToConciergeChat(true); const bankAccountState = reimbursementAccount.achData?.state ?? ''; // If a user tries to navigate directly to the validate page we'll show them the EnableStep From 479cf7adad63f36bf27e73effd9ce61675273656 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 26 Feb 2024 10:46:17 +0100 Subject: [PATCH 124/213] fix: typecheck --- .../MoneyRequestConfirmationList.tsx | 18 +++++++++++------- src/languages/types.ts | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 252b0ad44384..dbc94502bdd3 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -345,11 +345,11 @@ function MoneyRequestConfirmationList({ } else if (!!(receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) { text = translate('iou.request'); if (iouAmount !== 0) { - text = translate('iou.requestAmount', {amount: Number(formattedAmount)}); + text = translate('iou.requestAmount', {amount: formattedAmount}); } } else { const translationKey = isSplitBill ? 'iou.splitAmount' : 'iou.requestAmount'; - text = translate(translationKey, {amount: Number(formattedAmount)}); + text = translate(translationKey, {amount: formattedAmount}); } return [ { @@ -785,11 +785,15 @@ function MoneyRequestConfirmationList({ description={translate('common.category')} numberOfLinesTitle={2} onPress={() => { - if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID ?? '', reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.CATEGORY)); - return; - } - Navigation.navigate(ROUTES.MONEY_REQUEST_CATEGORY.getRoute(iouType, reportID)); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute( + CONST.IOU.ACTION.EDIT, + iouType, + transaction?.transactionID ?? '', + reportID ?? '', + Navigation.getActiveRouteWithoutParams(), + ), + ); }} style={styles.moneyRequestMenuItem} titleStyle={styles.flex1} diff --git a/src/languages/types.ts b/src/languages/types.ts index 410c8e1c2085..abfe2c3f7750 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -109,7 +109,7 @@ type RequestAmountParams = {amount: string}; type RequestedAmountMessageParams = {formattedAmount: string; comment?: string}; -type SplitAmountParams = {amount: number}; +type SplitAmountParams = {amount: string | number}; type DidSplitAmountMessageParams = {formattedAmount: string; comment: string}; From f7ed37e02855d0d3c8d3503694730b329287c974 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 26 Feb 2024 17:15:21 +0700 Subject: [PATCH 125/213] remove redundant condition --- src/components/Attachments/AttachmentView/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 68f41e1e51c3..e3c363c2f936 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -160,7 +160,7 @@ function AttachmentView({ const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; const onPDFLoadComplete = (path) => { - if (isUsedInCarousel && reportActionID && path) { + if (reportActionID && path) { CachedPDFPaths.add(reportActionID, path); } if (!loadComplete) { From d6c2c6ded934bc81117796ce9f7547a51beb65c8 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Mon, 26 Feb 2024 11:18:51 +0100 Subject: [PATCH 126/213] Fix typescript error --- src/pages/workspace/WorkspaceNewRoomPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index 73107d7e3eba..f9f691ca5a7f 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -265,7 +265,6 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli ref={inputCallbackRef} inputID={INPUT_IDS.ROOM_NAME} isFocused={isFocused} - // @ts-expect-error TODO: Remove this once RoomNameInput (https://github.com/Expensify/App/issues/25090) is migrated to TypeScript. shouldDelayFocus autoFocus /> From abd53473a1e76f6433ee786462ec5840e4d2e57c Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 26 Feb 2024 17:24:10 +0700 Subject: [PATCH 127/213] delete cache for transaction receipt --- src/components/Attachments/AttachmentView/index.js | 4 ++-- src/libs/actions/CachedPDFPaths/index.native.ts | 10 +++++----- src/libs/actions/CachedPDFPaths/types.ts | 4 ++-- src/libs/actions/IOU.ts | 2 ++ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index e3c363c2f936..0b402e2da4ff 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -160,8 +160,8 @@ function AttachmentView({ const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source; const onPDFLoadComplete = (path) => { - if (reportActionID && path) { - CachedPDFPaths.add(reportActionID, path); + if (path && (transaction.transactionID || reportActionID)) { + CachedPDFPaths.add(transaction.transactionID || reportActionID, path); } if (!loadComplete) { setLoadComplete(true); diff --git a/src/libs/actions/CachedPDFPaths/index.native.ts b/src/libs/actions/CachedPDFPaths/index.native.ts index 6c4456f7a528..09203995e9a1 100644 --- a/src/libs/actions/CachedPDFPaths/index.native.ts +++ b/src/libs/actions/CachedPDFPaths/index.native.ts @@ -15,11 +15,11 @@ Onyx.connect({ }, }); -const add: Add = (reportActionID: string, path: string) => { - if (pdfPaths[reportActionID]) { +const add: Add = (id: string, path: string) => { + if (pdfPaths[id]) { return Promise.resolve(); } - return Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[reportActionID]: path}); + return Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[id]: path}); }; const clear: Clear = (path: string) => { @@ -36,8 +36,8 @@ const clear: Clear = (path: string) => { }); }; -const clearByKey: ClearByKey = (reportActionID: string) => { - clear(pdfPaths[reportActionID] ?? '').then(() => Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[reportActionID]: null})); +const clearByKey: ClearByKey = (id: string) => { + clear(pdfPaths[id] ?? '').then(() => Onyx.merge(ONYXKEYS.CACHED_PDF_PATHS, {[id]: null})); }; const clearAll: ClearAll = () => { diff --git a/src/libs/actions/CachedPDFPaths/types.ts b/src/libs/actions/CachedPDFPaths/types.ts index 8c4843aaeaec..98b768c4645e 100644 --- a/src/libs/actions/CachedPDFPaths/types.ts +++ b/src/libs/actions/CachedPDFPaths/types.ts @@ -1,6 +1,6 @@ -type Add = (reportActionID: string, path: string) => Promise; +type Add = (id: string, path: string) => Promise; type Clear = (path: string) => Promise; type ClearAll = () => void; -type ClearByKey = (reportActionID: string) => void; +type ClearByKey = (id: string) => void; export type {Add, Clear, ClearAll, ClearByKey}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 37308c73e724..e8d1e43fb51b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -57,6 +57,7 @@ import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, ReceiptSource, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import * as CachedPDFPaths from './CachedPDFPaths'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -3139,6 +3140,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor // STEP 6: Make the API request API.write(WRITE_COMMANDS.DELETE_MONEY_REQUEST, parameters, {optimisticData, successData, failureData}); + CachedPDFPaths.clearByKey(transactionID); // STEP 7: Navigate the user depending on which page they are on and which resources were deleted if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { From 1e19123cf64f548463fc0ddc8d48baab4db3bc50 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Mon, 26 Feb 2024 11:30:48 +0100 Subject: [PATCH 128/213] fix: typecheck --- src/components/AvatarWithImagePicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx index fa8a6d71516f..4388ebb8f815 100644 --- a/src/components/AvatarWithImagePicker.tsx +++ b/src/components/AvatarWithImagePicker.tsx @@ -220,7 +220,7 @@ function AvatarWithImagePicker({ setError(null, {}); setIsMenuVisible(false); setImageData({ - uri: image.uri, + uri: image.uri ?? '', name: image.name, type: image.type, }); From 117c17128d524488162761c91d03c7de0838ae94 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Mon, 26 Feb 2024 14:50:43 +0100 Subject: [PATCH 129/213] Change imports --- src/components/Form/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index f6ed08c863ca..ba147dfa81a2 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -7,8 +7,8 @@ import type AmountTextInput from '@components/AmountTextInput'; import type CheckboxWithLabel from '@components/CheckboxWithLabel'; import type CountrySelector from '@components/CountrySelector'; import type Picker from '@components/Picker'; -import type RoomNameInput from '@components/RoomNameInput'; import type RadioButtons from '@components/RadioButtons'; +import type RoomNameInput from '@components/RoomNameInput'; import type SingleChoiceQuestion from '@components/SingleChoiceQuestion'; import type StatePicker from '@components/StatePicker'; import type TextInput from '@components/TextInput'; From 84592abe6d5991cd81464b8a2e175b90fa7cf446 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 26 Feb 2024 15:27:40 +0100 Subject: [PATCH 130/213] Migrate to TS SidebarOrderTest, LHNTestUtils test files --- .../home/report/ReportActionItemSingle.tsx | 2 +- src/types/onyx/ReportAction.ts | 3 + ...idebarOrderTest.js => SidebarOrderTest.ts} | 429 +++++++++++------- .../{LHNTestUtils.js => LHNTestUtils.tsx} | 222 ++++----- 4 files changed, 347 insertions(+), 309 deletions(-) rename tests/unit/{SidebarOrderTest.js => SidebarOrderTest.ts} (73%) rename tests/utils/{LHNTestUtils.js => LHNTestUtils.tsx} (68%) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 18f261024fd6..741422cc7e82 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -27,7 +27,7 @@ import type ChildrenProps from '@src/types/utils/ChildrenProps'; import ReportActionItemDate from './ReportActionItemDate'; import ReportActionItemFragment from './ReportActionItemFragment'; -type ReportActionItemSingleProps = ChildrenProps & { +type ReportActionItemSingleProps = Partial & { /** All the data of the action */ action: ReportAction; diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index ed3173381b32..dffbbab6c011 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -123,6 +123,9 @@ type ReportActionBase = { actorAccountID?: number; + /** The account of the last message's actor */ + actor?: string; + /** Person who created the action */ person?: Person[]; diff --git a/tests/unit/SidebarOrderTest.js b/tests/unit/SidebarOrderTest.ts similarity index 73% rename from tests/unit/SidebarOrderTest.js rename to tests/unit/SidebarOrderTest.ts index 7ae8c4e1e9b3..9e70347764df 100644 --- a/tests/unit/SidebarOrderTest.js +++ b/tests/unit/SidebarOrderTest.ts @@ -1,14 +1,16 @@ import {cleanup, screen} from '@testing-library/react-native'; -import lodashGet from 'lodash/get'; import Onyx from 'react-native-onyx'; -import CONST from '../../src/CONST'; -import * as Report from '../../src/libs/actions/Report'; -import DateUtils from '../../src/libs/DateUtils'; -import * as Localize from '../../src/libs/Localize'; +import * as Report from '@libs/actions/Report'; +import DateUtils from '@libs/DateUtils'; +import * as Localize from '@libs/Localize'; +import CONST from '@src/CONST'; +import type * as OnyxTypes from '@src/types/onyx'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; +type ReportCollectionDataSet = Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, OnyxTypes.Report>; + // Be sure to include the mocked Permissions and Expensicons libraries as well as the usePermissions hook or else the beta tests won't work jest.mock('../../src/libs/Permissions'); jest.mock('../../src/hooks/usePermissions.ts'); @@ -23,15 +25,16 @@ const ONYXKEYS = { COLLECTION: { REPORT: 'report_', REPORT_ACTIONS: 'reportActions_', + POLICY: 'policy_', }, NETWORK: 'network', -}; + IS_LOADING_REPORT_DATA: 'isLoadingReportData', +} as const; describe('Sidebar', () => { beforeAll(() => Onyx.init({ keys: ONYXKEYS, - registerStorageEventListener: () => {}, safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], }), ); @@ -88,6 +91,10 @@ describe('Sidebar', () => { const report = LHNTestUtils.getFakeReport([1, 2]); LHNTestUtils.getDefaultRenderedSidebarLinks(report.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -96,7 +103,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) @@ -120,6 +127,12 @@ describe('Sidebar', () => { Report.addComment(report2.reportID, 'Hi, this is a comment'); Report.addComment(report3.reportID, 'Hi, this is a comment'); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -128,9 +141,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -140,9 +151,9 @@ describe('Sidebar', () => { const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('One, Two'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('Three, Four'); + expect(displayNames[2].props.children[0]).toBe('One, Two'); }) ); }); @@ -166,6 +177,12 @@ describe('Sidebar', () => { const currentReportId = report1.reportID; LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -174,9 +191,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -189,9 +204,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('One, Two'); // this has `hasDraft` flag enabled so it will be on top - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('One, Two'); // this has `hasDraft` flag enabled so it will be on top + expect(displayNames[1].props.children[0]).toBe('Five, Six'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); }) ); }); @@ -209,6 +224,12 @@ describe('Sidebar', () => { Report.addComment(report2.reportID, 'Hi, this is a comment'); Report.addComment(report3.reportID, 'Hi, this is a comment'); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -217,9 +238,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -236,9 +255,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('One, Two'); + expect(displayNames[1].props.children[0]).toBe('Five, Six'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); }) ); }); @@ -250,7 +269,7 @@ describe('Sidebar', () => { const report3 = LHNTestUtils.getFakeReport([5, 6], 2); const taskReportName = 'Buy Grocery'; - const taskReport = { + const taskReport: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([7, 8], 1), type: CONST.REPORT.TYPE.TASK, reportName: taskReportName, @@ -266,6 +285,13 @@ describe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(taskReport.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + [`${ONYXKEYS.COLLECTION.REPORT}${taskReport.reportID}`]: taskReport, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -274,10 +300,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - [`${ONYXKEYS.COLLECTION.REPORT}${taskReport.reportID}`]: taskReport, + ...reportCollectionDataSet, }), ) @@ -286,10 +309,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe(taskReportName); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [3, 'props', 'children', 0])).toBe('One, Two'); + expect(displayNames[0].props.children[0]).toBe(taskReportName); + expect(displayNames[1].props.children[0]).toBe('Five, Six'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); + expect(displayNames[3].props.children[0]).toBe('One, Two'); }) ); }); @@ -298,14 +321,14 @@ describe('Sidebar', () => { // Given three reports in the recently updated order of 3, 2, 1 const report1 = LHNTestUtils.getFakeReport([1, 2], 4); const report2 = LHNTestUtils.getFakeReport([3, 4], 3); - const report3 = { + const report3: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6], 2), hasOutstandingChildRequest: false, // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const iouReport = { + const iouReport: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([7, 8], 1), type: CONST.REPORT.TYPE.IOU, ownerAccountID: 2, @@ -326,6 +349,13 @@ describe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(report3.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -334,10 +364,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, + ...reportCollectionDataSet, }), ) @@ -346,10 +373,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Email Two owes $100.00'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [3, 'props', 'children', 0])).toBe('One, Two'); + expect(displayNames[0].props.children[0]).toBe('Email Two owes $100.00'); + expect(displayNames[1].props.children[0]).toBe('Five, Six'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); + expect(displayNames[3].props.children[0]).toBe('One, Two'); }) ); }); @@ -360,14 +387,14 @@ describe('Sidebar', () => { const report2 = LHNTestUtils.getFakeReport([3, 4], 3); const fakeReport = LHNTestUtils.getFakeReportWithPolicy([5, 6], 2); const fakePolicy = LHNTestUtils.getFakePolicy(fakeReport.policyID); - const report3 = { + const report3: OnyxTypes.Report = { ...fakeReport, hasOutstandingChildRequest: false, // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const expenseReport = { + const expenseReport: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([7, 8], 1), type: CONST.REPORT.TYPE.EXPENSE, ownerAccountID: 7, @@ -389,6 +416,13 @@ describe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(report3.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + [`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`]: expenseReport, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -398,10 +432,7 @@ describe('Sidebar', () => { [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, [`${ONYXKEYS.COLLECTION.POLICY}${fakeReport.policyID}`]: fakePolicy, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - [`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`]: expenseReport, + ...reportCollectionDataSet, }), ) @@ -410,10 +441,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Workspace owes $100.00'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Email Five'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [3, 'props', 'children', 0])).toBe('One, Two'); + expect(displayNames[0].props.children[0]).toBe('Workspace owes $100.00'); + expect(displayNames[1].props.children[0]).toBe('Email Five'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); + expect(displayNames[3].props.children[0]).toBe('One, Two'); }) ); }); @@ -423,7 +454,7 @@ describe('Sidebar', () => { // And the second report has a draft // And the currently viewed report is the second report const report1 = LHNTestUtils.getFakeReport([1, 2], 3); - const report2 = { + const report2: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([3, 4], 2), hasDraft: true, }; @@ -437,6 +468,12 @@ describe('Sidebar', () => { const currentReportId = report2.reportID; LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -445,9 +482,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -465,9 +500,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('One, Two'); + expect(displayNames[0].props.children[0]).toBe('Three, Four'); + expect(displayNames[1].props.children[0]).toBe('Five, Six'); + expect(displayNames[2].props.children[0]).toBe('One, Two'); }) ); }); @@ -477,11 +512,15 @@ describe('Sidebar', () => { // Given a single report // And the report has a draft - const report = { + const report: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2]), hasDraft: true, }; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -490,7 +529,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) @@ -514,11 +553,15 @@ describe('Sidebar', () => { // Given a single report // And the report is pinned - const report = { + const report: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2]), isPinned: true, }; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -527,7 +570,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) @@ -551,22 +594,22 @@ describe('Sidebar', () => { // with the current user set to email9@ (someone not participating in any of the chats) // with a report that has a draft, a report that is pinned, and // an outstanding IOU report that doesn't belong to the current user - const report1 = { + const report1: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2], 3), isPinned: true, }; - const report2 = { + const report2: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([3, 4], 2), hasDraft: true, }; - const report3 = { + const report3: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6], 1), hasOutstandingChildRequest: false, // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const iouReport = { + const iouReport: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([7, 8]), type: CONST.REPORT.TYPE.IOU, ownerAccountID: 2, @@ -579,9 +622,19 @@ describe('Sidebar', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; report3.iouReportID = iouReport.reportID; + const currentReportId = report2.reportID; const currentlyLoggedInUserAccountID = 9; + LHNTestUtils.getDefaultRenderedSidebarLinks(currentReportId); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -591,10 +644,7 @@ describe('Sidebar', () => { [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [ONYXKEYS.SESSION]: {accountID: currentlyLoggedInUserAccountID}, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, + ...reportCollectionDataSet, }), ) @@ -607,9 +657,9 @@ describe('Sidebar', () => { expect(displayNames).toHaveLength(3); expect(screen.queryAllByTestId('Pin Icon')).toHaveLength(1); expect(screen.queryAllByTestId('Pencil Icon')).toHaveLength(1); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Email Two owes $100.00'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Email Two owes $100.00'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); }) ); }); @@ -617,23 +667,31 @@ describe('Sidebar', () => { it('alphabetizes all the chats that are pinned', () => { // Given three reports in the recently updated order of 3, 2, 1 // and they are all pinned - const report1 = { + const report1: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2], 3), isPinned: true, }; - const report2 = { + const report2: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([3, 4], 2), isPinned: true, }; - const report3 = { + const report3: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6], 1), isPinned: true, }; - const report4 = { + const report4: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([7, 8], 0), isPinned: true, }; + LHNTestUtils.getDefaultRenderedSidebarLinks('0'); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -642,9 +700,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -653,9 +709,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); }) // When a new report is added @@ -666,10 +722,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Seven, Eight'); - expect(lodashGet(displayNames, [3, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Seven, Eight'); + expect(displayNames[3].props.children[0]).toBe('Three, Four'); }) ); }); @@ -677,23 +733,31 @@ describe('Sidebar', () => { it('alphabetizes all the chats that have drafts', () => { // Given three reports in the recently updated order of 3, 2, 1 // and they all have drafts - const report1 = { + const report1: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2], 3), hasDraft: true, }; - const report2 = { + const report2: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([3, 4], 2), hasDraft: true, }; - const report3 = { + const report3: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6], 1), hasDraft: true, }; - const report4 = { + const report4: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([7, 8], 0), hasDraft: true, }; + LHNTestUtils.getDefaultRenderedSidebarLinks('0'); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -702,9 +766,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -713,9 +775,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); }) // When a new report is added @@ -726,17 +788,17 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Seven, Eight'); - expect(lodashGet(displayNames, [3, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Seven, Eight'); + expect(displayNames[3].props.children[0]).toBe('Three, Four'); }) ); }); it('puts archived chats last', () => { // Given three reports, with the first report being archived - const report1 = { + const report1: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, @@ -752,7 +814,15 @@ describe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; + LHNTestUtils.getDefaultRenderedSidebarLinks('0'); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -762,9 +832,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -773,9 +841,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Report (archived)'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('Three, Four'); + expect(displayNames[2].props.children[0]).toBe('Report (archived)'); }) ); }); @@ -790,6 +858,12 @@ describe('Sidebar', () => { const report3 = LHNTestUtils.getFakeReport([5, 6], 1, true); const report4 = LHNTestUtils.getFakeReport([7, 8], 0, true); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // Given the sidebar is rendered in #focus mode (hides read chats) @@ -799,9 +873,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -810,9 +882,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); }) // When a new report is added @@ -823,10 +895,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Seven, Eight'); - expect(lodashGet(displayNames, [3, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Seven, Eight'); + expect(displayNames[3].props.children[0]).toBe('Three, Four'); }) ); }); @@ -844,7 +916,15 @@ describe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; + LHNTestUtils.getDefaultRenderedSidebarLinks('0'); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -854,9 +934,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -865,46 +943,46 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Report (archived)'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('Three, Four'); + expect(displayNames[2].props.children[0]).toBe('Report (archived)'); }) ); }); it('orders IOU reports by displayName if amounts are the same', () => { // Given three IOU reports containing the same IOU amounts - const report1 = { + const report1: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2]), // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const report2 = { + const report2: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([3, 4]), // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const report3 = { + const report3: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6]), // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const report4 = { + const report4: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6]), // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const report5 = { + const report5: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6]), // This has to be added after the IOU report is generated - iouReportID: null, + iouReportID: undefined, }; - const iouReport1 = { + const iouReport1: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([7, 8]), type: CONST.REPORT.TYPE.IOU, ownerAccountID: 2, @@ -916,7 +994,7 @@ describe('Sidebar', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const iouReport2 = { + const iouReport2: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([9, 10]), type: CONST.REPORT.TYPE.IOU, ownerAccountID: 2, @@ -928,7 +1006,7 @@ describe('Sidebar', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const iouReport3 = { + const iouReport3: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([11, 12]), type: CONST.REPORT.TYPE.IOU, ownerAccountID: 2, @@ -940,7 +1018,7 @@ describe('Sidebar', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const iouReport4 = { + const iouReport4: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([11, 12]), type: CONST.REPORT.TYPE.IOU, ownerAccountID: 2, @@ -952,7 +1030,7 @@ describe('Sidebar', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const iouReport5 = { + const iouReport5: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([11, 12]), type: CONST.REPORT.TYPE.IOU, ownerAccountID: 2, @@ -972,7 +1050,22 @@ describe('Sidebar', () => { report5.iouReportID = iouReport5.reportID; const currentlyLoggedInUserAccountID = 13; + LHNTestUtils.getDefaultRenderedSidebarLinks('0'); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + [`${ONYXKEYS.COLLECTION.REPORT}${report4.reportID}`]: report4, + [`${ONYXKEYS.COLLECTION.REPORT}${report5.reportID}`]: report5, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport1.reportID}`]: iouReport1, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport2.reportID}`]: iouReport2, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport3.reportID}`]: iouReport3, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport4.reportID}`]: iouReport4, + [`${ONYXKEYS.COLLECTION.REPORT}${iouReport5.reportID}`]: iouReport5, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -982,16 +1075,7 @@ describe('Sidebar', () => { [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [ONYXKEYS.SESSION]: {accountID: currentlyLoggedInUserAccountID}, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - [`${ONYXKEYS.COLLECTION.REPORT}${report4.reportID}`]: report4, - [`${ONYXKEYS.COLLECTION.REPORT}${report5.reportID}`]: report5, - [`${ONYXKEYS.COLLECTION.REPORT}${iouReport1.reportID}`]: iouReport1, - [`${ONYXKEYS.COLLECTION.REPORT}${iouReport2.reportID}`]: iouReport2, - [`${ONYXKEYS.COLLECTION.REPORT}${iouReport3.reportID}`]: iouReport3, - [`${ONYXKEYS.COLLECTION.REPORT}${iouReport4.reportID}`]: iouReport4, - [`${ONYXKEYS.COLLECTION.REPORT}${iouReport5.reportID}`]: iouReport5, + ...reportCollectionDataSet, }), ) @@ -1000,11 +1084,11 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(5); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Email Five owes $100.00'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Email Four owes $1,000.00'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Email Six owes $100.00'); - expect(lodashGet(displayNames, [3, 'props', 'children', 0])).toBe('Email Three owes $100.00'); - expect(lodashGet(displayNames, [4, 'props', 'children', 0])).toBe('Email Two owes $100.00'); + expect(displayNames[0].props.children[0]).toBe('Email Five owes $100.00'); + expect(displayNames[1].props.children[0]).toBe('Email Four owes $1,000.00'); + expect(displayNames[2].props.children[0]).toBe('Email Six owes $100.00'); + expect(displayNames[3].props.children[0]).toBe('Email Three owes $100.00'); + expect(displayNames[4].props.children[0]).toBe('Email Two owes $100.00'); }) ); }); @@ -1012,15 +1096,15 @@ describe('Sidebar', () => { it('orders nonArchived reports by displayName if created timestamps are the same', () => { // Given three nonArchived reports created at the same time const lastVisibleActionCreated = DateUtils.getDBTime(); - const report1 = { + const report1: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([1, 2]), lastVisibleActionCreated, }; - const report2 = { + const report2: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([3, 4]), lastVisibleActionCreated, }; - const report3 = { + const report3: OnyxTypes.Report = { ...LHNTestUtils.getFakeReport([5, 6]), lastVisibleActionCreated, }; @@ -1031,6 +1115,13 @@ describe('Sidebar', () => { Report.addComment(report3.reportID, 'Hi, this is a comment'); LHNTestUtils.getDefaultRenderedSidebarLinks('0'); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders @@ -1039,9 +1130,7 @@ describe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -1050,9 +1139,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Five, Six'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [2, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Five, Six'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); + expect(displayNames[2].props.children[0]).toBe('Three, Four'); }) ); }); diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.tsx similarity index 68% rename from tests/utils/LHNTestUtils.js rename to tests/utils/LHNTestUtils.tsx index d44a63d51821..80f28002f975 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.tsx @@ -1,21 +1,41 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type {NavigationProp} from '@react-navigation/core/src/types'; +import type * as Navigation from '@react-navigation/native'; +import type {ParamListBase} from '@react-navigation/routers'; import {render} from '@testing-library/react-native'; -import PropTypes from 'prop-types'; +import type {ReactElement} from 'react'; import React from 'react'; -import ComposeProviders from '../../src/components/ComposeProviders'; -import {LocaleContextProvider} from '../../src/components/LocaleContextProvider'; -import OnyxProvider from '../../src/components/OnyxProvider'; -import {CurrentReportIDContextProvider} from '../../src/components/withCurrentReportID'; -import {EnvironmentProvider} from '../../src/components/withEnvironment'; -import CONST from '../../src/CONST'; -import DateUtils from '../../src/libs/DateUtils'; -import ReportActionItemSingle from '../../src/pages/home/report/ReportActionItemSingle'; -import reportActionPropTypes from '../../src/pages/home/report/reportActionPropTypes'; -import SidebarLinksData from '../../src/pages/home/sidebar/SidebarLinksData'; -import reportPropTypes from '../../src/pages/reportPropTypes'; +import ComposeProviders from '@components/ComposeProviders'; +import {LocaleContextProvider} from '@components/LocaleContextProvider'; +import OnyxProvider from '@components/OnyxProvider'; +import {CurrentReportIDContextProvider} from '@components/withCurrentReportID'; +import {EnvironmentProvider} from '@components/withEnvironment'; +import DateUtils from '@libs/DateUtils'; +import ReportActionItemSingle from '@pages/home/report/ReportActionItemSingle'; +import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData'; +import CONST from '@src/CONST'; +import type {PersonalDetailsList, Policy, Report, ReportAction} from '@src/types/onyx'; +import type {ActionName} from '@src/types/onyx/OriginalMessage'; + +type MockedReportActionItemSingleProps = { + /** Determines if the avatar is displayed as a subscript (positioned lower than normal) */ + shouldShowSubscriptAvatar?: boolean; + + /** Report for this action */ + report: Report; + + /** All the data of the action */ + reportAction: ReportAction; +}; + +type MockedSidebarLinksProps = { + /** Current report id */ + currentReportID?: string; +}; // we have to mock `useIsFocused` because it's used in the SidebarLinks component -const mockedNavigate = jest.fn(); -jest.mock('@react-navigation/native', () => { +const mockedNavigate: jest.MockedFn['navigate']> = jest.fn(); +jest.mock('@react-navigation/native', (): typeof Navigation => { const actualNav = jest.requireActual('@react-navigation/native'); return { ...actualNav, @@ -28,10 +48,10 @@ jest.mock('@react-navigation/native', () => { addListener: jest.fn(), }), createNavigationContainerRef: jest.fn(), - }; + } as typeof Navigation; }); -const fakePersonalDetails = { +const fakePersonalDetails: PersonalDetailsList = { 1: { accountID: 1, login: 'email1@test.com', @@ -101,13 +121,11 @@ let lastFakeReportID = 0; let lastFakeReportActionID = 0; /** - * @param {Number[]} participantAccountIDs - * @param {Number} millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) - * @param {boolean} isUnread - * @returns {Object} + * @param millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) */ -function getFakeReport(participantAccountIDs = [1, 2], millisecondsInThePast = 0, isUnread = false) { +function getFakeReport(participantAccountIDs = [1, 2], millisecondsInThePast = 0, isUnread = false): Report { const lastVisibleActionCreated = DateUtils.getDBTime(Date.now() - millisecondsInThePast); + return { type: CONST.REPORT.TYPE.CHAT, reportID: `${++lastFakeReportID}`, @@ -119,12 +137,11 @@ function getFakeReport(participantAccountIDs = [1, 2], millisecondsInThePast = 0 } /** - * @param {String} actor - * @param {Number} millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) - * @returns {Object} + * @param millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) */ -function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = 0) { - const timestamp = DateUtils.getDBTime(Date.now() - millisecondsInThePast); +function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = 0): ReportAction { + const timestamp = Date.now() - millisecondsInThePast; + const created = DateUtils.getDBTime(timestamp); return { actor, @@ -132,6 +149,7 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = reportActionID: `${++lastFakeReportActionID}`, actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, shouldShow: true, + created, timestamp, reportActionTimestamp: timestamp, person: [ @@ -195,35 +213,23 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = }; } -/** - * @param {boolean} isArchived - * @param {boolean} isUserCreatedPolicyRoom - * @param {boolean} hasAddWorkspaceError - * @param {boolean} isUnread - * @param {boolean} isPinned - * @param {boolean} hasDraft - * @returns {Object} - */ -function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft) { +function getAdvancedFakeReport(isArchived: boolean, isUserCreatedPolicyRoom: boolean, hasAddWorkspaceError: boolean, isUnread: boolean, isPinned: boolean, hasDraft: boolean): Report { return { ...getFakeReport([1, 2], 0, isUnread), type: CONST.REPORT.TYPE.CHAT, chatType: isUserCreatedPolicyRoom ? CONST.REPORT.CHAT_TYPE.POLICY_ROOM : CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, statusNum: isArchived ? CONST.REPORT.STATUS_NUM.CLOSED : 0, stateNum: isArchived ? CONST.REPORT.STATE_NUM.APPROVED : 0, - errorFields: hasAddWorkspaceError ? {addWorkspaceRoom: 'blah'} : null, + errorFields: hasAddWorkspaceError ? {1708946640843000: {addWorkspaceRoom: 'blah'}} : undefined, isPinned, hasDraft, }; } /** - * @param {Number[]} [participantAccountIDs] - * @param {Number} [millisecondsInThePast] the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) - * @param {boolean} [isUnread] - * @returns {Object} + * @param millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) */ -function getFakeReportWithPolicy(participantAccountIDs = [1, 2], millisecondsInThePast = 0, isUnread = false) { +function getFakeReportWithPolicy(participantAccountIDs = [1, 2], millisecondsInThePast = 0, isUnread = false): Report { return { ...getFakeReport(participantAccountIDs, millisecondsInThePast, isUnread), type: CONST.REPORT.TYPE.CHAT, @@ -235,12 +241,7 @@ function getFakeReportWithPolicy(participantAccountIDs = [1, 2], millisecondsInT }; } -/** - * @param {Number} [id] - * @param {String} [name] - * @returns {Object} - */ -function getFakePolicy(id = 1, name = 'Workspace-Test-001') { +function getFakePolicy(id = '1', name = 'Workspace-Test-001'): Policy { return { id, name, @@ -252,7 +253,7 @@ function getFakePolicy(id = 1, name = 'Workspace-Test-001') { avatar: '', employeeList: [], isPolicyExpenseChatEnabled: true, - lastModified: 1697323926777105, + lastModified: '1697323926777105', autoReporting: true, autoReportingFrequency: 'immediate', harvesting: { @@ -268,47 +269,20 @@ function getFakePolicy(id = 1, name = 'Workspace-Test-001') { } /** - * @param {String} actionName - * @param {String} actor - * @param {Number} millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) - * @returns {Object} + * @param millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) */ -function getFakeAdvancedReportAction(actionName = 'IOU', actor = 'email1@test.com', millisecondsInThePast = 0) { +function getFakeAdvancedReportAction(actionName: ActionName = 'IOU', actor = 'email1@test.com', millisecondsInThePast = 0): ReportAction { return { ...getFakeReportAction(actor, millisecondsInThePast), actionName, - }; + } as ReportAction; } -/** - * @param {String} [currentReportID] - */ -function getDefaultRenderedSidebarLinks(currentReportID = '') { - // A try-catch block needs to be added to the rendering so that any errors that happen while the component - // renders are caught and logged to the console. Without the try-catch block, Jest might only report the error - // as "The above error occurred in your component", without providing specific details. By using a try-catch block, - // any errors are caught and logged, allowing you to identify the exact error that might be causing a rendering issue - // when developing tests. - - try { - // Wrap the SideBarLinks inside of LocaleContextProvider so that all the locale props - // are passed to the component. If this is not done, then all the locale props are missing - // and there are a lot of render warnings. It needs to be done like this because normally in - // our app (App.js) is when the react application is wrapped in the context providers - render(); - } catch (error) { - console.error(error); - } -} - -/** - * @param {String} [currentReportID] - * @returns {JSX.Element} - */ -function MockedSidebarLinks({currentReportID}) { +function MockedSidebarLinks({currentReportID = ''}: MockedSidebarLinksProps) { return ( {}} insets={{ top: 0, @@ -323,18 +297,7 @@ function MockedSidebarLinks({currentReportID}) { ); } -MockedSidebarLinks.propTypes = { - currentReportID: PropTypes.string, -}; - -MockedSidebarLinks.defaultProps = { - currentReportID: '', -}; - -/** - * @param {React.ReactElement} component - */ -function internalRender(component) { +function getDefaultRenderedSidebarLinks(currentReportID = '') { // A try-catch block needs to be added to the rendering so that any errors that happen while the component // renders are caught and logged to the console. Without the try-catch block, Jest might only report the error // as "The above error occurred in your component", without providing specific details. By using a try-catch block, @@ -342,54 +305,36 @@ function internalRender(component) { // when developing tests. try { - render(component); + // Wrap the SideBarLinks inside of LocaleContextProvider so that all the locale props + // are passed to the component. If this is not done, then all the locale props are missing + // and there are a lot of render warnings. It needs to be done like this because normally in + // our app (App.js) is when the react application is wrapped in the context providers + render(); } catch (error) { console.error(error); } } -/** - * @param {Boolean} [shouldShowSubscriptAvatar] - * @param {Object} [report] - * @param {Object} [reportAction] - */ -function getDefaultRenderedReportActionItemSingle(shouldShowSubscriptAvatar = true, report = null, reportAction = null) { - const currentReport = report || getFakeReport(); - const currentReportAction = reportAction || getFakeAdvancedReportAction(); +function internalRender(component: ReactElement) { + // A try-catch block needs to be added to the rendering so that any errors that happen while the component + // renders are caught and logged to the console. Without the try-catch block, Jest might only report the error + // as "The above error occurred in your component", without providing specific details. By using a try-catch block, + // any errors are caught and logged, allowing you to identify the exact error that might be causing a rendering issue + // when developing tests. - internalRender( - , - ); + try { + render(component); + } catch (error) { + console.error(error); + } } -/** - * @param {Boolean} shouldShowSubscriptAvatar - * @param {Object} report - * @param {Object} reportAction - * @returns {JSX.Element} - */ -function MockedReportActionItemSingle({shouldShowSubscriptAvatar, report, reportAction}) { - const personalDetailsList = { - [reportAction.actorAccountID]: { - accountID: reportAction.actorAccountID, - login: 'email1@test.com', - displayName: 'Email One', - avatar: 'https://example.com/avatar.png', - firstName: 'One', - }, - }; - +function MockedReportActionItemSingle({shouldShowSubscriptAvatar = true, report, reportAction}: MockedReportActionItemSingleProps) { return ( , + ); +} export { fakePersonalDetails, From 9112cceb6fbadcd7d1de94867dd878f32e4bc901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Mon, 26 Feb 2024 17:13:43 +0100 Subject: [PATCH 131/213] Add review changes --- .../VideoPlayerControls/VolumeButton/index.js | 26 +++++++++++-------- src/libs/NumberUtils.ts | 23 +++++++++++++++- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js index b0757eb7df68..45f47eb87c36 100644 --- a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js +++ b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, {memo, useState} from 'react'; +import React, {memo, useCallback, useState} from 'react'; import {View} from 'react-native'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import Animated, {runOnJS, useAnimatedStyle, useDerivedValue} from 'react-native-reanimated'; @@ -9,6 +9,7 @@ import IconButton from '@components/VideoPlayer/IconButton'; import {useVolumeContext} from '@components/VideoPlayerContexts/VolumeContext'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as NumberUtils from '@libs/NumberUtils'; import stylePropTypes from '@styles/stylePropTypes'; const propTypes = { @@ -38,22 +39,25 @@ function VolumeButton({style, small}) { const [volumeIcon, setVolumeIcon] = useState({icon: getVolumeIcon(volume.value)}); const [isSliderBeingUsed, setIsSliderBeingUsed] = useState(false); - const onSliderLayout = (e) => { + const onSliderLayout = useCallback((e) => { setSliderHeight(e.nativeEvent.layout.height); - }; + }, []); - const gestureEventHandler = (event) => { - const val = Math.floor((1 - event.y / sliderHeight) * 100) / 100; - volume.value = Math.min(Math.max(val, 0), 1); - }; + const changeVolumeOnPan = useCallback( + (event) => { + const val = NumberUtils.roundToTwoDecimalPlaces(1 - event.y / sliderHeight); + volume.value = NumberUtils.clamp(val, 0, 1); + }, + [sliderHeight, volume], + ); const pan = Gesture.Pan() .onBegin((event) => { runOnJS(setIsSliderBeingUsed)(true); - gestureEventHandler(event); + changeVolumeOnPan(event); }) .onChange((event) => { - gestureEventHandler(event); + changeVolumeOnPan(event); }) .onFinalize(() => { runOnJS(setIsSliderBeingUsed)(false); @@ -61,9 +65,9 @@ function VolumeButton({style, small}) { const progressBarStyle = useAnimatedStyle(() => ({height: `${volume.value * 100}%`})); - const updateIcon = (vol) => { + const updateIcon = useCallback((vol) => { setVolumeIcon({icon: getVolumeIcon(vol)}); - }; + }, []); useDerivedValue(() => { runOnJS(updateVolume)(volume.value); diff --git a/src/libs/NumberUtils.ts b/src/libs/NumberUtils.ts index d7eb87a2ed1e..b9814487bf3f 100644 --- a/src/libs/NumberUtils.ts +++ b/src/libs/NumberUtils.ts @@ -69,4 +69,25 @@ function parseFloatAnyLocale(value: string): number { return parseFloat(value ? value.replace(',', '.') : value); } -export {rand64, generateHexadecimalValue, generateRandomInt, parseFloatAnyLocale}; +/** + * Rounds a number to two decimal places. + * @param value the value to round + * @returns the rounded value + */ +function roundToTwoDecimalPlaces(value: number): number { + return Math.round(value * 100) / 100; +} + +/** + * Clamps a value between a minimum and maximum value. + * + * @param value the value to clamp + * @param min the minimum value + * @param max the maximum value + * @returns the clamped value + */ +function clamp(value: number, min: number, max: number): number { + return Math.min(Math.max(value, min), max); +} + +export {rand64, generateHexadecimalValue, generateRandomInt, parseFloatAnyLocale, roundToTwoDecimalPlaces, clamp}; From 0c166623f3e438f65c81cc769426152c5bf7c3d8 Mon Sep 17 00:00:00 2001 From: jeremy-croff Date: Mon, 26 Feb 2024 19:19:40 -0600 Subject: [PATCH 132/213] fix(36385): update status bar colors --- src/styles/theme/themes/dark.ts | 2 +- src/styles/theme/themes/light.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index 3f59b08fc447..31716d75dd05 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -95,7 +95,7 @@ const darkTheme = { // The screen name (see SCREENS.ts) is the name of the screen as far as react-navigation is concerned, and the linkingConfig maps screen names to URLs PAGE_THEMES: { [SCREENS.HOME]: { - backgroundColor: colors.productDark200, + backgroundColor: colors.productDark100, statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, }, [SCREENS.REPORT]: { diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index c0b4a5fe3182..fecd8749aebb 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -95,7 +95,7 @@ const lightTheme = { // The screen name (see SCREENS.ts) is the name of the screen as far as react-navigation is concerned, and the linkingConfig maps screen names to URLs PAGE_THEMES: { [SCREENS.HOME]: { - backgroundColor: colors.productLight200, + backgroundColor: colors.productLight100, statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, }, [SCREENS.REPORT]: { From 131d075a12a298911e5403437a3333e370db15df Mon Sep 17 00:00:00 2001 From: dukenv0307 <129500732+dukenv0307@users.noreply.github.com> Date: Tue, 27 Feb 2024 09:41:32 +0700 Subject: [PATCH 133/213] Update src/libs/ReportUtils.ts Co-authored-by: Carlos Alvarez --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7c8ed1190473..98bd7d2e6f81 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1712,7 +1712,7 @@ function getDisplayNamesWithTooltips( } /** - * Return the concatenated string of the display name of a list user + * Returns the the display names of the given user accountIDs */ function getUserDetailsTooltipText(accountIDs: number[]): string { return accountIDs.map((accountID) => getDisplayNameForParticipant(accountID)).join(', '); From 0b8d0d0fe1b1a3b6327cd57e58e5afcc1f9e90d4 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 27 Feb 2024 11:38:54 +0700 Subject: [PATCH 134/213] fix the case invite a new user to workspace --- src/components/MultipleAvatars.tsx | 12 ++++-------- .../BaseUserDetailsTooltip/index.tsx | 4 ++-- src/libs/OptionsListUtils.ts | 2 +- src/libs/ReportUtils.ts | 7 ++++--- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index 37a27018f97f..101399230ae7 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -107,6 +107,8 @@ function MultipleAvatars({ let avatarContainerStyles = StyleUtils.getContainerStyles(size, isInReportAction); const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[size as AvatarSizeToStyles] ?? avatarSizeToStylesMap.default, [size, avatarSizeToStylesMap]); + const tooltipTexts = useMemo(() => (shouldShowTooltip ? icons.map((icon) => ReportUtils.getUserDetailTooltipText(Number(icon.id), icon.name)) : ['']), [shouldShowTooltip, icons]); + const avatarSize = useMemo(() => { if (isFocusMode) { return CONST.AVATAR_SIZE.MID_SUBSCRIPT; @@ -215,13 +217,7 @@ function MultipleAvatars({ {avatars.length > maxAvatarsInRow && ( Number(icon.id)), - ) - : '' - } + text={tooltipTexts.slice(avatarRows.length * maxAvatarsInRow - 1, avatarRows.length * maxAvatarsInRow + 9).join(', ')} > ) : ( - Number(icon.id))) : ''}> + getDisplayNameForParticipant(accountID)).join(', '); +function getUserDetailTooltipText(accountID: number, fallbackUserDisplayName = ''): string { + const displayNameForParticipant = getDisplayNameForParticipant(accountID); + return displayNameForParticipant || fallbackUserDisplayName; } /** @@ -5202,7 +5203,7 @@ export { buildOptimisticUnHoldReportAction, shouldDisplayThreadReplies, shouldDisableThread, - getUserDetailsTooltipText, + getUserDetailTooltipText, doesReportBelongToWorkspace, getChildReportNotificationPreference, getAllAncestorReportActions, From c3f8de2cc58a63bc23908fdc0a483976dd930a84 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 27 Feb 2024 09:39:24 +0100 Subject: [PATCH 135/213] [TS migration] Migrate 'SidebarFilterTest.js' test --- src/types/onyx/Report.ts | 6 +- ...ebarFilterTest.js => SidebarFilterTest.ts} | 194 ++++++++++++------ tests/unit/SidebarOrderTest.ts | 9 +- 3 files changed, 137 insertions(+), 72 deletions(-) rename tests/unit/{SidebarFilterTest.js => SidebarFilterTest.ts} (84%) diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index f5c4606fd335..ed6cdda3175e 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -1,5 +1,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type * as OnyxTypes from '@src/types/onyx/index'; import type * as OnyxCommon from './OnyxCommon'; import type PersonalDetails from './PersonalDetails'; import type {PolicyReportField} from './PolicyReportField'; @@ -178,6 +180,8 @@ type Report = { reportFields?: Record; }; +type ReportCollectionDataSet = Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, Report>; + export default Report; -export type {NotificationPreference, RoomVisibility, WriteCapability, Note}; +export type {NotificationPreference, RoomVisibility, WriteCapability, Note, ReportCollectionDataSet}; diff --git a/tests/unit/SidebarFilterTest.js b/tests/unit/SidebarFilterTest.ts similarity index 84% rename from tests/unit/SidebarFilterTest.js rename to tests/unit/SidebarFilterTest.ts index 148710cb2d25..58ec66698b83 100644 --- a/tests/unit/SidebarFilterTest.js +++ b/tests/unit/SidebarFilterTest.ts @@ -1,17 +1,18 @@ import {cleanup, screen} from '@testing-library/react-native'; -import lodashGet from 'lodash/get'; import Onyx from 'react-native-onyx'; -import CONST from '../../src/CONST'; -import DateUtils from '../../src/libs/DateUtils'; -import * as Localize from '../../src/libs/Localize'; +import DateUtils from '@libs/DateUtils'; +import * as Localize from '@libs/Localize'; +import CONST from '@src/CONST'; +import type {Report} from '@src/types/onyx'; +import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; // Be sure to include the mocked permissions library, as some components that are rendered // during the test depend on its methods. -jest.mock('../../src/libs/Permissions'); -jest.mock('../../src/hooks/usePermissions.ts'); +jest.mock('@libs/Permissions'); +jest.mock('@hooks/usePermissions.ts'); const ONYXKEYS = { PERSONAL_DETAILS_LIST: 'personalDetailsList', @@ -25,14 +26,13 @@ const ONYXKEYS = { POLICY: 'policy_', }, NETWORK: 'network', -}; +} as const; // We need to fix this test as a follow up. There seems to be some problems with memory after filtering got more complicated. xdescribe('Sidebar', () => { beforeAll(() => Onyx.init({ keys: ONYXKEYS, - registerStorageEventListener: () => {}, safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], }), ); @@ -57,14 +57,14 @@ xdescribe('Sidebar', () => { // Given a report with no participants const report = LHNTestUtils.getFakeReport([]); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that report - .then(() => - Onyx.multiSet({ - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - }), - ) + .then(() => Onyx.multiSet(reportCollectionDataSet)) // Then no reports are rendered in the LHN .then(() => { @@ -79,15 +79,19 @@ xdescribe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given a new report - const report = LHNTestUtils.getFakeReport(['emptychat+1@test.com', 'emptychat+2@test.com'], 0); + const report = LHNTestUtils.getFakeReport([1, 2], 0); + + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; return ( waitForBatchedUpdates() // When Onyx is updated to contain that report .then(() => Onyx.multiSet({ - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [ONYXKEYS.IS_LOADING_APP]: false, + ...reportCollectionDataSet, }), ) @@ -104,19 +108,23 @@ xdescribe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given a new report with a draft text - const report = { + const report: Report = { ...LHNTestUtils.getFakeReport([1, 2], 0), hasDraft: true, }; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that report .then(() => Onyx.multiSet({ - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, + ...reportCollectionDataSet, }), ) @@ -134,11 +142,15 @@ xdescribe('Sidebar', () => { // Given a user created policy room report // and the user not being in any betas - const report = { + const report: Report = { ...LHNTestUtils.getFakeReport(), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, }; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -147,7 +159,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: [], [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) @@ -165,19 +177,25 @@ xdescribe('Sidebar', () => { // Given three reports with the three different types of default policy rooms // and the user not being in any betas - const report1 = { + const report1: Report = { ...LHNTestUtils.getFakeReport(), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, }; - const report2 = { + const report2: Report = { ...LHNTestUtils.getFakeReport([3, 4]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, }; - const report3 = { + const report3: Report = { ...LHNTestUtils.getFakeReport([5, 6]), chatType: CONST.REPORT.CHAT_TYPE.DOMAIN_ALL, }; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -186,9 +204,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: [], [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -224,12 +240,16 @@ xdescribe('Sidebar', () => { policyID: '1', type: CONST.POLICY.TYPE.FREE, }; - const report = { + const report: Report = { ...LHNTestUtils.getFakeReport(), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, policyID: policy.policyID, }; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -238,8 +258,8 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: [], [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + ...reportCollectionDataSet, }), ) @@ -299,7 +319,7 @@ xdescribe('Sidebar', () => { const AMOUNT_OF_VARIABLES = 6; // eslint-disable-next-line no-bitwise for (let i = 0; i < 1 << AMOUNT_OF_VARIABLES; i++) { - const boolArr = []; + const boolArr: boolean[] = []; for (let j = AMOUNT_OF_VARIABLES - 1; j >= 0; j--) { // eslint-disable-next-line no-bitwise boolArr.push(Boolean(i & (1 << j))); @@ -309,13 +329,19 @@ xdescribe('Sidebar', () => { // for the specific case that's failing. You can then debug the code to see why the test is not passing. // const boolArr = [false, false, false, false, false]; - it(`the booleans ${boolArr}`, () => { - const report2 = { - ...LHNTestUtils.getAdvancedFakeReport(...boolArr), + it(`the booleans ${JSON.stringify(boolArr)}`, () => { + const [isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft] = boolArr; + const report2: Report = { + ...LHNTestUtils.getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft), policyID: policy.policyID, }; LHNTestUtils.getDefaultRenderedSidebarLinks(report1.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -325,9 +351,8 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + ...reportCollectionDataSet, }), ) // Then depending on the outcome, either one or two reports are visible @@ -339,7 +364,7 @@ xdescribe('Sidebar', () => { const navigatesToChatHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); expect(screen.queryAllByAccessibilityHint(navigatesToChatHintText)).toHaveLength(1); expect(displayNames).toHaveLength(1); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Three, Four'); } else { // Both reports visible const navigatesToChatHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); @@ -361,6 +386,12 @@ xdescribe('Sidebar', () => { const report3 = LHNTestUtils.getFakeReport([5, 6]); LHNTestUtils.getDefaultRenderedSidebarLinks(report1.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -369,9 +400,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, - [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + ...reportCollectionDataSet, }), ) @@ -380,8 +409,8 @@ xdescribe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(2); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('One, Two'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('One, Two'); + expect(displayNames[1].props.children[0]).toBe('Three, Four'); }) // When report3 becomes unread @@ -432,6 +461,11 @@ xdescribe('Sidebar', () => { }; LHNTestUtils.getDefaultRenderedSidebarLinks(draftReport.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport, + [`${ONYXKEYS.COLLECTION.REPORT}${pinnedReport.reportID}`]: pinnedReport, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -440,8 +474,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport, - [`${ONYXKEYS.COLLECTION.REPORT}${pinnedReport.reportID}`]: pinnedReport, + ...reportCollectionDataSet, }), ) @@ -450,26 +483,26 @@ xdescribe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(2); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Three, Four'); - expect(lodashGet(displayNames, [1, 'props', 'children', 0])).toBe('One, Two'); + expect(displayNames[0].props.children[0]).toBe('Three, Four'); + expect(displayNames[1].props.children[0]).toBe('One, Two'); }) ); }); it('archived rooms are displayed only when they have unread messages', () => { // Given an archived chat report, an archived default policy room, and an archived user created policy room - const archivedReport = { + const archivedReport: Report = { ...LHNTestUtils.getFakeReport([1, 2]), statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, }; - const archivedPolicyRoomReport = { + const archivedPolicyRoomReport: Report = { ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, }; - const archivedUserCreatedPolicyRoomReport = { + const archivedUserCreatedPolicyRoomReport: Report = { ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, @@ -479,6 +512,12 @@ xdescribe('Sidebar', () => { const betas = [CONST.BETAS.DEFAULT_ROOMS]; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${archivedReport.reportID}`]: archivedReport, + [`${ONYXKEYS.COLLECTION.REPORT}${archivedPolicyRoomReport.reportID}`]: archivedPolicyRoomReport, + [`${ONYXKEYS.COLLECTION.REPORT}${archivedUserCreatedPolicyRoomReport.reportID}`]: archivedUserCreatedPolicyRoomReport, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -488,9 +527,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${archivedReport.reportID}`]: archivedReport, - [`${ONYXKEYS.COLLECTION.REPORT}${archivedPolicyRoomReport.reportID}`]: archivedPolicyRoomReport, - [`${ONYXKEYS.COLLECTION.REPORT}${archivedUserCreatedPolicyRoomReport.reportID}`]: archivedUserCreatedPolicyRoomReport, + ...reportCollectionDataSet, }), ) @@ -530,11 +567,11 @@ xdescribe('Sidebar', () => { it('policy rooms are displayed only when they have unread messages', () => { // Given a default policy room and user created policy room - const policyRoomReport = { + const policyRoomReport: Report = { ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, }; - const userCreatedPolicyRoomReport = { + const userCreatedPolicyRoomReport: Report = { ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, }; @@ -542,6 +579,11 @@ xdescribe('Sidebar', () => { const betas = [CONST.BETAS.DEFAULT_ROOMS]; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${policyRoomReport.reportID}`]: policyRoomReport, + [`${ONYXKEYS.COLLECTION.REPORT}${userCreatedPolicyRoomReport.reportID}`]: userCreatedPolicyRoomReport, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -551,8 +593,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${policyRoomReport.reportID}`]: policyRoomReport, - [`${ONYXKEYS.COLLECTION.REPORT}${userCreatedPolicyRoomReport.reportID}`]: userCreatedPolicyRoomReport, + ...reportCollectionDataSet, }), ) @@ -622,7 +663,7 @@ xdescribe('Sidebar', () => { const AMOUNT_OF_VARIABLES = 6; // eslint-disable-next-line no-bitwise for (let i = 0; i < 1 << AMOUNT_OF_VARIABLES; i++) { - const boolArr = []; + const boolArr: boolean[] = []; for (let j = AMOUNT_OF_VARIABLES - 1; j >= 0; j--) { // eslint-disable-next-line no-bitwise boolArr.push(Boolean(i & (1 << j))); @@ -633,12 +674,18 @@ xdescribe('Sidebar', () => { // const boolArr = [false, false, false, true, false, false, false]; it(`the booleans ${JSON.stringify(boolArr)}`, () => { + const [isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft] = boolArr; const report2 = { - ...LHNTestUtils.getAdvancedFakeReport(...boolArr), + ...LHNTestUtils.getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft), policyID: policy.policyID, }; LHNTestUtils.getDefaultRenderedSidebarLinks(report1.reportID); + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, + [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -648,9 +695,8 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, - [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + ...reportCollectionDataSet, }), ) @@ -663,7 +709,7 @@ xdescribe('Sidebar', () => { const navigatesToChatHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); expect(screen.queryAllByAccessibilityHint(navigatesToChatHintText)).toHaveLength(1); expect(displayNames).toHaveLength(1); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Three, Four'); + expect(displayNames[0].props.children[0]).toBe('Three, Four'); } else { // Both reports visible const navigatesToChatHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); @@ -681,7 +727,7 @@ xdescribe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given an archived report with no comments - const report = { + const report: Report = { ...LHNTestUtils.getFakeReport(), lastVisibleActionCreated: '2022-11-22 03:48:27.267', statusNum: CONST.REPORT.STATUS_NUM.CLOSED, @@ -691,6 +737,10 @@ xdescribe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -700,7 +750,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) @@ -733,7 +783,7 @@ xdescribe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given an archived report that has all comments read - const report = { + const report: Report = { ...LHNTestUtils.getFakeReport(), statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, @@ -742,6 +792,10 @@ xdescribe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -751,7 +805,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) @@ -781,7 +835,7 @@ xdescribe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given an archived report that is not pinned - const report = { + const report: Report = { ...LHNTestUtils.getFakeReport(), isPinned: false, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, @@ -791,6 +845,10 @@ xdescribe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -800,7 +858,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) @@ -827,7 +885,7 @@ xdescribe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given an archived report that is not the active report - const report = { + const report: Report = { ...LHNTestUtils.getFakeReport(), statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, @@ -836,6 +894,10 @@ xdescribe('Sidebar', () => { // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS]; + const reportCollectionDataSet: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that data and the sidebar re-renders @@ -845,7 +907,7 @@ xdescribe('Sidebar', () => { [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + ...reportCollectionDataSet, }), ) diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 9e70347764df..27da8348f43d 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -5,16 +5,15 @@ import DateUtils from '@libs/DateUtils'; import * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; import type * as OnyxTypes from '@src/types/onyx'; +import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; -type ReportCollectionDataSet = Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, OnyxTypes.Report>; - // Be sure to include the mocked Permissions and Expensicons libraries as well as the usePermissions hook or else the beta tests won't work -jest.mock('../../src/libs/Permissions'); -jest.mock('../../src/hooks/usePermissions.ts'); -jest.mock('../../src/components/Icon/Expensicons'); +jest.mock('@libs/Permissions'); +jest.mock('@hooks/usePermissions.ts'); +jest.mock('@components/Icon/Expensicons'); const ONYXKEYS = { PERSONAL_DETAILS_LIST: 'personalDetailsList', From f17da9e0d2e4787ad153c9d6fa6e2ef9b4c9c5a9 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 27 Feb 2024 09:52:38 +0100 Subject: [PATCH 136/213] fix: typecheck --- .../MoneyRequestConfirmationList.tsx | 23 ++++++++----------- src/types/onyx/index.ts | 5 ---- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index dbc94502bdd3..21fdda1c58d2 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -29,6 +29,7 @@ import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type {MileageRate} from '@src/types/onyx/Policy'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; @@ -63,9 +64,6 @@ type MoneyRequestConfirmationListOnyxProps = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: OnyxEntry; - /** Collection of tax rates attached to a policy */ - policyTaxRates: OnyxEntry; - /** Unit and rate used for if the money request is a distance request */ mileageRate: OnyxEntry; @@ -88,7 +86,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & onConfirm?: (selectedParticipants: Participant[]) => void; /** Callback to parent modal to send money */ - onSendMoney?: (paymentMethod: OnyxTypes.PaymentMethodType) => void; + onSendMoney?: (paymentMethod: PaymentMethodType) => void; /** Callback to inform a participant is selected */ onSelectParticipant?: (option: Participant) => void; @@ -212,7 +210,6 @@ function MoneyRequestConfirmationList({ policyTags, policyCategories, policy, - policyTaxRates, iouCurrencyCode, isEditingSplitBill, hasSmartScanFailed, @@ -240,6 +237,7 @@ function MoneyRequestConfirmationList({ const distance = transaction?.routes?.route0.distance ?? 0; const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; + const taxRates = policy?.taxRates; // A flag for showing the categories field const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); @@ -275,8 +273,8 @@ function MoneyRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); - const defaultTaxKey = policyTaxRates?.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) ?? ''; + const defaultTaxKey = taxRates?.defaultExternalID; + const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const taxRateTitle = transaction?.taxRate?.text || defaultTaxName; @@ -481,7 +479,7 @@ function MoneyRequestConfirmationList({ }; const confirm = useCallback( - (paymentMethod: OnyxTypes.PaymentMethodType | undefined) => { + (paymentMethod: PaymentMethodType | undefined) => { if (selectedParticipantsMemo.length === 0) { return; } @@ -565,7 +563,7 @@ function MoneyRequestConfirmationList({ pressOnEnter isDisabled={shouldDisableButton} // eslint-disable-next-line @typescript-eslint/naming-convention - onPress={(_, value) => confirm(value as OnyxTypes.PaymentMethodType)} + onPress={(_, value) => confirm(value as PaymentMethodType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} @@ -833,7 +831,7 @@ function MoneyRequestConfirmationList({ @@ -850,7 +848,7 @@ function MoneyRequestConfirmationList({ @@ -896,9 +894,6 @@ export default withCurrentUserPersonalDetails( policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, - policyTaxRates: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, - }, iou: { key: ONYXKEYS.IOU, }, diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index a010b5459d97..6846fc302639 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -26,7 +26,6 @@ import type MapboxAccessToken from './MapboxAccessToken'; import type Modal from './Modal'; import type Network from './Network'; import type {OnyxUpdateEvent, OnyxUpdatesFromServer} from './OnyxUpdatesFromServer'; -import type {PaymentMethodType} from './OriginalMessage'; import type PersonalBankAccount from './PersonalBankAccount'; import type {PersonalDetailsList} from './PersonalDetails'; import type PersonalDetails from './PersonalDetails'; @@ -38,7 +37,6 @@ import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; import type {PolicyTag, PolicyTagList, PolicyTags} from './PolicyTag'; -import type {PolicyTaxRate, PolicyTaxRates} from './PolicyTaxRates'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; @@ -155,8 +153,5 @@ export type { RecentlyUsedReportFields, LastPaymentMethod, InvitedEmailsToAccountIDs, - PaymentMethodType, - PolicyTaxRate, - PolicyTaxRates, Log, }; From aa4522beebc54a73a1a7bfcf34a7cad3d9ce87c0 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 27 Feb 2024 10:29:33 +0100 Subject: [PATCH 137/213] [TS migration] Migrate 'SessionTest.js' test --- src/types/onyx/Report.ts | 3 +- .../{SessionTest.js => SessionTest.ts} | 44 +++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) rename tests/actions/{SessionTest.js => SessionTest.ts} (74%) diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index ed6cdda3175e..6151064a26e2 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -1,7 +1,6 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type * as OnyxTypes from '@src/types/onyx/index'; +import type ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxCommon from './OnyxCommon'; import type PersonalDetails from './PersonalDetails'; import type {PolicyReportField} from './PolicyReportField'; diff --git a/tests/actions/SessionTest.js b/tests/actions/SessionTest.ts similarity index 74% rename from tests/actions/SessionTest.js rename to tests/actions/SessionTest.ts index f7d7f8ed5835..f4c4d186ff50 100644 --- a/tests/actions/SessionTest.js +++ b/tests/actions/SessionTest.ts @@ -1,27 +1,27 @@ import {beforeEach, jest, test} from '@jest/globals'; import Onyx from 'react-native-onyx'; -import CONST from '../../src/CONST'; -import * as App from '../../src/libs/actions/App'; -import OnyxUpdateManager from '../../src/libs/actions/OnyxUpdateManager'; -import HttpUtils from '../../src/libs/HttpUtils'; -import PushNotification from '../../src/libs/Notification/PushNotification'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as App from '@libs/actions/App'; +import OnyxUpdateManager from '@libs/actions/OnyxUpdateManager'; +import HttpUtils from '@libs/HttpUtils'; +import PushNotification from '@libs/Notification/PushNotification'; // This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection -// eslint-disable-next-line no-unused-vars -import subscribePushNotification from '../../src/libs/Notification/PushNotification/subscribePushNotification'; -import ONYXKEYS from '../../src/ONYXKEYS'; +import '@libs/Notification/PushNotification/subscribePushNotification'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Credentials, Session} from '@src/types/onyx'; import * as TestHelper from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // We are mocking this method so that we can later test to see if it was called and what arguments it was called with. // We test HttpUtils.xhr() since this means that our API command turned into a network request and isn't only queued. -HttpUtils.xhr = jest.fn(); +HttpUtils.xhr = jest.fn(); // Mocked to ensure push notifications are subscribed/unsubscribed as the session changes -jest.mock('../../src/libs/Notification/PushNotification'); +jest.mock('@libs/Notification/PushNotification'); Onyx.init({ keys: ONYXKEYS, - registerStorageEventListener: () => {}, }); OnyxUpdateManager(); @@ -35,13 +35,13 @@ describe('Session', () => { const TEST_INITIAL_AUTH_TOKEN = 'initialAuthToken'; const TEST_REFRESHED_AUTH_TOKEN = 'refreshedAuthToken'; - let credentials; + let credentials: OnyxEntry = null; Onyx.connect({ key: ONYXKEYS.CREDENTIALS, - callback: (val) => (credentials = val || {}), + callback: (val) => (credentials = val), }); - let session; + let session: OnyxEntry = null; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => (session = val), @@ -53,19 +53,19 @@ describe('Session', () => { .then(() => { // Then our re-authentication credentials should be generated and our session data // have the correct information + initial authToken. - expect(credentials.login).toBe(TEST_USER_LOGIN); - expect(credentials.autoGeneratedLogin).not.toBeUndefined(); - expect(credentials.autoGeneratedPassword).not.toBeUndefined(); - expect(session.authToken).toBe(TEST_INITIAL_AUTH_TOKEN); - expect(session.accountID).toBe(TEST_USER_ACCOUNT_ID); - expect(session.email).toBe(TEST_USER_LOGIN); + expect(credentials?.login).toBe(TEST_USER_LOGIN); + expect(credentials?.autoGeneratedLogin).not.toBeUndefined(); + expect(credentials?.autoGeneratedPassword).not.toBeUndefined(); + expect(session?.authToken).toBe(TEST_INITIAL_AUTH_TOKEN); + expect(session?.accountID).toBe(TEST_USER_ACCOUNT_ID); + expect(session?.email).toBe(TEST_USER_LOGIN); // At this point we have an authToken. To simulate it expiring we'll just make another // request and mock the response so it returns 407. Once this happens we should attempt // to Re-Authenticate with the stored credentials. Our next call will be to Authenticate // so we will mock that response with a new authToken and then verify that Onyx has our // data. - HttpUtils.xhr + (HttpUtils.xhr as jest.MockedFunction) // This will make the call to OpenApp below return with an expired session code .mockImplementationOnce(() => @@ -92,7 +92,7 @@ describe('Session', () => { .then(() => { // Then it should fail and reauthenticate the user adding the new authToken to the session // data in Onyx - expect(session.authToken).toBe(TEST_REFRESHED_AUTH_TOKEN); + expect(session?.authToken).toBe(TEST_REFRESHED_AUTH_TOKEN); }); }); From 34fc6664a7c9b5bf3b35e969aa4af934a9cad588 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 27 Feb 2024 13:04:47 +0100 Subject: [PATCH 138/213] Implement CollectionDataSet type --- src/ONYXKEYS.ts | 2 +- src/types/onyx/Report.ts | 3 ++- src/types/utils/CollectionDataSet.ts | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 src/types/utils/CollectionDataSet.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index f0b400687b12..cda3097dd431 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -584,4 +584,4 @@ type MissingOnyxKeysError = `Error: Types don't match, OnyxKey type is missing: type AssertOnyxKeys = AssertTypesEqual; export default ONYXKEYS; -export type {OnyxValues, OnyxKey, OnyxCollectionKey, OnyxValue, OnyxValueKey, OnyxFormKey, OnyxFormValuesMapping, OnyxFormDraftKey}; +export type {OnyxValues, OnyxKey, OnyxCollectionKey, OnyxValue, OnyxValueKey, OnyxFormKey, OnyxFormValuesMapping, OnyxFormDraftKey, OnyxCollectionValuesMapping}; diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 6151064a26e2..282013cddb89 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -1,6 +1,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type ONYXKEYS from '@src/ONYXKEYS'; +import type CollectionDataSet from '@src/types/utils/CollectionDataSet'; import type * as OnyxCommon from './OnyxCommon'; import type PersonalDetails from './PersonalDetails'; import type {PolicyReportField} from './PolicyReportField'; @@ -179,7 +180,7 @@ type Report = { reportFields?: Record; }; -type ReportCollectionDataSet = Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, Report>; +type ReportCollectionDataSet = CollectionDataSet; export default Report; diff --git a/src/types/utils/CollectionDataSet.ts b/src/types/utils/CollectionDataSet.ts new file mode 100644 index 000000000000..fc95a69dc9bf --- /dev/null +++ b/src/types/utils/CollectionDataSet.ts @@ -0,0 +1,6 @@ +import type {OnyxCollectionKey, OnyxCollectionValuesMapping} from '@src/ONYXKEYS'; + +/** Helps with typing a collection item update inside Onyx.multiSet call */ +type CollectionDataSet = Record<`${TCollectionKey}${string}`, OnyxCollectionValuesMapping[TCollectionKey]>; + +export default CollectionDataSet; From 25359bc0aafd368f2b3a5b0b727cef9362aacd3c Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 27 Feb 2024 15:25:42 +0200 Subject: [PATCH 139/213] Profile page UI updates --- assets/images/workspace-profile-light.png | Bin 0 -> 62632 bytes assets/images/workspace-profile.png | Bin 635446 -> 70565 bytes ios/Podfile.lock | 2 +- src/pages/workspace/WorkspaceProfilePage.tsx | 15 ++++++++++++--- src/styles/index.ts | 3 +-- src/styles/utils/spacing.ts | 4 ++++ 6 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 assets/images/workspace-profile-light.png diff --git a/assets/images/workspace-profile-light.png b/assets/images/workspace-profile-light.png new file mode 100644 index 0000000000000000000000000000000000000000..7e82c98656d26beba0dd81b3f2dac772f0eb722c GIT binary patch literal 62632 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

EVKtk z4&X-#o_aR9WBn$B7 z6{Hmz;8LzPjV%(mJWu%j+ZR29$k#qG zqPAHYx}_yx7U5q?sG+@vN*YgqtqGM&c?v?UGbfanJFxL}2*I^HKDKhs()onykVI=R zo@EB&;PZb=aoL{mpS$RpzW9kL|3cd>|AB<+4jBxI{9eFPjHI2nq zwI3LBEAiMMcRvKvw{= z4gjTM&FoV^3kOnWz!cOzhE{+5IfVLG^*Rol1!|>Gmlgn9gqke&6y;T3l;nz7(D+pH zf+&UPu#Gy=@(NnyVM5jAk8lE^I;=oH19JVZ*I#&AptCrb9xzLnm%#=M0yODq!&~4` zE|PzzNv58VY@s~k^HcuN0cLG#he)j-dI_?GV2eIkSogiJd72OOXb5NHEB$*U_Fw<` ztH$x7^p2EUT0^spM%*s8CTL)KZlaQX52@0a?2dM&5uT4q1aIYlzTG(RWd6 zHkS_g%nARIvIY{M)ylt?b}(_@IdriAIU3-1IyeFvmVNlaH>FIsS|3W6wQRtpBKaI( zDIG`I*?U%->@uI*#n&Hz^Au`%M=ER6iC!BM*5-4lN|4Khil?vqjHKNxUguY(GV&XQ z>ZAf)%~6GlGQD^7;<}zH)D~_2m4702!qAGR1^O?kwEdpxATA{ghaE_TR74Dshz`T- zuDjuq0QBvIs>by_=RH3Hv%#o2fj%erU@03&CXR6;L{9g}pd&w1p}sO3wcB$t`oB+boB3e`CmNQm5W zIuQA(eXql^P&jar2$AGhA59>8^|I+-uk!IEvlr20Zc8NgJ`(SI;=uT+db)j3ZWT84 z1G|)u^$;rQsagWPpHOoI`o00m(CoBCA_S)t{byY8StaO50=DyTm)+x)41S|R^&;e5 z4euN;ceW-}a$3>^y8D){fFrPDA{tRvAi5iOAQEETHbO$Wqc96`79d# z8bs*UdlvH77jW^3MW|5-Qx5dkN7j(-v}Yt{5R7h7cT{_|wO*^}(DHwIBGM`q7%piV zYHiR`IrKe>WnChLzgi*^luVfn^Pl9_>h3lrX3JzwEfi`p8?c*L>sOB}N`CaT*8eOM z|09K}uuO&jvps*ZLOocJA)@4*uU^h2Au@|Te*-_67f32!T_CuyuP*nxWh%cx4B zbr@2Zm#hJvIs_%uwA{}TWxqPGPFC>!Md^utHrdaio~~VRccbItu0*wzuK}xO4>97w z1co&4|B?(PnZ6IX_=m97J!^nlb_1z68&7cA*`fbHP%`P1Ot(KB_8( zO2U=B`W3teS|l$BRb!rGDz#E3BFdR^EhWXcblf1twxkw(KS~QmH{pW8D}@R|4f#C) zF*CgT5J0GL9cHr)qhNmdZ3>k){9TY`sP;k45;Qvy zs#IiL;2ktmXZVP7loSLt`jiS)5i01wz@gtDypecGzlkr=F${uSXS}wmu9E~e@}+qY zKlZgzzV<(g zP{q;!MJOr>cE@|D40xH4EkCqRF+dMf0?dR2@<>VHi1;yuI&Flyuo8uBkE~j^fInHG zUTcn{bf~%@RI_QY_i2?12g=rAKOz?6gi95YU}lZ#J^KErBJmzyrNJs8-p{{ zcf;>zhrCj#GCwej8g*VCA26f-8}373g&N?x(isNjoPLo)jrIX*6$d$I6)NN@&v?7@ zyV`}lc~zmtEej?qw^Mvgp>{uzo0w6r!%(P=1K*=idG(*JC{$Zy4i)O0HF&e}OraWl zN_hFaLQQheKU1N$V<_CCp!a;gw)nN9fkNddx`1+!sFVAgLgjD=BXl;C^RH2;;|>31 zg$jAv2$dBX;I}ztoK~pvZ&JROq4BOl-EmEdno-S@{5uMjIZzhdMO*zZDAdKI7ly4+ zO=4u23e|P0u@5dQ)S!hO6zUcHFog=aQC5eJ6>2#SZca7#Kqbz?IUkXz@Dx7D9(y|* z-t_+(g$hMxZ}=JK6e{E?<<(D_^$E>8aLim+sI=dXQ}1l(MTP1!deDq|8Cs#@6H}t# z)qf&&MWNbiai~zeMU2yPg<1>_xvo$_&7g)Ks8BB>Jvt5Snc*3#@3Orwou-iwpFc$n zJK{`PA-1voT!nh6f$@faa;FulmZy;y+LASbyRJ~@{2bBUSE$%OTfWG}kK8#{sNldu z!A)S%8!A-p#wJBj{$X{)-Set&ulU2fK4QSkOji}EW-`ZkyF#5lsZim=RW!JOoQi*P>CtQw7gMT)}Tds0QKhR~e(MV=@@?t>#+E^qcTp$hw(>o7-C>hMLg|1zOA$0H^XDz-v} z?s^}>!|=$H@!dD<-%VtLHny%VThw#!dX9@zYjo0q!oTk(LWSjHa&$AIHhU^f&c|#Z zc}`Zk8vnSF@o!hdNR5GmBM^HR>>q9TE2l}<5GGVE=a$p9=0l!xOs=Q2dl0h#4-09v zwxaUCjg&jgu_0^ti3xRW6@<#xB2oJJ-!{Y5@C76Bs7~|W7iO-_vHdkm!2aumnkJxY zf0y?r_kKd9%}}->@FxQ4>HOEM!1Hh{DC|d4Z%-2A`RBXF@p#=Ki&_}q5>~&(nxCNZ z@!+NS$K_i5g9HDB{V8X%H~i#V&)xiQb3Q>o23902PuHT%=goWjm%NeQ96jvgcAwfG zbFR(Cm=)soJg%>UP?h~w{#)+gXsBVG^>B&*zNRArj}+qp5EZ%@ZQ~stuy}b0ixf{F z+^cGQU`oBmggQdC2jJd7s6d}k;fuFonHZQ+&sP(RF%`<+1T z<8t0pG>Ct6DK987;kwE7e*42Uz$^@%!NdzB`>_^35ixFys z5kc*vVsJoDE3!({(~E zS<6$&AWQRMTAU+D)TNG?rlPwgD^x_eE&`l(5yYe6juv}@Qp+-p0EG%0GbHm1fEy^X zz?HdXBhLS}AXk379RE-;ANf3@B}e^8>V^%fDTS)_`cSNc7c+$u`TrF* z#8imGC%g@1v;8bZ2P+51@ZDYOe1reGOLd^Xm{5ZVK_j`wGilGJGZLeIN};kP{k;?| ztL6XwGt{YqKnxDKL#PGv6ag=41t&6K!3V!tt~NT3icjP74gW;B4=3EE7CXdfIwjVz z+LkaR)I9--d)h9U4qHLo6RO~TE5>bc(@7MrfgymzMhK{dYaCW{qRO*sDWBZ(EBhs( zlJpgQA8|HscljO1cC<8Bu4xTWouZI29As-~fyA&!C z+Z7%E$+d$(x~Ne3=LLnTiSn)XKlvw81)(z_)OyHQ3S3f1EQ2Nr<>KP%+fpc#L;4a) zK1F~99lE6z$oOXz?OY9K>2;!>RPc$opA_74LTwpGE&1BbH<0z2+ATlbOrZ6SFS3DF zmeARpG{OXMBV4WZhVqa!X3XvSI`_@CfXNoK--VR@7LCPBXT)+j*e?l{r=7doxwF|V z?H>~=31Sw6`e+p##WRL}Fk@NIKW1A0MT$K~Pco-jh(JNp@#t8}xo zYl1?i{pl~YbOIvG0d+qrqqeBVw?z?kW3d%8Co|#kRsjDA@OnB#5!OjoaH0&%aiG=Q z{u>i20}bs66%>VH7z)V8+Km`QT>T9~Eq2=MENu1xHF~%tl5IaFr6>mwOd~hG`Y}93 zv>?qgX6P}1Dnoh9=i);;ufx4!KVl_HgzZP=i5m-&4wQOFq2|NzL8wxNw%^bXzU1Sh8*v`x zku3v4#o`AuWmh$YWtqda=34esy+>0@!D$&N2^F@ux>u-Gyi@Oz;ahOaeq7_qry<1f z{T*?Qu`t11C)9cMTOcE2?X@-_Sqd+y$+^dKSNMt02JIV*{Jf`A5p5ZCY@!m?&HmLb z>s^Rpv?lmah`~`TyZN((8k*@KcnvSkLE#=9!b{L_I1o8XEBCzLMd(T7kXKq0uiR3o z^jx9ha#x{Z_+y2NHXzWc90M|1*a><3GtZ`nfw+4kRS1>&&;W z*9cWJxO@4(b^+`+^+;Hdg_T4B{xxEjPZKJ9wem~8pgAH%r$o}iK~uz*p*bIPig{s+ zT2m5kNkmkJ+{9}NdnEFO*>dEDInfR!#edm5yB)VdAPQGe7z{#%t9k!Nt!nC?&JII? zl5N!bY&UT*4hF;L!;iNCOKM*U)CfKeDJX|Z!|8%J0>d-Jy z`wmrpv>j>&Nfa>qc2I)F-u?+xh9OdQDE){sK}Kt&;xj!e%B>pj&=vzBlR(9Y8CvWt zB49@8#HfcoMd1UfxGvpsiU@Nd1xn`14|*|B%M?(hOPRSmB)gBpBv)Xe9YB>9RXbGa-xP1^@8B%IJRU$54pos% z8$_fo_J|rvBS3{e);WShRcFwQok`kG8=80}ZBMY7{+Q$ zg+B?XX&R`|G*HnZr-Ax)Cs3s%P;Du)j5}1wHOo?h)J|Q8=@}^`&^f(BeXXr-R8k#h z)1N|;np(2GLQY=7)QI8-;73-H>og^qeZ1(%n(Z|U9NI!#SpjO`4B?KusM_Hc989`) z99M^_q>i{V>PJR-L?kdTqT@K6dofVsUjXXN1OiVuRDLN?q5VLGmQNs|NC$vQd5>7a z!sL}bDP;zuy+T3U4i3%JTomXmzpO2@|s$GKaxhza_b&-JP zlz*?-19xl}21aHV_0|rxar6z;PXsE9=~x;7DoSCC60o3XV-bd0Zc)Xr@4nMmxmk_} zzS_d1<}5@;V0Xhh2EUe(t=iGyw>xRk0N9FrB>6cjF{zeoDR!2iCZjMMzc)~gJl#NT z>%D-=Bxj^1fSU5EY<%@GWzSKOJ-8ThPLwjlc-+I)Cn_QKsL+NBR=_i;kHBH5Qhz9e zj1Pnv?UrD=hg4eqVMN)R`0~W?6 zqb#Z!@>AzrOFXV}uqrf}q5DP8N9XHFQOWG^$B8C}+8x`B zO3&?L!*mW>R2p4^iWw=~G#7OP^#*EFkv{kmKYno;D^I|wapCVyP{a`5e(9X~BNlnO zT&}0J@hH#x=rIzh^N3WLU+8o+i=gDppVZ-psN}AKor4s#(zQshzAx$q>J3z_;^ z?9qkzaCE;@G*>Bx1~s;@^s;Gu&4JYNst6wAd|5eycoeuv}Kb=ru2zssT#%Yhz7wXvF*mdj2pIOu| z<#pmIp#HN9`@>?@L;lx*TAZQqUDYy(78cw=Z5QXp@0Jc_ub^sEmC?0Uh3*9?Ac0v( z;S{e@M^W={w(5`_hIQMi0%{G<2pU2nx`?RQsFfZ!vd~OhCqy~YS5Uxf3a3Pjd_=if;7}rKvI#kIe z;X4Idun#ftuP#4je{imo_+14)BR|ih*dYncNbRO|4D3@#l{pn(sbZ-7i!^HK@x~56 zh?>i={ij#tnuunv0vh)7(J4fjU*Pegdck(RT>>&^}X> zN+U<@6@(1W>;DYEz_FGq89p}DODf?F{#kCi4Sn?V`>s}QNdTK^;1MYkAxk3GwQjpq z8XKm)`hOHfJ;k!7FaC9k|07V-KL*ru^69Sw^+E)IAwgq!Gz4Gob(;#7GGHjK#N*?s zle_>bRjG!i3AA|`QW0BCJTvF9^vqFaUQJdJf z!N^{|Ur+7*CZ)LkoLiGXW&A7vwI29#pdx>n)dvLDjY**LexTZ`NTN~c(q6+7QGSmB zhpJem1SWa}9Jp#F#cMz=p>`yX$P|@dGH~8IH3aJD+M&u|b1VTV40QPYb6s?ZKUTF- za2||isoKJFW#azb1@YEO=k0*XfWxdqWx7m50IK)oI6Hi!C0N)E`T5uie8i=9A$IWPmbQ2xyv5YX^(-4QwOovXJM z=c#+|cN*S$#NlBWNCXQ?S~*1QrbES{2ut>gskF&g(&k4_foYhEeZ#cD7y)TjSieZr zw*%_!Q1>}h{)3;3%?Db{AW*9_+Cx;rg#<2yY);t*m5pa6rmJfIT)KdVKuIlo4=8Fa z&c%eb)-GQeL7kntngp8Vj#S7R0cqY|Jui);z8z4%v3IpglAADa%f+XPs^}N*|EP(r z@$@l5HjF*V-DcZ+8;>yt38`uD?&day%5wdGp-@{EyuRe8A}TfD9sdZSqUn1W6*6Jz zFLiMh{{XP-g?-k3)<}iauZjEfIHu_e_2N_doN$>H(}N73=5fS z;Ckzy0)uanI+7e5?s;qxcgPImA19Kay$V}KPPYA<5o)dtiW^7#m76BUptcBy0W?IY z6;OnVAQ=M9^}Zq0%kz3#N5IzX<%p63ubsI-sj>6}U^!#WbI#()5V0RIMJMR^1m^8W zpcU_dj8pja8gl)}1}asBc++Fy1h}PO7&V`iTBj$UFSi^X&RGQlyB-L288`%Nu-o)N zBsU0eG1w;wwf+f&>XKrOK|;lFl!Pd0m6}`vn$Ny8j%%Y4-}KRqNi~cY88tj98BL9b z_;Tquw;wjL@1wx*^B$c9NU&?bm?z&9b(Yv2h&wdX0OG&+_MYzlRJvqH_vU1QMI{bv=bt~qFb6gk#I zmZ*YL-c_iC`R>kxszPv=ff?pVt^={+dYan3`fU^{1~BCE4=7H?pBPU>l%wjn++kW5 zGZC#Vemg?FIIm*@pC!~>TQK<({{~N`=YZ=9^;KIn{n~q_8M*s3e<9WXErl8e7W~%~ zs*G=Rw@L%`x!}*;%|UTsc+q$?WbWq`>XblzWx)w7ia>`Hs>)aM_#+ARDkcf_ z%+NyF#~7MBL-d}AYY)=I%?cGN2;014h@?j6vkG-ZNS8`Dd0 z3VFY*D3woF{=cU}U2r~#mPY3kDyn@0g_;^dy$X9u42dgHAf907J&5i`g=#M>OODP& zNV#yQXY&Ge=Jp8LKF0r8ua?6FKYDZOMFaUG8>q&D{~~OUWUe*a>bpy+mHiKsFz~iB2**QnUZ3x%TUc<-$RwaddLfP5qpTC!6RP7>%!<<8WzN)-PN_@mg>qUxiQsvl*pOrPZl1TUx0(p-|CQ$9@O< zL8xfpvxJ)8B-90jx{P?SdvPb!X8e7@Tl)aJ(yrquzz4UW^v0_bL+Is#8*I*qe2q_i zN`6I(-NXX*n?Yz(RxHv+R|w%1NqDgzWas<4W7eO2%i9B`zA zis24Yz1R|m`}r@$bn=5MUt;_6(lG~?>I*)2{8w}2OD>8+ZAx4FQGlqxNZ>k}0^#bk zYIAAt1{zl0M>o*&q5U9K)hRf=OQ?Hks0xe|ethR=Nj|f`vK%1Pu;D?d(l@aVPDTBv zftVay?b2-8EY9e~L0`DrcH2I%Cd{isC<2e=w;!BBy)+VMB+4}>{VP?BN)bA%iFP0m_ROSJ4?l>M$}zl`8>I6Obt)UrPnS&c5a_P7VvhU^z)2m3<^ zZ5ksVrp8}@*J?)N>I6{aNrFaaeTG^WWnzb$KOQ{s%RJk z4qY};;0N+w+wTtTCpDXKxRn1Z4kY`kK2QrBx5}nd{tF2kUD)RiYlpwui4|jyG(#E6 zn|`!4P{)hw;r7dUt6TEKGnzi&IJ92~)vzDuMWvocf02%33KccG zZdep+;q&&5Qc#{!W1z+#*5rKzcXi4hCU@+Yl{BS3_W(SsP#^3aQ)YgKFJ&KtG&w&0 z+Wy4#J%w5d?SHSRJja_-8~^&;neo3gZ_6mO<#;X-wsw7Mf2hX`{)6#%iMTUYV9RiC zmuoG{SpMsC+y1PlGx6tz{C68qR~~Q&Cg@u0vYhdskSdj`z+XzC;UQ=ZWy%?iYqdgE zl!gz6TVS8-G9G3qRoL{~CQsDVP22WEp=$Ht8Yt9fP4bUT_c%-2xRJx{*7&=pr^ByVd9vVly&{@=#8y&2;lLCh z5y9xrQU2?5Wj{^>9RECvc;00GqgzQNyX6k;jUX}C=cM;*MU^w*95n_Og~ja#t(7#0O3()a@Ur5Bwt6MWGsSF%wq? zA5l(irn`sI7%6?5iOBO(cTu5A6Ycm%Ja0b#<(vIRimUI|b(Ohoq~*SD>mkbYv-bR>3YB?Y zp`sKOaa>ZU`T@G>uKfb$>XuUw*r2jjs9YIL!W-mHeAQ=q=YH~L-tXmFvX5@D^ITY> zq#1<@KV;^#1mY@>kkT`_(%3I{img-jONrE^mCcakDO2J7u|;-urxhynSuXr+aD|*A zyBSnt!lm+b)4W1koZrSNHht>@g`QWI+wY9b_JW_eEM0^wCene0ArU{uQiAdxg^B{- z8~?bxng8&R`9{ighm#Uye~(Vlimv}rh06SW6slVa^9Z5xD?FNFuuEk?&h^LKQKqDD zGP4_3sG72V6zZr#y@gQckd;o@K{>ife#??~LkbmyD#*%B)&Xj%I9h_LnmF5=!L$%g zhx3Kxd6-wID8=MXru1G|(Eg;zH9$&jK$EKE}!$=nx2Mv)aoEG4{o5 z!SscmTvn({;U2ClRBMVsYH1YaIImFAi64b}gF^j0p}OFiq1aUSjiaH|5(gD3N^_fV zE@eu~03vHtt^?Nqba_-e<*>{vRFvX*{7UBT>;Oes^N->C6&|JzDb#lE63e&$lFg^o zb6n}uDM9vXU&GaJz;)qS;TDCOj46hgi~^7wv_2vhgwSYlL|F*Be+6g zMl2pcSf%hKlw#zyndh?HeoLVuo_uN8ba>-ft-@`EhTGxQjA$EHsB$U|CaAGN0tuw) z6#Zci_FnM{DkgXy_bXH}geLSlV&45I)W6xgq9vzc7!lKVDPbUf;Tb>J*iNABMLJ#v&CR_@akoBqF5=#b#8J| zp~7YCDdHxvDs_P#ZnvyvW6;_ag-Qi+X5f6skg-@kqJKM^X3=l&Kz*YAu?Lbrk-{b_ z|5vF0v_jnoimS6RqfkB19V=8oHfE{5(&8vUqAA^8&I2bq3=8ry)nrmz zsw%}pFzaWi*mQ-3ftsys*6YD(81Dm#8h?HbC-LWfrCv{z~MlusF@evwWG8ivb&K&e+0Y8_&cN}3Cy;Nuv? zKo5{X9lq7raZ5G)ki|9dYQQp~)2K7Y>l@3m%_$-0>ze4?o6>3NO6)99-F2+%{ zaJmt-`uJH-s0l8q{UV*>cD1BIY_NRxl;1FR0)|P7A7_ezu6u^R!E>=lmF@TVB53$? z<0_IbwR)M9r$Db!sKdz-3z1)cSXi*WglJ>O=Bh&VD|SFL_=79b3bo8D)c50v;r||? zRLMiWy%fK${wgkJlG=2lrvu zodV+DjvtO2%prFk8JXazmyW$c&Q%~l3WDVRy9(7%5k6XUPqDi52pTF9 zP4xcOh5Sch@J!?HsS24H6C7XZR3)Y+J_&MugIK^f;UzZwW)T2_3}<)oxa6~z@m(}z zf9U0&ZTOj-LcOUtunfN9#Q@9Y2bX&cs>EIWN68FxHf^rPL@W zSU?VGz1QfBoO9OYH(Wre->h$>%J=M{debQ;bheFw$dQ3y550_iNx20O2=%$0Srre) zbe%J5EA#j`Lai^OQ_EjjKZsEM#$G-}4>^}9q*JX2y&0W|JSvNkziqpj0bMVc zo@Kg`xiak;q>aKKt`K8x!v$!HXOuPyW#+CH>`_MzfBQ3??2vsf&U^djz-0xY_MqWc zIFisMQl{}~gxU=3XLlwvPl*;$CO%`)d*drE^q0uT5Gq+xH(`W?mXTd4PGSe0a?dsV zwX6r_RCH4NzmXjoW*pXClnyWgxpJWB1caCsSkd`>13t|Fx_jR0f13jS|-qU ziw;bCnNT%;1eHskPQfTsl=@W@=-*81DfG{)cuud_w%@to+x@H741UHibyezin;m;x zWhp|vTR(n15~>}A`Oml2Y?hvAWI{T1YLg#mwOj{byZSYh(th1~q{RLjQ}4NB!}qqm z9;UgV|GcB%TZRV9qxH`9Mn>?zmKT92ev4Iw7Q}e_Q<3k#+|t7j;@CrH2E2#N?tkA1 zbxdWw?#X_`pQ>L>s3}VuuhNv7JCq_bYZtB(%dV-2BQU;eWtoB1k`ZN0=OseDr{;kC zZl@mhK#yKD7@P+w#(WPHvo&6n#Xv@k-@03$0FBwk3|R9f^Fzq;0jtbH?so9Mych6Y zpJ7zey912s-EhW3qgJTa)~*bQQ9!5z^{OY<#e-25$r2h~SKFXj=e}|TqsgnDAXG7j ztU%+`g=xK~82JLB8jvMa1kf8i52{oaLi%wsk#l&lNUNCvs$)+zQ)X2K*Z7qRfls3E zrKOu}s*7WgnE)-Dh9tCt4^RuSq(oIB98nWT@)Q_~R`PLilur1V4;o~a+1UIm}7JvoxGJt*{8pn@|_ z(RW>hWr~VycUqRBCm0(g3hqZu`b(7_T8aB~Lptq}Octo4LSiwSj+^pxx_li0fn z6<%C!r38a51)mpde>I_MP67+c9~(gteY9(hfFmC81BgzaAh`MDBv*O!=ocG)tWQr* zsnQZcm5rJ`dU}spB9|-QZI#e8*YdYx)Ck?Mce|EQ7fDaj^a0nq2^9i$-e8~qWhENQ z>eCf!Da5_EKf1qYX}u<}YGguH%rv`e)}gwb@~!zh8-BC`R|iCXtsqni8h)^3#xx1v zL@Fu=Clz1iX@I-I+;1dQnK?4cUD%%>)ILq9XhSCr2E>)b5|Ad^$#%$h(nt2?b7^cq zl6MYh>5OW@BO#NWi?>8PnGYvv<-*cbA0&#p5Z6R&KCinuc~_(=({l}fSwg5iIFXua z`1w3P2UOh;N_(DAk!2kLSVpLQ^)p?m3oE}3t??j={A# zSr++7qWcBi%=&)$UyO5eK6+>}d-Em1JA^LopS&0I{HET|me$Q&pJ@2YvxEwHdGSOl zzr0doeK1~GTCKE%P+NB;_hv%ntU&GgC$RX93Kd>V8w}`6+U=U7X$jj3F+eHBr+|bZ zY(o-YRsEmV-1evdhGmx@1ouAWl zOJpHhuZZc)G18?u8$sqbslB`~A(!sh`cNr~)mwU=P<1`wcW=$x*RQYtMk2^;O|6(~ zN2{}hN~7OPsMpu@v*G=O%6yc)=qGl7{0&0IiNulRzW%O1 zO)1E^UH3Y#HjTiagzC*DSDl8-k4t{Gxsp)tujz-}MX2*N{e;p9F9hSN%&4&Z_}`9C zcO&Y9?T}&9Xot}a`fL2N|aM6OFQ|mj=Q8zJXcZp?LrK&IZzq!=q%}QwH z4I+x;Exy<5yV!ER1l4;ql#>&mw3>+6gP&b3epDtu_kv$esQ1VKv%M86kUty5+?b+-bQMxO&0Qi6|1D6PX{jlusf#7`P<$ zK5FL{{DNf!Kk2M!-XrRhW?bM74@xuc3kBuM9#8iXD&ynYntuGcolu#N_F~l`M*6r6 zt@3$R&P8(k5qyl9v!$BzSG_iujPu#LkxP#b9e5&jF$D7q{-m7MsK&pD;vTSZAVVtf zYLtV*5~n0(399#Kbz+wJY4Ay?el&cmXxC%8flzO$>GxX+l~VK<{tc9~AElDh>g^;+ zvAp1nphb?0(v2Ms2Z@h1R;;NPn@W&RCV;^nhk*rXe+yFG0}i$rvH?`e3;tLJ$PAgA z)(5C9_E=^&iwA`qI;?cErEIEp;~whuHT`TjPpDXbGeXT1Xo~f}f!;VIR8Xb5ys4-a z2-Vnq;x?Y1=VeR$daUWWq|+|oJPZTR*!AB+H9fSv2@j|S1^{gjp74)R5|$;X-a~7> zFejMBgGuPI!;ar>vHd-+Dx1Rgcs?Bys^I#X{!0iI3;6;ALd~V%h5rqmY_`cyZ3GVw z7KRk|_u<|CixsLleCth*&MDbRRCa>$x}rP4(b0PbFn6h2$D0PQ#~317^D#MVwO&Mz z!rRf8rqaX=kJw!vOK<;>vV2Gu{KZ&efY0Kw`U@5$H@4iDtgU(>n@$nxu14??dGqUQ z`eXCnoKR6FR7#O$sJRsSKPIS8!R_V3rU*VM*F2!1zxl6q_LsaypK$U{8t9xc&z?we zTNW3ZMZ~^v=uVq5a&Il>%q$_>2@8A`gUx0n0bff|`vWSWA~(x(DqSPWpO*&lpHS+# z6REOMFmK?MP(_b+eJp^mA)7t{zH^dL@pM~0eqcf=9cx>4-f>2-E+gFn^*vOsYJfip z6*x>#nJE8%Xjq~Td0>x|T`F5m%MjY_W3Tpq>COS9eqgVsRYpT5Gv>K@ORxn$CR0@M z8|9B?SwqDzC^>o-J*2S4taSTXKo4E@{Z6W${eRu{6AVfKeCJ#8e*aCOx`lop|5cUC zQ;Hs1idiy+&fE9#;g`HScI2$r7MvqgENhRO?Qxu;YKgA{#X*4KrT{P+U_ncY&2@Jw zc_q{sw2#*dpWD?HIuY|?zJA4cbNhhO!TF8Mxd$>eD)<6zO||Nq{ryGv?E6EL8}Gzd z1sDfBOQ_xebn>g6?+6?J{M2e$?-5@%6=8z>W$K7c)5}6$KRJk>n#4MUChLTTSNU~| zfCcL2Uq5bNPtOp_U_pttt&e zE+W)dI2j9wWPIs`IwYvG(=b=Oq8?+ET>GdegnC?FZB6w6MEg`JHV*UvJZIe;jRT${ zRHKFwp&Aj?by2!bQ&za0%aPgRBYTUtC}~7=v*tP;CZN~?&XHR}v@nxyU5pddkh=f! zRugy}^r1jHA_@9^20!J2ySh=>L#;mw&rN|9fvBe3FperpX+EPQ+r z=h8WU1K6QZLwL1y&6u-H+Aa+a<*o(>b#QxqB35bAGU&PC^K5C)@D0fVEx|MPAu zB1eufCF`%yT?yr4@W;Vqi`P|MQ=*g$fPE>b`-Hj=E&F~`Y3u<){dqtIq;4+@K(e$d z?*zBpIgS=Lyb6OcD7g}<%w(YM%wp{8>UT`j&<)(or(ys5M3{#!)jWHsNb1cQ(`5;j zP0L~UmUl|tg<8=IEMZPz5RviohRfASx(OGw^iBE|H>#gDq1?Jzrv;J`)I|RKXW~P1 z-sp7gCVD38MPw&m(9>1h6IAN*gvua7L1h#^1It)D@g!Y7!p>jm+Q)<^UFl@7Qz4&D z%*XFFCrk0NTr8m$->PDiE%oE8UXPhmjkKTi-c82+JB8U4(tmt%`E>M_5;_tV@AHnr z#{tHy-RPXV7WJ~^w{*RY^}=ZItQ0zjctY(;U*1daZ<7A%L;ofBA2~CEFiCviKVGXD zv(H`>|K{@x>A3pI!P-nl4w+sgm1h@1HChm+bPZW^dQH^a{{T^r;gfHw#ETNmTT|MC zYeLnYH|Y=!q821`x1uo@`~CWkFzUh1PdClLy&edWA_CFI9qcTi6tTCpe93NAo_iq0C>}UX}dz%S*_xd^wLB(=dyBrWIR+#l1 zmnO#e+E6$ob(Xrq4AAE5XC(!b5R(lq-!B0VDXNuj`A?i(uQtZhtSqqm(vH`u#O1vq zD*Q;4W2IaFJ^A%N3?eNERV71 zo6vEt5|ym$m-D%+Hx>w`ri7YIOVm5HY@s@9&StDdzWE(5N>0G7^Oge#8!l|C>K(Ke zpicfgkD;PZN|nivVh{zLt4+OCCZ{r*9lacNR32$V47;3yBPgR?tgV4UR&`A?IF6b+ z?BnEutCd_Am;rlhbEx80GIQuWyRT&)2{2;pbLcyE+*(oaC`6i_2cm2R)s7$;-iw5TTz&n1j_eB~-A}#PL)v=B0t!L3-N1;5f=!Nfa+O2s=#Dv;Su3_Yt%HP!7TIMky zn={_3K~m>urNnGg!8!MeQW2;&YTBxRYUchOQ{N@j%zHBKq-qFtbV+;hC86RfWn#I8 z&~old$k3EfF$fx`6GEldT|$j1G$Rt9k~sgT^6v{NIaAwA!>$nPI|-DeZ;;HdoU=fzEzb#6nT%70s!$or zc(fi6D(X@x91|+6qpt;_`adlRm8;r57OfKnMiu6S3V|6j=SH}69>W%;&HIGfkb|@& zRDzwAT`nM0h)*Z&D&}JP7K92O^KcBi&iBQRs5b~T2XRrh;CUh_PeIVp*rqd6RMaCuEz+K{<-%Eas!<5DCn-Uo7ucL z@Y6eP%(1R=1Ja9ygo?Thbc$oB#3*oq1)-9)Ce%?ID=+es9xn+9b-X)4BaiAn5$efdG9+(jHu!?S0 zxX-q!a+gqfA2zKCm0%}dR9O;gnp;ljC|>e`P-)%c!*R%rP>ELW5NZui@dQ|dgaxA* zYIq~19maMEmDQG?C)BK`SN4aG6DmZxBGgK$bqtk)PsUIgg(X(!>J;qBbh2F6Wi`mg z-*tuvDI`>Hz|3hfBvhJJd3{f)uRV7#`dM5|m-+d2fB$sIx!ML9kb zs%1W!iwPBT^Qi=2-TGP*D&3@u#CbfSW@=s#YQ&%J<}K?7J&g!8eg(CQ(>|ecuc`U< zG@)|ludn9`l>q*NP~pxULhU>8!-UExEIBn3m}VeOfjEcT|9Qc@^F#~k5h1M!6}wn; z`b4N)W`0SiqPzugK_?ZrlF=nwr43CdETNU6@D5wowavOmsK`idD94&m3+6iaF1<;p z&^+Um*AAh!u`lzGQCsDNed-pWiiXfhVWuRI-iT1+1Ap(}4MN3cntzf||L}G$N^*ig z6x~9e{{IK3#w$66O)D{5)00dXr-goTxv1HFW~dUL=l?!Kg*n|`L#=4tUPDzqeUqU| z*YM)}@mF4Ff`;N42dEQV(9UYE6f{%PK0_sd9EGVpFjN&f)$x*{LUbt^OPvtzRhljX zL6T0dU0mS)HORu4aOE|wBNiPa-Q6)%ZV7O#V&Nh~4GI2X>+6o8j%MQWMtyPlBlkzj z+6qH0`&=S6@V_dov{uoy3{^hf-;r8psDMxoUuLM=0IA<*s4yp=p`x|GP*qRgWvG__ z;eU#_fucbmxD1OHFDA-_p{mDwhRQb7Ql}?|8UVf53{^u#@W@anTxPb*WlP;Eqh9Kk zfbul5WvFr}t#BQlGHY~q&rll%GMvU+W2h7Y9V^KT3>7o^Qh(Sa*xh@3W~g&|ik;{_ z@JMtnEYI8U%lCg}sO&|)%}@=bewCrZoLq(q?|f#cs;4h9REgn#1vn+-IARcp$;hgO zfFsjgRSS0v)jrb&b&^94L#4s(qgMzlxTj zai~th>)$g}6zPfwG!_|ZzCNx=**q~+yfzHA*G&cEJ9Dh@mZ2(zj#NhDbpPcs zRR0lFP3bobbz`x9{T!+~-qsl^(Ws4$|Ae7#Fw~wRJJNQxo*AkdFel={1nuhVW1bjl z7Ibcl40Zb@hKd*6=q5NNi12ZJlA|Va)q8?D4Yw@!QLy*y>pIsmO2hJzg~#^{mHlEX zrZR?M`p9?{n2Yr?iO>{+lnME6DkjW^|0z8>ZLnTvplBE>mmu!B4vz$k7aB>PxkRSI zFPE%x`z>=0A-pz{*!+e!l+6P}m6_W%XQ*7774FwsA?h~_Ri`1|)h5D%nFt5T6-T$n zM^M)pDpBWhi*t%|7=|jZ#*l6j{Ln#Cx}6_Nhg7NccPSo#+K=juPU%H2kD(5hotdgS zS8s%*|ABXILjjEqP_l5Q-c$`6Q$Ve@@09%o1n}Scucw`%ZtO1cU)2GeVo$;mrqtkn zQJe@A_ipJvOz%nIv`!|~M*#63r87^(_)i~DwG$Ri{`rW3`j(%(bWUW#J2LkyD>l}O zR%^+`ubU+PN7{27<75;~{mV`b*<$wtD8TKQaJa~PG8w|W{trd`r&C3`5?+0WuHO2i ztK{FQf83$|MUN>S<39^YJc?ILV8f{}<{w;eq&?&x|G^(z$A2w=-Xo|ahL>p!`KPIs zESmXGhc^Fw{nK8`h#A{eG}T+-6It2#2zp8yRAZ3WCo}`1C z80v6_>buXaGsv8Tq|*U>Dc%1Mtfk`=**8>f`YcOEq{tWK5&w(I^x|UtjKwKqQSX_7 z(E;%kmy7?>?VH++|5G^tvGtp5&-qt~V$nIZKLs=n0j!)opM{g-=szO<=j0#%)nis$ zM@{~PT$%h|inITeCw)+N;i>+m*!{mqQ7oeMpZp8N^Nae&O!7bTzh7TbpC5`ptA8X` z3T;A>{QTPhV7~Rg`Wya+zURMN|Ey$A{ZI3c{Iu46Z|(C=4ETbN?W3laK6K!lNdakb zz?EZkhCIeeR;#t@_kk88TM4vUYk3X^`^-Scd<&@ZLH`tnYs$6w--rNJ}+ zm5#{jZFGR4+i&-l@?8(L?H`+RI`Y_3ehyuTxbC>tjsd*xYvl?ush+ZgC zF4|dG5i67F@;}YAY@oc^14g&zMNe?rQhZbYGIJq3T1Y-66hB#ME!^~-(^&t?f1lB3 zK4E?kJ{>o&@i-E>Bb7gL{u-)Mh5NG(G|IJilxQoLJ=2!B(J~$W$YrFvuZGaGw+4m1 ze@v-HZ=#op5y1rXkiUcU@n(RUfsXqMNP$oU8mu~Z^;AC=%%E}wPCVo^fNoB9uZ^t`RPovT7wspy$S zyQzQWU+>Yt6Ah00c>=hORXXP)EX_r&Qium3=g+DlA(`$|L8$VW@$y zLmE~Y>Kp#I$gk?iP=n7gU+n7`uGQ<(y4`XxZci!fi1u>*JKb%vsn z|4V?%xW0s%G-%E7I}wK9Of1Q1=h_#ZjZ9R#H(*9 zPwftF{iBGu+h|Wpcv=4eY(&?*Kvd@AWVH>E1pAUOnVU*UjZsGv_8*M zEvM8(=0=HG-_0eJ9Go@TJIX}&5(5JxW^<53F}SZ@1l@~z_}gZ)Zk$Ml4FwceDdIe4 z@ooKMrd62hikjQyR=(EfD%dCUKn6k({y5Xc8W zt?SGQDGNnPJ$)NcSG~a9MUPK-tGeSI@*6+}uNDL7hzH2WR&~JDlxi_pYIwHKTSH@|UJ=9G&)hAL`6)Jmtgm7!hyNe0xUr=8nsPRh$HIb6sbV?Dzf4eQIRVF?k zYo=w)IBD@e^R)`qi(a7SXY;yrQ>jpCo%e(lLRDq0I#^Ic+3Qyc>epAOXR6h!uUjV0 z10m&guX1&66xXsS>8bj(oO+sR<-Wx>8-$Pf_Wv4#_ihU66x9AeMKqh`RVF?iQ5DJ) z;0diyq+G?Jt8vj4$G{hWg~Iwfocj-&PC=c5+6ATJgMG~iGV$41{bB%;r}7GFE8IH< z?%)-`n`lQMM?j$su**tN!AAOoVuR}F!4Gz!hoGV?s3`#Y2Ca?b&jnSDzf=C>Vgd}x zzdipI9OVCE{5Odoez8V`AQS*dT*e_P@!tS|sRcy93}t)#^ zKxa9%_y>R^%oqY>JiJ@x=~v=^OHiQ+Dujdho3Au_ zF#i|hzd`&6Zn1_Jlo$LExMdu)_;1)cAJ`gqqZ~X+dxANIB(wYF&j>0YLNBs+z_uty z1q4R|l3*W_8h=UgTLhI}9}YPOZtH#?Kq9Vr{Qm`2jlWm^E!gcx=HH(GMTdI+FUJ2L z_Rd7RO&Exx95Ku5|Nn4n#>zw=yW9S9qcwL@y%t?G`2vQc|Jj99{Z5!AIbqDW|pc@ z|5T37eZeQ}M+$ZB?|%-BKMJ)Te}n$tZB+AV zpZ;$*{zKCEzy3ef_}>*jB#li-6`v||uLZyG|G;@NRPebNODscOAI7iEPO=rpwP@)2u!3XVNXy8nJ@+l7Ah0)_rug;c&AEr8Kr<66XxqyB=DrDexn!TKTQuyP-*Of@B;`J|K#twP{*yv zmnztxaK;fCY8pF}&{U{nC$#7gigi*?V;iBI!NOZ>NpyeDGjmZs#%n>9Zs44(9ju)z2QK+`yzcbW; zCGPZ^p$hsg_>nUzv|rnK%lLKWtvKK+nTl^1af^&1H`JL$fMT4FYf8LIYL!+MIW z@gAyN=N@WTt|Qbml>kAdU>Q)GyOsG(qBG6l?p`S$V3^gG6Cv`6wDi)96L?l`wt6=7@JruS&uTXLP zW8>R)2o+!l_%2i&HM`gTs)&3_sKEE8z_b-g&;Cx8JHa?isJN6+Ef2dh)PQ}^P`HPx zi%N~73}Md+=K(g37%CHJEL2R)2o3|9WtUQs=diE1` z2^A=W3aXs3T&NKS+m>N){*K{?a_lhtY=1|2HDtnBob8RRLWPn0tLSkYBve^>A$g5| z66!=#p@JDjUwI2s(a6G~S2j;ifW4J2m7$7FO>%_9wu&rNi|G_Xg$wmTy9`yBtzoP= zM4m09OXdMRr?(1GDxf6@@Z|=P;-D%7|-pKx7f*=78!cz}z2pwMq9 zskAK5iw0ODRHuPZb=8DQp-$a|T5k${^{ca6s3^+6j!*$8%^_3>6*!4dtrqR;#Pba! z@5{_}lTCH=_p2#W@kHQww*hFGq0%a$3IaixC~TMs1}mBWNey@Z-(uu*ps zp{CJ^hW+(I1pumm-z-!BsQ6SCjX~Kth9wMY(ZhvGOEOeib-nV3NI|Q%;b7eGec1J{ zg?G!Vmv*IVWvHa6aT=kf(V-|i`Q<|O1%E_;vrwthF#5ho7t@X*Of$z+P_SI6!*-$4 zx>zs+zwUA6wZlns<7Yf+hDre12-W+4k5FSlIx}eKj6xm8P9#rs0)jn4Z4-Y#p;j73 z-n(*Nc4{L?E0;xqxKJODIr1yE@sn(CwNUFllZzmnh|mp9g*tq1?aCQW5UOI2P&f2{ zJhgn2oh5ajCS-~MQy1z`EiUhdMh+1U9?MX+`>;L!p&Gw!7LR{*Z3Ives#h&G#85=g zBK{~;zgji^_2YjM=L~OUN!Ds{zzm_>NH!AHCb?FGYHtn?FVlyOS7^OZhea+r4`U;t z>iC{vNg<)gtwP<_|KU{ID*}*ugKB7<_>Pn>>IH`4eAl;8sPEYx|5%TI2q)Ga?Omfy0W_K%X>?=JXjKb5(d?ZB2g&xnd+8R}r;K>|aH!y2lLf51?s9wcqg zoYYX=HcCJ>JFB5qfQow)JAZ)~u+9g>%wUq!Y6HnoiQYR6)iIt|ly{i&{$UpV7}gpp z#n}o&rGB=oHdNbfs7z*i@a2?c)sD0*ZmvcM`(C_l7eno@KSf>tcNhE^vZ1cby1)Kx zsN40|Grh3jmn!RUyzHhc4Mru^aE+*8sPQL)4VUzX&GUxZZY0GOY)@M!P1{izl^)al z!8o!t<}zu?3N)^ALoM{@uQQq(%41;b<1}iej2C_`3%ilcHEIR4W$94UKep*<^oS|N3^%w2^^`OHS}C4@ao~ax8PQHJy-Y=zS&Idx9ylDKULV!o6e^y_(Fv6_u+31x7uxd)HR1I% ztm)m(MyN_ZEf)M|CDb4F6}3}n*3Sh$OR7hJ6W?$F6FVHC^5NBgIGLYMsPZa8&44S5wPDZH1cyGafNLPImz#CtS6!3@Pzung8y$2D*6&?y|wK8Sre*^@LcG87XIfY z@$*j(@T?y;mzn!`T1hEm=ePWzp<)C+o=|~@2^E}+y@c9k!G1`n0H$@~4e%pxkCZ}& z;ivKWgldgM^$g$%Au<}p&-48W>?hP~=SL@09RV%EZj*ITLPZ$rbvDs^4^@i|RPtw` zA0v!C!E1^2?+3t$7B{lVg4aP+fl+xcy`HglYZpBT)khOEgo=+MRNx^(MTC0>eiJJC z5GvS@ALT+u(B~&;cGL%`|(u`b%uN{mn8weE$L-nqYO{fC^(;`?s z7ULms_c{WhGA9mnM?wYW7yKAqeO0hF0$lLpmKa!hV|Xbm(GT4{`EKDM{LV1mOGW?_ zrw4WrDiDV1A&F3d6A~(>p?nR{hfo2~d{zg&4Zsr#Ril6MDe7=WWonxVb*k{#gu3LX zoGiYV1RjSVR7jIkPeS#|E=I-qv(PCqIu1B7hFBA0`y}S5)tK@#Uu+u8cUvLsajRn8l{*X{( zX?s>eb#xDGm&vGHNw%glatq)jAQXWDO&sW~a3Dgcf}JcyIYw_n-3ibj*-5xy%)ecUm#&@KIWr+BLaW>;xJs6=5Pb5?T zD%a54m^jCW@QP3YSah6db$gijG&Osu0+?sz^JaSpRcwC$>r1GwAXLYdOsM8fbr@zs z0VOlq%!kh*)I#AQ301>2!c&EzT<})|Y40UeElQ~ipIeruIMd9e*UUiCi)?X5;pBGyJ1Mx8-mnA|?)VHjVbd?E>=X&Yqab5pb`>7aUai5y45>nRa z*HizZJq?fQuj_xPhQ_g-gqrjl8QEQ#wcp23)9+B{4AoVJ_*QtFXol%h8|tmvZu$fe zs`qxjap_IrUTS!An1tA*-w8G5owMIwK3Dzp=>f1(KZ_)8%)KJc()CUNX4kmlsWtV_ zd9Cec?flpJoB8LC7hPdFOlkcn_50sA%cn~UX&EfS&`O!z^s~FF{&nFe!x|sMcg>6X zX;SnsmQ-*3Wlj(4uj{`Y8Uw7E|4XZ1NyuIbLjC&;)tJGr`L~9n?PjP^86KXaA8||> z%Wq8^n#%={3nSzgA#b4NB+{)_x3+2f1 zq02H@vA*y=6S4@EUL0;xy*hqg&&I!!dy)-5t075i#Zolu|I6dg>r{$A>bz;-V}gy( zTEFfSkKf(Sy7=wa%+a5kxSV8K+1Q;n7^*TJ_CwdQJX$bfDd91%8!FFP($N7Y6w${d z@DQ^2+w#ON4DCXLqu2(!p(gQ%vW7A)(!HT_bTJA}4@Nd4Pjp^BRjZlU6J|_i7pIE%<3p zs2un_J^inq%Kbl%KM*RTS3+tQ6j@`1P|qUN$Z{N&7sX#C)Uyr0vIa7oZIMvBND36K>YQTlM%^d#z0}mhRP~9F|@kEZ&)K#@iGp?$-W;mglgUs z`pm)LjA-;6q59ThsA)n~%X-aN$?FM~0h5tO^N=^r6Y4lZd=a6_*@mA+Aq*;=ZTAvt ztxkT-6Y6NgFWZ1nJ>AchZMa0J5cC&h7a9=t!}%xfRYG+n*TX5!-O41P?p1wU?b(Yy zOQ=eN8yu{awG~!#Lfz0zD@yIp{c=5_HD6?W&GaG~fW>*HNkUB{>TseO9(ACCRDaCwlfg}?NyfMgenBsP%a1H^@M7M z{so9x^Z)iCWRhP4LftY#RjvxJREFWu|0RS9JK6BlDC}&uNmu(j2vyD6`gahZcsHy* zLlsK_)pW}VRXp7b6tfQHNFKCKKkLoa)4gx(2f}~&lUx42Jq7+H@NbHQ%G@rEfNK4! z*yr_RmzYULVZF?Z79!}>)n=3i!@De;@rrB|D~uLFjNj~6zIrPsi+M-9en{sgTPF6B z7gQuv?1hj}lUKiJZ`*>}`Yx6JKZ&0gS+zIPRHIrGS}3}LzZ@kV>? z(fvX*Sx?*rFR>-sp5}(yW+of3{dKSA&>>jmA{$qzW;z4GOxWF#P>+`-PATP7`%!O5 zeJUFfHY>buC)K|YAwtjSmE@J!P9Fceazjl<2Xm$3eK1P%yiCo~(G#j4Q-s!Ed{qs> z1cJ2B;5~dosQvXygcevX*vf_R=g$y_e)>mY8OY^9CxjYK_4D{S!^cBy`WY%m^I5aR z__QZ791aC5iG6c)HGFX#)pIM9dKZtx(8`3W#*At~!!&~0yb#v{8*n5`%QS7Mxw-AC z5Nh2}PkPP%1);J5C>i=GdwGsf)y@Z|eg&cKO_J!{3kpF=La3WwNT`&S>a<3v-id`~ z(IZk1CyR#4GQ(wtmQMR^W`(LaS1{D!BUBn0?@3(~d0ApB(&+2WIlsbCD>7>E-BN9h z>j^bBThc&%1I`obx%3+dRdPR#GuF-}cM@u~PK$(^vAdl^vGgZVc7x^#b-_@hM#~EZ z#%FmNY(+SJkP9>U0z=hJOPQ%cwnYDwP)#C-q}5hiBGjs(K2NBH4S(GQo*~q+RtS|& zkaPPOD#FRlhPq6sQfkh+p>jpZ!8KAnynG)HbW2k z7@^iT{H1aI%rn%nR-d7&&H`$_;s0LY7qvpl?|0G zu=)(uc-y?BAG#RV8;=>P9!n$^wEhrPFDFzeER6hgvk-G)Ck-zigA*EFG?BdS&mHEn^(xBT+?TYl~ozR^%sQH@RO^@@_~2@Ux$ zO#oG4ON3gA5Zm*F3Ns7<6*`TBy4^~sTBs9rg;1~B@FOV!byv_2jwwPd!H#TYLuGSN ziqRKR*Zn-bkpG zI<5U%{t=*JIIasD{wd_r8wr)6XM$`qhN=j8RM3B6ng(b{DhtwX=E}ws{Srf!IpAJ) zROt1DrZO0(Y@ATT1wySwNU5s4q^}SESoIA*RhVtJ5Nh24=Kr@T0_xg^ABmf+8tR>d z>Qbq?l$--9w&&$5+xHpj1fN!R0~pN#y`E49^+T{(2D?V6;|PVt2w*n};k3Nrrxe54 zcJo_)mkjHq3xtY`H~i~{D)$j8^ZGjxRZ*9S|7OmTp{h4WZu7C_he>tC-rTM?RLmHv z>ds04%rK%8tZ!AkuUcUhJi%Nzc>p`!H{4BPfOq1Fh6S;x|Y#`=1+FJlwg6yb)zTL;LAlIbW~d!LA|q676P#Jws%8e}9}_AM z9gTt&Lgj@|2=xu64a&A)oI=YDgnC$b7h8!?o0n;IFH^`g`z0+@ByV1lFG$s5851g1 zB~;e@b3#41Hy--ohM#tG1)*Y_k!IxphW4SGw;jO5<*$8=P0k2&?*U`-k0I7Q3v0$R z&ui}i@Z4TIt)qerEjplgN;&RD&wM6f_W)+mP)VjO{`T-G*sgfR+p>dpLvv!3U-3tT zQTE-4N3!z>s4>4Z>fF`BfNE<_-{tC_gZ)oJjmim?AC)f4Yu9y7u?fJ(-pfrpCh$&Q({G7&#V01M?=`Pxye`vAs7ei&>{u4l7 zDscJ$DbALU36+cjKpv9Yt!ye1P;}i;tyzBaJYmED+)d#=*>69YOK8qq7Irn)`OV(B zEwK&4Ks4Re^#4D(Gr?qs$igxvHV@t03g(VD7Pi|eauYol2gQh2JX~mP3Tz0i-PPF< zEEvVxP1nh>>15`rXu=X%Ozms-c&3a@|AoAx%`?mZf_iAn?60}xGiEXo$AS6pD}V8z zCW5_r%IyO-;}VMPoEB1r+8h(JN>G_&{jJ_A(<94w#wLAjKkM)IL)~@8YfK2TiY_9` zBO=NP$+i2Ehx)A+sP(vd-UDi#bp6N%6ez+?BgqrFMVQ)Pvx1OhA{2ECGjYm5|? z0xHy!=TyAK98i1BrBZc)>S5XkYDyr))W@au0$paQe^OWnq9jc5f;&MzX=xrG^sBJ0 zI)E2^76zzNol+-RUJNcGuqia|e~h|~LD1uThvOnLWj$1Hgspp0Bax?vCo1;@N^QHT zcNK>`RIb3&ZCY>wsIo-8H8e*as%Sa_RCsNLk+=h?@(}}Zwuh=pyWrGB_?T#6l7OXz z{>x7ZkK7tcZ=)EU2!(<)la<~e@bDlTPm@j=2bFHw#_Cy~JXD$9x!tHz^H568{KY07 z0TpZTR1L`iHB!z)74ZgudX$ZJO$E+L#U~aWMz1C-BKnu^=^5c8<6y|sxZV!yb$Ptq zE4r9%n(4hl57cQ3VV?qYZ`ZKwTR9yWgI~HzT-&bj6laKHC&cd9)DOxPJWyFuK<#mn zek!GI##MN#W?lj+v)53S?Q)HlphKSq>dS5wnf;CW8#H3PE%1e5fjY2 zKhNxNBOGNLsC32LfYf>Of_-4W)mDZsPf~GtqQhWZiazHg(;U7g2!E!(yLRwPOeg#yz zQR=0gAh$YbcPbbaA!L~=U-wHB)Zg&u9WvT1F=gD`SkVxb$HL&8GtS3#9Mm!zvw-T) zgvbL`B@6;pr~E+y6TMW#Ljf1Z$v{;-F5*tbPYV_;v{QRT9NF;4dp+C%RPj`-P=g-= z>Qd)r2h?b?Mt0r28K|;Ryk8wD5x$uU&Ic-Y$0$4#asJgDZUxguh4KX#oxOg77N{%j z)Sggs!=K;$5`bM6GCXx_70&?`O954_83(EahlM;)NdTz1ICc{rjgi9rNd#3~6M`B( z52)b&5ky4gbe?|t#d8QnK`0t_qP`VOxTq}2{yV7MJ5rEc+_1q-(cCv%p0bGgBv8R+ zG!dvC7ipjpP`+Rr3@|nBU?A+q=`)e2@k0s>z=_E~{lb-&DJ>~{2jSp}5{yLGB>o3# zwBgSVizCwfRwZ~UvWuwC0F|)S1YQQJL|3YS+J&ivc-;fc0(D%nnMpup?7H@I%;#wy zsDdX1P4qof1VGgS6h*kk(}UzgZ9w7?)CX>9C!W|c6q%u_L}!3%lYvTGQJex)0TKdV z2vpJ33B7>=l4d$CTsK;Dc`7l;GMA?^h6dDB5Pc8z9U-7{Dc1sc-J$#=s82m} zsq)X`EBj#oOrWl0L=w_K9oMP9vxDx`fvJ7exa_jkBHZw&?pATu#?1$5r-i{pex)`* zUCg=Q>jvc?L4D{B4Eqg0mm-kjmBpz*wKeq!P>JD1jL8n_^i4nZn~Gjfs94VDsX}+j zJE-Mb!YaC%s4~>k!1dQ{Q1NC#?;$=Y3iX3xs7=x)nc!IS zxH||njlfj{n0QkPsvKRQ!Z6f-^$XrToyJqbfEhqV+hy3!{Ih|bqK2!6=cz=K1&ERw zdHs9|j<=FXst)c#-mofAX}HBjjd_y+4u0-O6J|^qn3+XREd!vATQ-6HtN@B_ZZZI} z(i&CmTo^?qi?ZlP#3^Z1iJ6LrqP`YY1N>kNbC#ldzO%IUiWB`u)c=>nt!B;=t^u{S znu^*wu`~Z{hUFs5zpR#SM&10&JWQi6t7EfcPpXg$x`~NW>RT^54*Un|CdNEP3M<4< zWkANfOJl^-jDyOb0TrjPiSv1gqOi0(fXe-2q?TXOLv4Re1y!knGn%`+;u*uC*i0L* z)a!UU!$Va>&O>#S{}0rE?3|5qr63H2fxQ1a@3d*=n|O?(RNKyMxVu+=0wjdPQQHcA zyFNZK|E$>TPM^b5?0XJSM_HkI%zPF$(XmqJeL!V1uQ5~_G0yE{`$0ejC1=F8r(M7N zF9!+PR~YIELH!S?D>wY;a^yZFbG;9wv{3DP3Q%`DKLq@mZCL1NXRcZ4*tY|!od7Cr z*p2rXDox-%cjYNS#o%c%ZaMnRs8c+>%1~>HI?qs*p#BHcg&Th5T+aw&Og1Yctg!DX zKn1Sz1o;vKWVfhJ)mhz}f4vQ;+AdQe7c%Mp-9pp;{q+nCkBc5xJx}C8hI*Ga`-J%) zW3Y$HXhBq!c*+|*oSm=zqica0r4(#R73Ti~>N!E3KBpfQ8REvB_Nje+1yHq0(2)W` zM|O!!9}-mSm`{kW7u8fWkY?y`Mq=2aEXa%{tH(Dv6S~ceoAe8S4Q2QLo z4WA#0$6qPM6U(_%p8!B&d);0CB}v2hciuj^pijKur{Zz3?X5_0JBZH(GASmD4mq@? zm|jz%^evFaGT&c+vM6D{Oga&`;v@T9_Fr)X)S!x$5+G?RIx82ewo@wz>irX{odGIi z;3&!HE+JiJCIRsi3^i@;@iCD0Qg+K`tRbU-L@l3WsQn;i7mFO~^7ACz=>ujHfU37B%M2B-Pl_4qw?idx z0U1il7gFy7s!7ED0fst;$LE6#Rn2cZk8jJ}!6tfxp_+DwW~S1S@(q6z_P@$dN4%Vmc=sii{}HQOgcPbuacchH4k?vgHOt zsD$p`Bqg4{52#8|{{Tbvc|XQbu|7`@g6a|fYZ&T~QN%D6EY!!-ODEJRpfY;-`_@u{ zj_ereOna+z#w?(UmHwgdoS|Cl$7zY7*5vvGQ45A@ZS@)I{DaGkzU&&lcXuyIhig9t zs7g@(07H!}|0qK>!+ItyKcd<710vf?40X&f)Se&zxo+`zdWE4Lzm>B5UZ{r6lb`aZ zOgmE@dK2HJydBLMD>Lu!b8q?W3PWud7%FDp_|*@43 zRf19mx39#N4a8CtvJ_};TWplmreLT*+Jg*L>3K^86{UW1_P@YT-Lbj5ZMVICfLcAC zZub~Uj7(_NfEoc~#LZnXTjAZIG52!sSJ%xG4Vp9yYJiev@Mj5I* zYmuP}SF6Q#&o~YKf}qxT`aYnl(K7hYNyShrM*p1*HQ+`WYI0h&MP6m7X-c9POQdU$weX=8C$ zgHUBrM-$Cbc1h5(Q&>7U!INyKA<%s;_x|Wo>f0i8wLyLGvHr_vDh8O8*y_G=5sM^cCu#zpzX?zsMO|3(@5NL6t&0yg_0&M~O1mp!)s3Z& zi|c@DHvlzAH)n=-mmezYD^{7Zr=%8yEo;I#6GOE(8LDa-!7k6XxeKLY-+rFIxK-J7 z`cRM3jep@2%4tr+D2qTd%UD2AU>s?SM&=*N7S`W_q9!#Pn(`JHE;3aes?jMymhcq4 zPP)ZVDLPS3>&47Sa99boDq-+aa*W6+-*St}%Kn$$JR1DN1T2rPo*)}>PP4rE%*-fB zS93mPiN?J{1}F{dAmC{_;ZiyF6=3o^1U5AoZhH*XG&G|ahMKb5w8T({z6J_08}go{ z*Y?|>*L=PP!scK65pYg__0iH|_$NP6$E|Xo?U;5zQGsICKe}05|1Qrgyq_`5YcR3V z3-0sm#^7xh!-T8%OAOWYJkDW7iPZ;rj&-&_B_9qj&Y0|)Bx=8|zqG&nPQ&v?U}m|$ z{>HMlDe_%}|*hRzjsEGRT+f->eBk{gD3AdG{gW(&M4G?q4nP zt-E3J9dDm49(rdy^Z@lMVmGp$b4~L;qB$ngnEt#tK$|aD^%nF;Q(r6>FB*(xpX+{V zzptEYgPz^hzT(z<*nE-k|8SIjj?2g`E@Bsk%7+QA^jRLk=4XY^a>T;YpHG)Z$mu3! zY5iNw-%#`Md`0;3kjL~faFh>F+V92~CU)ocu9iIPs;zEeR_{~ipp@mvuf1noI=5y}YABN_hWc`uhiS^I5=%npL zCq8zJ@8*g9o~hU0%V+FIFWO;r*w18!?J-pMc68yN)%~CU5&gDhsIe&$x|R03OSAo~ zC`(41%lhZ|k1{OwGY>qtBE09rzs$xbi>(|xBz#Og1t?2cO6*dkRTNT3XNt%}bfhcQ zuKX9#=HIpbDW&)$7Nie7MAcgxE!2n#Kv-hLE4ugYri3DT+dDv17)=7;Wc@p{rxE7m zT8@Bhvier-r;o4)1I1}EgB*3F9HaY{0;#mi>$^Xf5e~s&*p}Ls_G|pmSRKS^~mKgrEsL0 zilG{p68)i+wOg;e=za%K=aPSP*(X#@grRFRj%K_4!OV8&1Z99UEUtg+hUD#%4a{at zS*!+R+&I;g^+)nSUQ}v8jjzi*>pT}03V#XqoF~m{wI&a55o<+MuCG5y2r8gDwMI*R1 z!69*|`bXqCwqH+pSH+}&QmrxC6q++)hjLgk8&D1g*;z5|8X8}-QsItu-ih=#;#!7v@+AcE;1?%?#6={iU zRq?X2*Vpm%^&JnHs79ZYn(;jasEcTdcOOnf{Vz%X$U7G$r(qb1D&znEXJ#X!PAWT z44}p){kJb+sPS1m-8wwICaPR=Q?2vmSu_m)4pwPoJK;b8=+P zP|xsy;{}|&yWxN01eI^Vrto<840R2t`*7u3H05pCs9?I;yhbtHn$RnBH7Hb_DagUz zb1UwWH4#nCC5-@$xTd_*GD*KvJbhoQ&S*xDI0^|X867Am6;iK(AeeeeY4xAQtiO*? ziSG&O&iIp_iCmb<7@0x@eW^mD-S|@h&0_HfgeCDAUvnh?zRHDjB znZrtsl%Ajl>)HPJds2b4%X2MUzRjAc&G@rEOrdX2K`KKlQc)&Tb-yyzji?Qp@)XWb z4-9_bnFL=esBw7y%i}Y|4~{mVs5coZHrZvUsMgGX9!M(7JvE@Kz?AYGnXV0M{F>RN zctvM?01XvxxxHVa(Xf=(*efI1h^ek$p-v!8Qea5CIaUfXf_VkDi{ZSl2T+}AhRRlw zl&p8d=Ge90@T0|{@#nc^s9r31#y8gt6}`X-G1Q{`Wf}ZIPXBegGLvv8p0IV#P%{A) z#ac!5a7FW5Vl>~DfJ%N!9h?95cmcf~%>u+a#Zhx?PBGNw{5O5l$)4ae45noCawU4l zb34UsYYnVQPKB2Yb-T_`Nn0~ipnadAl2l*n6~tu)3!krb4AqEP8LHA7hC0kpV~C)V zy_sU@A^uHJdqENx3>6=o{lZM=87eE!6!q<_P|0C2e`L*7w=_f5?Xh#crz3?PP@p1~tlXbGy!$cN&J3n(_0`(!?m;|w*`SuxZ&f6>~}%M2BQ zy3bHe4tVkl*6t+BkV>JQ4ZkOvM_Hh$At8wnU8Mu}l%SFx2@VYXWpO%uMs#B&?x1 zwBdI*4bv}oK9p*tm#f_tF;u(Ow%uo_X`eg|6`1x674stI_+F!ozT_|x`dDCgGCn5b z7bUyOPzh&sQyH=yWvF>*V;V;^61!)pz-cC?rYnNVaWA?yul9LE@Cy0Vi%wF_YB>{D z7ei&o|D6mKf=agti@nZC%bPI&3j#4By7_0;)1lc+u<=w|iQp2p8U*OVLktzy%GnUx zMTWXf55$SJ%kLQ~_O;1uHE;1Lr4bgUmS{H3pA^h_g`pDmBs;}WrzxgWP~Ql49RX+^ zdCQ+OvGCJ1K^+`VE#BYeAO9WMUjjP+2s6}lO#b(TOAK|I@>?0|?7h0>Up-}yV_U_D zybD(&qS+zR<~~6!R_cFTt3iRPG6R_(WT-hmE;3XebVh|+9AK!t-9?0d*vaOuPhLG8 z!x`qyOU~C_PJ~O|W~jPOF;upR^))|$SWuX2&!yFhlP1%sc@`k_Xc^FCTNt9PFt7s^L z?Yor+7%KkAS6U)&GE^Lydd@IZOk*lfz|@|*#?x7?UVKKv4v`-EetAJ1(B{N!3z-_B5}gIM4>43=3F%N0Foo&| zG1OL4&ip4Hpc)ju`5tO$aVn~L$ufNnA&Ji}v!P=IwPQc)-{mLK?21D12L+_$Vim+L zIP^7!ntpYap+*G;V`6VO%GI=}WtH>|9Q7bWopPU{7UETFVK~4}J~NtP0`&SiLyi^u z1l4N*-6=3h6T1wRP~ComdO<-_UJPZ1ItoA2`c@h8i~*sz%gb#vn+%I)#J}W(a-_ zMcrqpJgpe2IhxT4&IuqIn*ss_pn|bvteu3;+R>`EJ#Wzpp3?mC8n^WM(pK0r+GYg- z<=8@)O-=H*(5d4CQv1V3UJSLfC=2S=K(%-;HI!PBlka1JO6}$K5=tXeJlXz<7HA^b zPtA&Gx>jGkj6rA_Dq&NRQw)`6>PXm}wA1#@2#mT#GQTRV;ZgFkm;KPaDN;ucPghGcUj>Pu*OSMigX`g1?v-DoomvO~? znXs&#)OFCmwREPaGMc32__YQxxLPjx;JOk|WlKMdf{i`tt0%as#*36uy0@l6Z4E@1 zmxlzp>wRS2SDKe)&8k@Jrrt{&_5k{FM(*R;zuL?X_RenEZ4d^+pQrEt&b`RAJDROP zAQC%mm7n7P0cjCyLGW>3vpL!%GW}L95~@{iX{visQs$8)IWvr&*Z&!zGJq6E125ya zPZ*VytpS$oV3$!H39Q;VA*E|o zB|0^oaI;HeKhIZO<(S4|N!Q)VvEh1$MM#BZb^bCM&5WcM7;qQ5f;dmoRWVy;VnCM| zqd}+~4Io)(RYs-pzNc&fbI`njn*LbluQFGCM;j$kQ`Wli_TF$rGBPrEWb9=i4yiNg=C?c} zoI!rzsR=OAbyyqjzTr+kgeql0(lDY3Nn!>qM)efIj3rWe+T2qJD=GGlS%I1@Fd3o2 zzf0x=(?77d7SW1PCy_mpHD?4LV+_WAe@jt`QR@+^@9Gsaw@9LTenKe_4J%Edxrc8k z18_F1v0@)o^h$mYvse7cBtiwS#G;T6!OHh`ntjB-GkKTn`3gEH3PsE-@hM}9CgCCU z_#=Jhe0q8&43aW=OUK9keu=AwZE7PuZ?UX(hN}8G{xRGd@X-dFlM$*x-?fz8DcOHE z6>6UmRvEQZp_d~*07`t?H=3G}6|0qRm;9&O5iU?_TUHItik{``KI-Al?Z=SLrZ62XU+)t6hdOZ&h<~B*^8&)yON_|}S)PwsVKLGcd z{0H@+BmTPnjAe34{`j2#>%o1=_17|LsnBid(K2Z5Rr>R(H# z^Pl&)olyVik;Kf$0c%ipaSFj=Nw*at263#*tPYYY8Dku2-06%y$am47|HX6#>j z$&3Kz$Cm{`J@5V!6QbLYpm7tSUO`27_Gs6$Mt)|n+kf4_sFZsPz_JR5F6_!={jgWh zqMrlxQqq*O3zPW55xWX1<)dT20doV$#sJ&EhUOw_zhYp&=4VBzFm?f+OQ<=Y0zl=^ zBKs|gHQpy zsrjJ^EjdG|q?U4=Q0JS5+5_B^yOGnKW8VRM0HeZ78VYiHy$UGvG|=p<=gj-Z*3l-^ z9yL?-=5Q=u0RQKmOMGf6!%VB~Frgmi(~Ge$0#;SI{ox`=CveL9?ex~$YJhtGtbf}8 zDR{L1Q#Q`x=)SQ}*~K~}R3v?6j}vNUgsub+cUXL7=<=I{xU}b(-wAquJ;Vx;T-~dD z%cTn2PepS+ZQzy{Vo)ArM^yxWTIVL8y0U#vCDn%+;=oSr=Y>>N8W1%4A4Yk2;!5>t z@kcPKYbR*q`0olf*ych}aeOEsCPmA3PI2y8Itq8E;R?N#z!iNrF3E53N~Pn( zFo44if~iEQ6=A`PB9!&|8SrTnW;V!TS7gnuACMQH_W#ANca^T6qgdwCFJn}{;ouof z(SEYNhv6tjwb8M>!M9Mmh8mRdO`fBIBGp8T!*hwF0cx2x0_7v1Vxr3RZOMzEtTFqm zQ-UDf3lzc<%;o+Ugs%CtbiK{`FspcL+W*R`^b8l>@buA)N|J;<+MZvMxYSpN6t9~GN zr{)jBx6TeU8C4cNoKi=O8kwal8}!WP0;)Tenw`uUFYI9lB|dE+)|>DbOHnHvM~x$M z;piMtl2J0tWKo)EdEc>RL7h!8l-qpTIA`nav_y~DB%22x7MrSC=C!k=Tm|+GLz5}X3BjVrBdV6{#s<)0@j*g>h4rl88*n3cjLQDC0a0fvc2Erfj|I+?!X2Troi6!1AF)|XFTX#DOaqn7z>Qo|!s%MmpK`>%V zA6J@(wb#uK^@47YrkHq(O*{Lta8Nw7nk!w+d5-8f8+u)+i#s0+U1}v70>J&9KvHjM8(gimJZe^(QNsF9^fD0{x%i*}M-I z-Maok(qQhaP#AYDCW+D}o1a6_BQ%I^Q%7K06 zhjRM8S%pb^{v3`(*tv9e&(@~+@RHK_gcF2InY~bW+le*A$Qw5o`fIF`$m}hn#?Y&; z-L(PVdada%M)QGcPbN$IuPojTY*){J$oNW6WKxScW(M3P_TMB;`X3FDyksEJpyuOHy!)xzKLUYdUr>^vnk#Q zV%)~jK!p6rF*|>qW&ut1mHTMT*;^>_fGP&Tg)gUCkky<{`T@rhSG?yag8akM*SHF! z8Qwg)N{0sY=Yur+J7$o1LkfDtm{lHu6WvediNmq%*-eRs^=E^gR{Y;(I!C7+O#^H{ zJ-E5Ii5}XU>4d4P-pSpjfxN5-&&LVAh~4SD@C|Iva$5-f(VK}~{vOTgivLej^5=*K zdLGa4F@ru*peU9mlUc&kajV?YS=z=L#Ftk}{)ry+Mt-jFexJuYcWiHBZ~`Tpcc!K4 zT@;i0ijzX}BKpZs-J%wpQ#VD6FmV5iC7fYNSr$3bU`(H&jqP;v7s{Bvr|l{TbtQv^ zoX~?5Vi7HshQ{sE!Z0L3u!Zlkd7iRvqP%jp(%;~i$R~*|t;o}iH|CkrqUB1{f;_67 zP0xHC@G{liy~F586)N3*n7jLeuz)SUx4ts*)}y|)Iv<6L(t^ZAu+B$Ir}jVoh5)Mn z{`j+h`3e#uXTG!Vq|Bk&n4TYZ@Zr0hIgr>12RdK+Ls|BRD#*P}`D#~$!-=MEfIu@C zzsrHNwVNIALQg8M;I{N;Lh5)WpRGe!LpQN1)!to?StmZUrLy2tIV~c{_rSNc>11DC z$yt^!pU_1^m!!JRB3QWdE0*wA;vM0T%kDfQp33};Ujza~u0f-2zc|m(fod4V^lK*O zNcC<_aJbmp%(^0ZU34JbfD8S$BGLYzAwXkG+6X44@C(ZL0(Co?KmJtD5do$ z!ak|}saO1lmq_WyVR-h#fQD&IODciWr=rLcxc7F5e_qgD2+qobG0lMj(NCo_uaGc} zq=183A<*YhR=5q{qgeHRm~2E~-`F_E#;zl%i)3ksH^BoT`%0niAW&}`2vardcRI;K zb##LVv*vTli&#{atd`OCTT~m}RP+|@I!Str5ZzWWCgz%HU=HBTSyBOZ$KmO295Wtg zbm3^*gsDQpyUX^}eAtu;hnj8U_224l0O0k{Bbw;-5RVs(cP>gl3?;Nd)EHiB#Vfx3 zH_rK`l#nk?9}1p*OrVq3bMg1YbW%+lY$=~S;LS)^3aaVZvPj|KDB&mrA*#eMVT7*b zk9HyV#bVjN3iJd6H#_Jk(^wnqDbvoU`cBumlhoh&y==>_^ypB>^y}A9(6v>OJ|HVQ z-{gSB8bjBge^Z#i-={c4qu01u4@8&Q}vu{)`wR@Z_Y>_+ELj~_DQ79 zhm7jr>E<^#Wq%L>o=)gvbp-=0zLQ$N%8XZTcD-^>h7ai8o{-^k?0e-&(&5`XvgsqR ze52ec(_&Y$+6#E28=mk$W!Ng5@SXnh52js)@+s?L2K-aPIln9K6Ds6&X$v!QujM(M z@);mcoKHK>6r8}75-IJ37DXI8sePjhv5^E~&@;N(L5HHD z8y_*OirQqq%W5Z++?S`jrnBlsV9%U-EC7JlX0DoB+-B$N_&kJA>8Kf0@>g?-BL7mZ z=ks;mh39_J1j^JrSsj`lQJYxceJlu*WPALXRs~zR4d@z6c{U>zJRglaSB}gd^VoZ>6r5p38Bw<=c-S zj)`G8%2w1RBUrl5czip+c=_GEk7qTtavQVpoREDX_k3mJPBcr%D|vqRi^Csvw6BZ# zx-#bMgbe!T0KyWE$*-1_cUNd3>p00vS0PSIj%q;VM?elWI-*3iQ8>69>ZZY!`zpb1&bvi<<5CbiA{n40HDzuLn)$1?C5*bF#(%j zPm~D=aRP$qf^?1ENc)x`Z(inKDm`s{!>Qyhhb;E+?l5ZIPW~c)IG6+&=tSP-bG{MQ z5n@s0oa!xQEdzn2Jnt!>cGBN4VE2Dl>?>iCZ>DHsa-C%(7}B!(dW!?D)(# zZc4_g)6J86eu;VI_StVez>C2&lHNfKe6_mWBCO zye0q(Cy0tW$wFvn4Bv@1%pq7pg`Kz@D`MN)aS`*ReZg(j6zN4)Lx1CQBY+ zEtI5Lhcase@EX*6PP~1OwpaOIaJ2r|`{uWeL@qoxDALHog}+XYaHwg0^EV*W5AR4U zL{8VYWHWQIF9-W@<2l&gTw2IfX^X~I72vjE?zPK~_DBY+1657Hr`Wr0-f<-R6k<~Mz+ZWY-Kadmf!ZUvgCR*olA*4>5PH!Q(Fi(Qzy2Kv z2%O_O6q^>e7avk}URJ3UQsB{w#0CkLAcN}VcrG+o;{X88J3yV@Q>Qo;jQeZh*r*2R zu+}+ZA*B$mzAs5Wi{>_n)tFhZEiFVFlh^6{8qbhabScIUhy`A{3ZSEPg4m)Sb&{c8 z=A_9pk>5PK@tuJP+)Hq*qj@~E?iYTaC>!f*2r%n%-C7!a$w^9**qQ2{sE^k|1m)9GDTXi}liin+a&)*|P zedln&mffAnIfF@9NWgB;u8W-z|&&q0sNAn2Y(D(9)r`hX}1Q zv56jjlc_3Kjo7 zt{T(vy}w-8*j67Ru?byTs7|^j!>DMvXY3rOp3_mJ;?!N~; z)suP`@&DKg6!zy*cQ-|Y2@dJH)SX36vfaf3%TXisxbF#z?9(bd((e>CJe;uu;tXL{ z$Sq9BJ5DEQ7!fXY91DDIYRT(A0tCj?v97ENi-hT;O$BjFns< zp8|TF-0cKQ7){pnG8ZNQD+Z>vt;8h$Hdr!>r1_~;1v9Ue$u02uyG95AKwFN^7xJc| z#|!jqL%6enlI4QBmL#R}0Ms56W8f+0Olhc}dIPtT79Y(r*5T^>@0&=UNLm~S&vard z8s?t!qoG*uUg{7n zcPz+rQ{5h2ME)*{DF*);ENTBxX5&JhWoBc1kV&$_%I0@KR`D!f~1Ca~qqJa%Mvi>xI7Cd7ec+iVO1ww_dC;iD_8-xBIGn}?;a}&%p zK97}5eqb1>@q)x~!JMxRQ`aMZaBOhxd&+e;T*_@V1uw9N7jy!f9Us@uu<5H4jc}$L ztT0|(YSJSDtLkTk2)&BuO%sdPW90=Y8;4fRuYm*{Czf9=FU=oDm&c_;rj6(21M*1NZlCPa}IFPOX0R`+y2q_9E17b*NTEYa^|%FKy}S0F-})L=OE7nTB{@)wF?%D%>vcyHVpo4%jFxJCjtg8I=Fr!%zpsxZ-)ZQ41zV$O%`$4lvY3{;J`% zN?xYA!wKTuvJt!PQ}M;ADzFPBS#w1pmCez4x=1sh@i};pxzrRN+P;K8uF@6-PO&i% zNE7}`t~(ss62U-YmSvFis46 zR!X!*{7WQU_XWS8=>|KI1;khjad}r8J`yk-_Dd5k2bgVCnreT7nll$3e#W?LOE;Vs z%_$t=@8bgBAsr=V2@0tEC0yXgwNrZsc&k*Lu|z{~l|(KJ;m)S&IxMeh)0_B2YiPsC zQfBI9FiZ{x+cQD;DS96R5XDwd`D72u#-CHK7jd-;u|u++-M+qhDU~IEa2*p2#6BtK zu2V?c@f8*STmQ^wE89 zI8MUiC2u9xcOR@qe`=?a_gF31S45EYe^xJ}Lk6A-Y@^V2yAq9(){=6jL8oW{%*M{6 zJTLZcrgSX6^y<|R^JetLQ7xmQ6Gyj*mo45&O)+706)M>XqsLdzYt_=NsMQ2N+Y0!n zNBOBFv6<}#o#8Wo8Vo@*KR!Q3c%lE$6v)hyaZhl<>ra(O6)dL|h}>L6U7djeqlU%6 zTakeZQ!5#`fy)8T@|e3_FWH@Lg^%A?7l_Tgy0-83N*uDdkV#lcP})~0%5n7YT19+J zyBscT!yf*5g{~IEgbno@(Xt#P|2K~YEbZ>rbD+Csg$?Wd?)s+Q_x^HkK~yJCoaZP` z2y**q?ua_f&9H3U89jOW@RW`EM!exE{oa!?wpD7;oT~^{^XGV!=6ScA)BpxVyCxCL zut!O~?{)VoXkEreN3}HY=}uDIGuQkfDv+&EVPZk1NzuIMGg`!~Gd={TBdW4-L1*yI zhXKDrx1QWamLMI`SwZu5dd+f9Q&ABI(}*H%w0)Mf3>=^r>syK^&nHa)K#-Fb8iVTb zBYTf-`tPu9=n*{c6%zBH)s29FB28>Orzj&nu<9LtrX+QC?eCCeFmZGH5}p731b1-ETpPq_Go*RzBm}uys4zZn&Kwso}`paZ<;r) zxgJZyo9{OQS#u5aD?iq+{99ev;|c>Epd{jikPwIAIU4_lkWYQ;2j^JZDkR4^H^+vR z+i$gmkxPj_RHUNWY7m-$L{hlGEvNowAUJm&z;^sn!PydZh%%xa3Fo5$F&_hvqy9>A zTY_fMM^cnb6wusXkJqKGR^@h|vu~ND#hgGAcrYBl4a&sx``Xc_j-tV|0Zeg^KuL_HT~(O;MZhQj>ky5Fa7Y72?-0` z3XVydHMNj&vZUq8(87bgpL$Kb|5eWA{z}NxXbZY8Jy)?t&0GJhvAVN#{5;G)s;CQf zzxL39{`=bY#dAMQ$X!mE2(au#(*T~iKgikB5WN2~>X-q5uz0J}R0r*x1H9Yf1it}w zV>#Yw8NY)&H!;<6R%?Wm*Qq4SLD2T&U^dl3rP$@o4{R&9GpWIJL-J!JmUu2y4&Qi{ z@9{&kEY^PwQGr!V0w--r0!2q((z#G;J1KA{7Kk-Ld5u_B0=1C3pYj*bR?7$Z z`RUWy<@P-^2>)8Gb;^pNtVPt7=ROy4x%Ur=zwedggq^{Vs+IM)^qV|*48@pUCpiWcuq@Cyg`vy$)EBbz-DV&lR0 z5?E06XAv`dh}hu$x;Akj(_@^L;4uphL=F_zZ_h`9Fpp+mj>k77aFX$&ymyQ8q_kC5 z+#7BBb!If2WI*eoFyMGneNc)F8jWdf8Tg=`%Pm)&8zSH8xT`U(f4bv{I?`aHWZ1W57@B-iLxC+%d9kw#F8 z)x{LLKkXu?MAiZ1ac=wbX@FjJKanp=Kru%bhT}n@`DjN58()%eHs?=dUtqY4Ww`be&a=bD@OYp&l+paI96FWEm}fQ@Sk7WKbF zd(J)qRKqc!ltG1y?112w=aWOU6fpBYCM~{4<<~6uvOGBrw9x(fpR#j2>&@6|t&P0D zKYvY~893kgah4fMhG^~l5Eus2=`$tm`KW##&*D{K1=!<5j0*^V z^WK(EkL`J?JmWbUpgZo3zH>^>-%;56$Gr*RFy$q7gc7qsaCvF{f*V>R)e`(Oy+nGT z6pT^K#3YzS^C!JT00Rc>1;e>&I%XSMrWzmknPgKfvMv$YBol*y4s$tp4adTOjludZ z2pzLY4)-o%MGZMYzHBhYiht8G%wd9B)hjqyF+tHIoJaGPCd?8rqMt)GWBFGyi@Iao zIFs4x5+5~RmFVeA$3{Fbe{9+P!NF7gF0JBeu2RWkDg7M+2Xeg#_NlkZm&U`Ci_ z$i3Vf%8$*#%C8{Y&k@F_E1@S}#!!3R`g}j{1bI)4PYEOSZhgjaofwqLp6(f2l)J6U zA6+_MZjRVfzyKqQS?Gw)fPY!YTst$O%?8&1wU{}-3? zR}>W=0|Ik)sb^r55Uv5XE@A!J56BBvR+vtm?AFzJo$1gPZ;2h|#r#h0F%AvlmlUk} zQY97Lz?2N~`}s5n6%f$oK1aQA1|O!bUi;Mb_ES74m^ud9qf6-z%5(Yzb|u;O>SVaG zO_+2MfSDpaKWkLzzMuXicOvfse?^S}o4GU^>HqgMAjY!;M@90ldQHCpMPR;W! zugH)djXhWEDG^iLiTaFzuPTAweT~1Uf7_-zRDz1YG~nikLB|No->QZV?3+utkHJp% z1031Po|ZRUU81KuBROhOqxgN1{Y)Dl2hYd?o+a2~HWCA=ubT^2Iq>Vh7n+2cye;fF zjfZ0Zghjkx8rxw_$|~vpoQ-shck&vl5rX~6bziXEDkqM|7wXo(A{7%?b7bdV3%3_~ zcr3o`dT4^SCTS)76e6bvgrlocFJ9Pk9G1tPSPi1&>{>Hd~$be-7m5Xssy zKnv8vP3(1?Zemluz0v1r?)9gWwOzD~etmy&6LSXNF?yRr?@^fS|NRUNOyl32^49o> zqUfd?uW#}1&Pr#xiD_rIlRFAS;U`o6Bl~V zQ|Q2>Xs9v~#RbbJfb5no>d6`pX@VbPId_vg&bQP6s{_vG$i$CgcPQIb7~tZ8UxtB*u}t53$njKREr zj^&|MRPTEu5{N%HU}rt(k2U+_lTYY;>Vpja6j9^+>%zrAp;jt}uHxb+c>UVkOYjU? zxoMl=R{5D7ihk27dwYzjLnTx}#;L98nZ(mu2_5acnU+RYaI;5hBXfa=XR2I1&$jh; z-YCi1`*Y*q;^~*?SGf8v=p>D zcquWFrMtMSiAM4q8PvK)*Z+4Fn5Ul4=}-_rKn_|6|ub5Q}5VG zvh%3Oy$r`^cQ-WoZu=bTU2hng6NA&XRYuM9_I!&^XqvZpwdwmfR3p9ar?lOcLN#)y zP3u#7mp||onWQ`9J5Py}O54Da%l_Wfd*#u5#(=8zAnDVH>lK}R2|Qyy>89U78|@1C z2K4n>s!Czz#EyB7;@>p#=n915WeJBNc)Wd0w6fO!-pp%9;L=ME$RWse3s(jn>a!&5g>&YYrr z6bUOUY9=X9eK2jsRE)bY!W+wQ}Al+@G4Q%?R1D*cG3YWnkhH{O!BKD|K{cRoXI zDX4gpHRLEScwT(-*f*fJto%y5I?{xiwS1gzjuh%UjOZrR-qUC!MpFr^>?X+QxSP0? zS4=}}9pn|RQ1a6p;GLeO-45EQZ27DE#xpKYqW6JGUlSH?5 z32mW$`_hsVKGRFy7{+rv0(K<4!!%SZYO8q~7zbgY1JSa_&q&+ZIJiTeG5pnzW zn0-dn2pu1L`#;JpW5%D#i&3@-S$7I%|Mxgq5##m0*rtE1O$+WRw=X zF8an_-MO`l3YJDz(-@mN(dKAZ&VO)3_06ntm1H@LzrOsG7tnx(at%`LScV*))_cmD zb?YcmF!2n$Lgj7lF#o#lzV0nM{`>QyLF)>DO-K!2S?FA*%^esrw1QkQo;O+_o7^4l1Z|itHI_Cf%U443mTOH% zW{y|3jGn@gLg(UU@35E%eYnoYFL4$R*_kYfY4mCzck^)vRhX4~-rosn<5b_diz9%}Xt1l+Bm zxDkWAdGv)S>!2vOs8Pcfuz0V~fIvYMIw%Vt4K)7YO|pz86?s@uzm*ubB)SjxAeL~;xc;1~6u;4eCTm~# z-pcA@96FkC4L|hR&S|Ne#W(+wUrc%8-kAAx{O4u$#HeKnzSwv`V27MEaJ%<`d9&3I zKT4HqE1#)XSHpsDzPfnzg_*#-8z;i8`fRdS_d^qWWy{$UId7X^a^g8CFWlVau(|X2K=zw~$(?WK1`OM5B?w}?w;bLMW_AhDr8uz~Py{wk2_Nw;PYab{0h=}t1B*Pm5gV*f2@jZc`A^)umNjH>+!Ol1B`nO zJolDH)E+U)j0ae2`kv29%!dZ8wg$9E-`0mHT)n(^s4Svuc8~$nrVp-m1-ifG`#WhH zc&K)8r1Gxb{Arl+;LdbCTL@p6yQ<+ap(Oc;apf!D5#(eyj-so~>M zg<(3<)xjAN&C0H;`G-^R-FD-n6;_8t!Zyi1$> z3~s+amLz}>Jl&-RdJ|NnHyvZ+)6?sQ|jR5y*vl+V+_Aeo=0iq1gDl+{;ROCqD?m3|e`_ zm1maPuCV)jQ@@$iY2~8{3sQf1oG9*D)M40RBRq(?IU zR|uneKe8NPZAXvf<4Xv{%LhP6T9wb3WdOkJLsDHVv`Ar6@!jO-a29Ib_c<>S8qsfc zk9~sP1QJaj-f=bBpRnE{qo{McM}xv%l%BBQGHAsg7fP^S7~%h8q~;Y<(&1uWNcFFe zV+ClHrVUKFcrr;+*hDjf%?s4GcW)kyW_1D^@{sCa*2kdZ(%z9Jh6kshpkww@i3Ra- zz!>1pl5;9+GoJRgRgZce9#X9UuWzJ|ilN=Qw%!%p<}%lGbl|=UTKdX&U-CbRvQD78 zGvMmEO@TZaO)k|4zp#Hds!;l#ACPAe0@9^Wbk$~g}uYnmr~yl@N(+H zexts?JPeUsD#QW59$WUQI}qButT9~wRq+4j;6WgKQ88`Nhwu5jkYOTYb?J2d0sXqOL(p!#-Ay1N&)`)5$83`&54xDIM*!i%*}xCi4!ajDA{Nn%J>xD2GLi2G9s-TEs(GWUlP?^8n(

dy`y2Z9#}ApC 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

  • q-0 z+H-^Te5}0}%D?WTDgg$+{`hUWqCagLbr31iLS~?0r=PsfY_%6kpjoG@lP@18=w|S= zJHL-^{H*iT_72ulFv|7#ABVq)?olLNwuR@w*KO3ZZMkdTKD=WIzTV!XsX@^{vcCB= z7rwXP@B29u{L=fwFv!c)f8gctK2uM1zW>_vo^w@aobrZjK(|q~gWhredUWF}Gfjpa z+-k;We1B*!`~zAx>SJBU1;I}y9RlncHIwAo_;(ps^%onH+~WfGbtR>w#Et*AHL9+T zKMt(7ySVP%sCp}+i(iS9eHR;MT2roE|1W`di5eZAbV6~3tH0U!#!#(*^i3P0P*ikN zFZ|_&)~Hb{Jhb-&6NcqX7=(Z2?1}~8aLb14s^o)j(dBY`4bJ_hZ3v<#{u^=QCsG}H zMgPYp_xQU})v|6Pm-ctF@>9!FzrXX$9_p$OVz=tu83o5WEqpw^;ZydR@#`3gL3o_dRa z+Qv_$67xFWeZA;y)cV8{o+@?C1Z^+olQ=3&#+B-aftVAVwVi9)zRW#HnKs;@ZnG2%s<}M2#WD_;NSQa{y*_= zX4?9Df*MxrH9$#HmoQBOP*+0e*ZdpbXQuw>`cLsk*2JG=H1!Yv;Fsw5^56N)As}{u z9Mf_i#d84YqOrehC9_{P>jga%AwrMsvBmULR!h z;L=F@C?*~K#6J=?M)9Y1J?1aAubcH|KTK~bHKZ%D6zK%!lK=q~YZ@aoT=(6?q zB4O$OjL-dB|10>3f8KIP0{m?J*53_v2y)_2Og{3Gf*$k8F0k$&H)_Fg6iG)w77@*w za=i&x#VCbM$5f0TB^!pbF1X7FbrqM1FFXPK)Z^JWpm{0bgn3PTNCe-D1@pHlv7h;? z;O{1DW`n?A2oJuWr%?iz6nrOF%+~ySVjTSI)_?FfzQF$}eu3}NDZ*~&W99l4ACRi9 zvVu>qw*K(n_~yU$m&?}?|2umMvk;jP|L7n5-eRo8|JbM(m~gm>NL-Bppe>A;Ld{}H z7P}8;wR_jS1+$40H}cNrhy{mEajp#Zir#y8{f7Y(>w;v5lj_a(ItL{12Q z((+HDj__tAo<5TzJaPVuCsK)Q? z8vZwW^WXYU{P4fN>#rL8)%r65Z2iq&HSy`Hl=yobe~L_1C0N;iSAX9Q)%ZUz0f!{c zx7ftNL`$GBkBvrbA$qv6ilUlOk_5poVo5cILTFqr1P>@APvc))?um8a%eh>2TlmHo zqBZ|3T{Qn{y3HSap?LTQpBtM$MD;uV&&~IUg768?| zK6I__njSl#1I{{v5XE#+D5^v6ANPKT%X8~t0|$J8U&eQTO#Etm(hK~-htK5SsQQ(^*R(P| z{4-uk;qPo?3xf|h-h+Siuka6k^Oq)m_`mBP{?2`bpsauRZ+vHm#Gg(v^%Do#Hb9o} zE*K5wY06Qu0-gdm?Gbp2E*xhTgJUxonQGA76Jv7 zJwPZp9{%*%S;6=`k$%B{>VIwh;lJ?BpS$1nKjEJmvL^nu@mpQT|CD$d8~(^jbtZ@u z1zCQy54Vtm+n-X2pyKUJ>Wo1+;WZL~Pxat;k>=8sOTpK`Crs#dZl#WHe26LdL&pV|Co_r8$%G$2V|H)$mzX!q;m)6B0Y`>i)MxzQ4O(T>Szu!h=Y;Xa5>6f)LG0mBXk=xhq`ALF z_2SwKz9zrRn$MI*`{rX1;1yr6n;%%;?wp+YKJb%iCV?s2cli3j|L~EZRE{10EGPe6 z;s4Tqy?y_QpZ-(j9-js}_`fkO<2oJ5>};Vm{%1>4L_dZ=eg=^vY_zi3+y4Yj+v1*~ zPL&#bU+~rZp^WZ=?@PW0|7*{ujN>VO?fAt1Hs1@rC;#lf)&93`FxUR;75}66-yh{O zqcR8*FYX(9ljvR+9jA8O;v+oHhfKTWejvLCj6(6%Lwtun9Xjx};A_E;-d+Cl@oVLO zsn&cw?EixQw*TAkGWt-%)*yN-Q(-i z{_BSSr}($(W+O2?yhb; literal 635446 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®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 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index dc8eb94eeb3f..12663c3f238a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2001,4 +2001,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 0ccbb4f2406893c6e9f266dc1e7470dcd72885d2 -COCOAPODS: 1.13.0 +COCOAPODS: 1.11.3 diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index 48dfe10a2a0e..16c4ddca4800 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -3,6 +3,7 @@ import type {ImageStyle, StyleProp} from 'react-native'; import {Image, ScrollView, StyleSheet, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; +import WorkspaceProfileLight from '@assets/images/workspace-profile-light.png'; import WorkspaceProfile from '@assets/images/workspace-profile.png'; import Avatar from '@components/Avatar'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; @@ -14,6 +15,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Section from '@components/Section'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useThemePreference from '@hooks/useThemePreference'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; @@ -42,6 +44,8 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfi const styles = useThemeStyles(); const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); + const themePreference = useThemePreference(); + const isDarkTheme = themePreference === CONST.THEME.DARK; const outputCurrency = policy?.outputCurrency ?? ''; const currencySymbol = currencyList?.[outputCurrency]?.symbol ?? ''; @@ -55,7 +59,7 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfi const policyName = policy?.name ?? ''; const policyDescription = policy?.description ?? ''; const readOnly = !PolicyUtils.isPolicyAdmin(policy); - const imageStyle: StyleProp = isSmallScreenWidth ? [styles.mhv12, styles.mhn5] : [styles.mhv8, styles.mhn8]; + const imageStyle: StyleProp = isSmallScreenWidth ? [styles.mhv12, styles.mhn5, styles.mbn5] : [styles.mhv8, styles.mhn8, styles.mbn5]; return ( Policy.updateWorkspaceAvatar(policy?.id ?? '', file as File)} onImageRemoved={() => Policy.deleteWorkspaceAvatar(policy?.id ?? '')} diff --git a/src/styles/index.ts b/src/styles/index.ts index 32392d62e5c4..f77fc008b6dc 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4630,8 +4630,7 @@ const styles = (theme: ThemeColors) => }, workspaceTitleStyle: { - fontFamily: FontUtils.fontFamily.platform.EXP_NEUE_BOLD, - fontWeight: '500', + ...headlineFont, fontSize: variables.workspaceProfileName, }, } satisfies Styles); diff --git a/src/styles/utils/spacing.ts b/src/styles/utils/spacing.ts index 0249b3c6dfc0..b807ee2bdf24 100644 --- a/src/styles/utils/spacing.ts +++ b/src/styles/utils/spacing.ts @@ -315,6 +315,10 @@ export default { marginBottom: -4, }, + mbn5: { + marginBottom: -20, + }, + p0: { padding: 0, paddingHorizontal: 0, From 9459a092bc6cd791deb10e2dda463020f3b71edf Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 27 Feb 2024 15:36:31 +0200 Subject: [PATCH 140/213] Lowercase report action since starts with verb --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 4d7041d4a791..17e694ca0f57 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -632,7 +632,7 @@ export default { waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `started settling up, payment is held until ${submitterDisplayName} adds a bank account`, adminCanceledRequest: ({manager, amount}: AdminCanceledRequestParams) => `${manager} cancelled the ${amount} payment.`, canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) => - `Canceled the ${amount} payment, because ${submitterDisplayName} did not enable their Expensify Wallet within 30 days`, + `canceled the ${amount} payment, because ${submitterDisplayName} did not enable their Expensify Wallet within 30 days`, settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) => `${submitterDisplayName} added a bank account. The ${amount} payment has been made.`, paidElsewhereWithAmount: ({payer, amount}: PaidElsewhereWithAmountParams) => `${payer ? `${payer} ` : ''}paid ${amount} elsewhere`, diff --git a/src/languages/es.ts b/src/languages/es.ts index c9ff087d0de7..f82d74d877b2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -625,7 +625,7 @@ export default { waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `inicio el pago, pero no se procesará hasta que ${submitterDisplayName} añada una cuenta bancaria`, adminCanceledRequest: ({manager, amount}: AdminCanceledRequestParams) => `${manager} canceló el pago de ${amount}.`, canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) => - `Canceló el pago ${amount}, porque ${submitterDisplayName} no habilitó su billetera Expensify en un plazo de 30 días.`, + `canceló el pago ${amount}, porque ${submitterDisplayName} no habilitó su billetera Expensify en un plazo de 30 días.`, settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) => `${submitterDisplayName} añadió una cuenta bancaria. El pago de ${amount} se ha realizado.`, paidElsewhereWithAmount: ({payer, amount}: PaidElsewhereWithAmountParams) => `${payer ? `${payer} ` : ''}pagó ${amount} de otra forma`, From ad6eff980b311b26444bcdd102fcc72e6aa9e6a4 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 27 Feb 2024 14:51:34 +0100 Subject: [PATCH 141/213] fix: linter --- src/components/MoneyRequestConfirmationList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 21fdda1c58d2..773e98b6462e 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -274,7 +274,7 @@ function MoneyRequestConfirmationList({ const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); const defaultTaxKey = taxRates?.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; + const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) ?? ''; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const taxRateTitle = transaction?.taxRate?.text || defaultTaxName; From e0ebab6fe3f2f9f47ade4763dc89b341a70c31fd Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 27 Feb 2024 15:52:45 +0200 Subject: [PATCH 142/213] rollback pod --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 12663c3f238a..dc8eb94eeb3f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2001,4 +2001,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 0ccbb4f2406893c6e9f266dc1e7470dcd72885d2 -COCOAPODS: 1.11.3 +COCOAPODS: 1.13.0 From 63e2cf4ce94f50ebf86e7d51d627081330d845ce Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 27 Feb 2024 16:00:05 +0200 Subject: [PATCH 143/213] Remove border radius from image, fix border color around pencil icon --- src/pages/workspace/WorkspaceProfilePage.tsx | 2 +- src/styles/index.ts | 2 +- src/styles/variables.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index 16c4ddca4800..cb4519ad5f06 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -80,7 +80,7 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfi title="" > diff --git a/src/styles/index.ts b/src/styles/index.ts index f77fc008b6dc..028bb88d6a03 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3068,7 +3068,7 @@ const styles = (theme: ThemeColors) => smallEditIcon: { alignItems: 'center', backgroundColor: theme.buttonDefaultBG, - borderColor: theme.appBG, + borderColor: theme.buttonDefaultBG, borderRadius: 20, borderWidth: 3, color: theme.textReversed, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index f7c9bd055041..c88d8f0df784 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -216,7 +216,7 @@ export default { updateAnimationH: 240, updateTextViewContainerWidth: 310, updateViewHeaderHeight: 70, - workspaceProfileName: 20, + workspaceProfileName: 22, mushroomTopHatWidth: 138, mushroomTopHatHeight: 128, From 38e88bb1a51d06b0f2aafab32fef5dfe3ecedf95 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 27 Feb 2024 17:25:14 +0200 Subject: [PATCH 144/213] Change border --- src/styles/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 028bb88d6a03..fa67f28b294b 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3068,7 +3068,7 @@ const styles = (theme: ThemeColors) => smallEditIcon: { alignItems: 'center', backgroundColor: theme.buttonDefaultBG, - borderColor: theme.buttonDefaultBG, + borderColor: theme.cardBG, borderRadius: 20, borderWidth: 3, color: theme.textReversed, From ebe03bf961f6a095917b45f064a5dd5c690009f5 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Tue, 27 Feb 2024 19:56:29 +0100 Subject: [PATCH 145/213] Fix bug with Selected bank account is not checked in bank account selection step --- src/components/AddPlaidBankAccount.js | 1 + src/components/RadioButtons.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index b6fc639546a8..1bb696d09ed1 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -301,6 +301,7 @@ function AddPlaidBankAccount({ defaultCheckedValue={defaultSelectedPlaidAccountID} onPress={handleSelectingPlaidAccount} radioButtonStyle={[styles.mb6]} + value={selectedPlaidAccountID} /> diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index 90c7d8580b5c..c9ebf712072e 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -40,10 +40,10 @@ function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyl const styles = useThemeStyles(); const [checkedValue, setCheckedValue] = useState(defaultCheckedValue); useEffect(() => { - if (value === checkedValue) { + if (value === checkedValue || value === undefined) { return; } - setCheckedValue(value ?? ''); + setCheckedValue(value); }, [checkedValue, value]); return ( From f7ca4014a94388804331d4fcdf2ae98b39b20c18 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Tue, 27 Feb 2024 19:56:32 +0100 Subject: [PATCH 146/213] Recalculate the sections only on actual change --- src/components/OptionsSelector/BaseOptionsSelector.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 23451b8e1a09..d8ed45f5d6eb 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -1,5 +1,6 @@ import {useIsFocused} from '@react-navigation/native'; import lodashGet from 'lodash/get'; +import lodashIsEqual from 'lodash/isEqual'; import PropTypes from 'prop-types'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {ScrollView, View} from 'react-native'; @@ -14,6 +15,7 @@ import ShowMoreButton from '@components/ShowMoreButton'; import TextInput from '@components/TextInput'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; +import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import getPlatform from '@libs/getPlatform'; import KeyboardShortcut from '@libs/KeyboardShortcut'; @@ -100,6 +102,7 @@ function BaseOptionsSelector(props) { const prevPaginationPage = useRef(paginationPage); const prevSelectedOptions = useRef(props.selectedOptions); const prevValue = useRef(value); + const previousSections = usePrevious(props.sections); useImperativeHandle(props.forwardedRef, () => textInputRef.current); @@ -143,7 +146,7 @@ function BaseOptionsSelector(props) { /** * Maps sections to render only allowed count of them per section. * - * @returns {Objects[]} + * @returns {Object[]} */ const sliceSections = useCallback( () => @@ -415,6 +418,10 @@ function BaseOptionsSelector(props) { }, [isFocused, props.autoFocus]); useEffect(() => { + if (lodashIsEqual(props.sections, previousSections)) { + return; + } + const newSections = sliceSections(); const newOptions = flattenSections(); @@ -434,7 +441,7 @@ function BaseOptionsSelector(props) { setFocusedIndex(prevFocusedOptionIndex || (_.isNumber(props.focusedIndex) ? props.focusedIndex : newFocusedIndex)); // we want to run this effect only when the sections change // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.sections]); + }, [props.sections, previousSections]); useEffect(() => { // If we just toggled an option on a multi-selection page or cleared the search input, scroll to top From 87f2848b1f79f24fd56b43a88931187f1447d007 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Tue, 27 Feb 2024 20:06:29 +0100 Subject: [PATCH 147/213] Reverte passing value for RadioButtons inside AddPlaidBankAccount --- src/components/AddPlaidBankAccount.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index 1bb696d09ed1..b6fc639546a8 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -301,7 +301,6 @@ function AddPlaidBankAccount({ defaultCheckedValue={defaultSelectedPlaidAccountID} onPress={handleSelectingPlaidAccount} radioButtonStyle={[styles.mb6]} - value={selectedPlaidAccountID} /> From b120f487f6b5e0f33301dfdf0aaf953abf6de0af Mon Sep 17 00:00:00 2001 From: Riya Shete <156463907+codinggeek2023@users.noreply.github.com> Date: Wed, 28 Feb 2024 00:56:55 +0530 Subject: [PATCH 148/213] Update en.ts --- src/languages/en.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index b6a24f33035c..a645c977a473 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2206,7 +2206,6 @@ export default { address: 'Address', waypointDescription: { start: 'Start', - finish: 'Finish', stop: 'Stop', }, mapPending: { From 56ab70862ec2a6d2c3fcd11e188d7ef6266dd421 Mon Sep 17 00:00:00 2001 From: Riya Shete <156463907+codinggeek2023@users.noreply.github.com> Date: Wed, 28 Feb 2024 00:57:58 +0530 Subject: [PATCH 149/213] Update IOURequestStepWaypoint.tsx --- src/pages/iou/request/step/IOURequestStepWaypoint.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx index eee6da9e87ef..c4e27d52e09f 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx @@ -82,8 +82,6 @@ function IOURequestStepWaypoint({ switch (parsedWaypointIndex) { case 0: return 'distance.waypointDescription.start'; - case waypointCount - 1: - return 'distance.waypointDescription.finish'; default: return 'distance.waypointDescription.stop'; } From f5963cae18f1277ed95884b4db1d673f9068360c Mon Sep 17 00:00:00 2001 From: Riya Shete <156463907+codinggeek2023@users.noreply.github.com> Date: Wed, 28 Feb 2024 01:01:36 +0530 Subject: [PATCH 150/213] Update es.ts --- src/languages/es.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index c9ff087d0de7..da30eb01e84d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2725,7 +2725,6 @@ export default { address: 'Dirección', waypointDescription: { start: 'Comienzo', - finish: 'Final', stop: 'Parada', }, mapPending: { From f92b845aff8c030b2a3fd2c76c69d9e06bf101c8 Mon Sep 17 00:00:00 2001 From: Riya Shete <156463907+codinggeek2023@users.noreply.github.com> Date: Wed, 28 Feb 2024 01:06:27 +0530 Subject: [PATCH 151/213] fix lint --- src/pages/iou/request/step/IOURequestStepWaypoint.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx index c4e27d52e09f..5a68c85546e6 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx @@ -85,7 +85,7 @@ function IOURequestStepWaypoint({ default: return 'distance.waypointDescription.stop'; } - }, [parsedWaypointIndex, waypointCount]); + }, [parsedWaypointIndex]); const locationBias = useLocationBias(allWaypoints, userLocation); const waypointAddress = currentWaypoint.address ?? ''; From 8e816ff76502b7a412d53f804f17e18c6293e2da Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Tue, 27 Feb 2024 20:53:23 +0100 Subject: [PATCH 152/213] Set sections to initial value on render --- src/components/OptionsSelector/BaseOptionsSelector.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index d8ed45f5d6eb..ac76846ab2e3 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -346,6 +346,9 @@ function BaseOptionsSelector(props) { [allOptions, sections], ); + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(() => setSections(sliceSections()), []); + useEffect(() => { subscribeToEnterShortcut(); subscribeToCtrlEnterShortcut(); From ffe921337aad6287e627ed7a397b41eaadea567c Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Tue, 27 Feb 2024 22:29:40 +0200 Subject: [PATCH 153/213] Use direct fontSizeXLarge --- src/styles/index.ts | 2 +- src/styles/variables.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index fa67f28b294b..304cc9e7f6bd 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4631,7 +4631,7 @@ const styles = (theme: ThemeColors) => workspaceTitleStyle: { ...headlineFont, - fontSize: variables.workspaceProfileName, + fontSize: variables.fontSizeXLarge, }, } satisfies Styles); diff --git a/src/styles/variables.ts b/src/styles/variables.ts index c88d8f0df784..3d5115a31f54 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -216,7 +216,6 @@ export default { updateAnimationH: 240, updateTextViewContainerWidth: 310, updateViewHeaderHeight: 70, - workspaceProfileName: 22, mushroomTopHatWidth: 138, mushroomTopHatHeight: 128, From b39957f599bc7d9807ae7a843ac10f1caf472f1e Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Tue, 27 Feb 2024 21:37:25 +0100 Subject: [PATCH 154/213] Initialize sections with a value --- .../OptionsSelector/BaseOptionsSelector.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index ac76846ab2e3..1fa63f181dd6 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -85,7 +85,6 @@ function BaseOptionsSelector(props) { const accessibilityRoles = _.values(CONST.ROLE); const [disabledOptionsIndexes, setDisabledOptionsIndexes] = useState([]); - const [sections, setSections] = useState(); const [shouldDisableRowSelection, setShouldDisableRowSelection] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const [value, setValue] = useState(''); @@ -137,12 +136,6 @@ function BaseOptionsSelector(props) { return calcAllOptions; }, [props.sections]); - // eslint-disable-next-line react-hooks/exhaustive-deps - const initialAllOptions = useMemo(() => flattenSections(), []); - const [allOptions, setAllOptions] = useState(initialAllOptions); - const [focusedIndex, setFocusedIndex] = useState(getInitiallyFocusedIndex(initialAllOptions)); - const [focusedOption, setFocusedOption] = useState(allOptions[focusedIndex]); - /** * Maps sections to render only allowed count of them per section. * @@ -165,6 +158,13 @@ function BaseOptionsSelector(props) { [paginationPage, props.sections], ); + // eslint-disable-next-line react-hooks/exhaustive-deps + const initialAllOptions = useMemo(() => flattenSections(), []); + const [sections, setSections] = useState(sliceSections()); + const [allOptions, setAllOptions] = useState(initialAllOptions); + const [focusedIndex, setFocusedIndex] = useState(getInitiallyFocusedIndex(initialAllOptions)); + const [focusedOption, setFocusedOption] = useState(allOptions[focusedIndex]); + /** * Completes the follow-up actions after a row is selected * @@ -346,9 +346,6 @@ function BaseOptionsSelector(props) { [allOptions, sections], ); - // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(() => setSections(sliceSections()), []); - useEffect(() => { subscribeToEnterShortcut(); subscribeToCtrlEnterShortcut(); From 8cf4670d37eeda785db17f8509150fc5800e61b5 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Tue, 27 Feb 2024 22:16:57 +0100 Subject: [PATCH 155/213] Update code --- src/components/RadioButtons.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index c9ebf712072e..2030ce8f0bfd 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -39,11 +39,12 @@ type RadioButtonsProps = { function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyle, errorText, onInputChange = () => {}, value}: RadioButtonsProps, ref: ForwardedRef) { const styles = useThemeStyles(); const [checkedValue, setCheckedValue] = useState(defaultCheckedValue); + useEffect(() => { if (value === checkedValue || value === undefined) { return; } - setCheckedValue(value); + setCheckedValue(value ?? ''); }, [checkedValue, value]); return ( From 630a17e2a97815b48e80bf7c96c9411438e6f267 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:51:24 +0530 Subject: [PATCH 156/213] Add prop isRowMultilineSupported to BaseSelectionList --- src/components/SelectionList/BaseSelectionList.tsx | 1 + src/components/SelectionList/types.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 1c69d00b3910..9ddb37695334 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -63,6 +63,7 @@ function BaseSelectionList( onLayout, customListHeader, listHeaderWrapperStyle, + isRowMultilineSupported = false, }: BaseSelectionListProps, inputRef: ForwardedRef, ) { diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 59f6b14cfb1f..370439b48835 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -258,6 +258,9 @@ type BaseSelectionListProps = Partial & { /** Styles for the list header wrapper */ listHeaderWrapperStyle?: StyleProp; + + /** Whether to wrap large text up to 2 lines */ + isRowMultilineSupported?: boolean; }; type ItemLayout = { From b7ab5736aee5e7f22f1abb5b7fa939d830deec66 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:53:24 +0530 Subject: [PATCH 157/213] Add isMultilineSupported prop to ListItem --- src/components/SelectionList/RadioListItem.tsx | 1 + src/components/SelectionList/types.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index b3340a0faf7a..b39f014a193b 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -16,6 +16,7 @@ function RadioListItem({ onDismissError, shouldPreventDefaultFocusOnSelectRow, rightHandSideComponent, + isMultilineSupported = false, }: RadioListItemProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 370439b48835..550276166e9e 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -37,6 +37,9 @@ type CommonListItemProps = { /** Styles for the checkbox wrapper view if select multiple option is on */ selectMultipleStyle?: StyleProp; + + /** Whether to wrap large text up to 2 lines */ + isMultilineSupported?: boolean; }; type ListItem = { @@ -83,6 +86,9 @@ type ListItem = { /** Whether this option should show subscript */ shouldShowSubscript?: boolean; + + /** Whether to wrap large text up to 2 lines */ + isMultilineSupported?: boolean; }; type ListItemProps = CommonListItemProps & { From 52a2fedc3f332edc3535885984540b4804132ecc Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:55:15 +0530 Subject: [PATCH 158/213] Add numberOfLines prop type to TextWithTooltip --- src/components/TextWithTooltip/index.native.tsx | 4 ++-- src/components/TextWithTooltip/index.tsx | 4 ++-- src/components/TextWithTooltip/types.ts | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/TextWithTooltip/index.native.tsx b/src/components/TextWithTooltip/index.native.tsx index f8013ae00e4c..345f1fb770a5 100644 --- a/src/components/TextWithTooltip/index.native.tsx +++ b/src/components/TextWithTooltip/index.native.tsx @@ -2,11 +2,11 @@ import React from 'react'; import Text from '@components/Text'; import type TextWithTooltipProps from './types'; -function TextWithTooltip({text, textStyles}: TextWithTooltipProps) { +function TextWithTooltip({text, textStyles, numberOfLines}: TextWithTooltipProps) { return ( {text} diff --git a/src/components/TextWithTooltip/index.tsx b/src/components/TextWithTooltip/index.tsx index fd202db8de64..0b03c9fcc592 100644 --- a/src/components/TextWithTooltip/index.tsx +++ b/src/components/TextWithTooltip/index.tsx @@ -7,7 +7,7 @@ type LayoutChangeEvent = { target: HTMLElement; }; -function TextWithTooltip({text, shouldShowTooltip, textStyles}: TextWithTooltipProps) { +function TextWithTooltip({text, shouldShowTooltip, textStyles, numberOfLines}: TextWithTooltipProps) { const [showTooltip, setShowTooltip] = useState(false); return ( @@ -17,7 +17,7 @@ function TextWithTooltip({text, shouldShowTooltip, textStyles}: TextWithTooltipP > { const target = (e.nativeEvent as unknown as LayoutChangeEvent).target; if (!shouldShowTooltip) { diff --git a/src/components/TextWithTooltip/types.ts b/src/components/TextWithTooltip/types.ts index 80517b0b2acf..426336851ada 100644 --- a/src/components/TextWithTooltip/types.ts +++ b/src/components/TextWithTooltip/types.ts @@ -4,6 +4,9 @@ type TextWithTooltipProps = { text: string; shouldShowTooltip: boolean; textStyles?: StyleProp; + + /** Custom number of lines for Text */ + numberOfLines?: number; }; export default TextWithTooltipProps; From f585826cfe433c01e812c5fd76058cf7c6055020 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:55:58 +0530 Subject: [PATCH 159/213] Pass isRowMultilineSupported in CategoryPicker --- src/components/CategoryPicker/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index 2374fc9e5d0c..89312a7ca614 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -70,6 +70,7 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC onSelectRow={onSubmit} ListItem={RadioListItem} initiallyFocusedOptionKey={selectedOptionKey} + isRowMultilineSupported /> ); } From 3a87b37183cd5e6854891b254a52e764c9c61019 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:56:17 +0530 Subject: [PATCH 160/213] Pass isMultilineSupported to ListItem --- src/components/SelectionList/BaseSelectionList.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 9ddb37695334..9db0b4102b99 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -294,6 +294,7 @@ function BaseSelectionList( shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} keyForList={item.keyForList} + isMultilineSupported={isRowMultilineSupported} /> ); }; From b72a521c1290767455744e0e6da030f576e2da1c Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:57:00 +0530 Subject: [PATCH 161/213] Pass custom numberOfLines to TextWithTooltip --- src/components/SelectionList/RadioListItem.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index b39f014a193b..019cae84d156 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -45,9 +45,10 @@ function RadioListItem({ styles.optionDisplayName, isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText, styles.sidebarLinkTextBold, - styles.pre, + isMultilineSupported ? styles.preWrap : styles.pre, item.alternateText ? styles.mb1 : null, ]} + numberOfLines={isMultilineSupported ? 2 : 1} /> {!!item.alternateText && ( From c140118d793b8400ca9f12f29c1cf0f92d603b99 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:57:37 +0530 Subject: [PATCH 162/213] Add the missing prop comments in TextWithTooltip Props --- src/components/TextWithTooltip/types.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/TextWithTooltip/types.ts b/src/components/TextWithTooltip/types.ts index 426336851ada..9199cc34ad5f 100644 --- a/src/components/TextWithTooltip/types.ts +++ b/src/components/TextWithTooltip/types.ts @@ -1,8 +1,13 @@ import type {StyleProp, TextStyle} from 'react-native'; type TextWithTooltipProps = { + /** The text to display */ text: string; + + /** Whether to show the toolip text */ shouldShowTooltip: boolean; + + /** Additional text styles */ textStyles?: StyleProp; /** Custom number of lines for Text */ From 261ae17cdf57596d40fdae0da86e3541029861c9 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 28 Feb 2024 06:59:47 +0530 Subject: [PATCH 163/213] Run prettier --- src/components/TextWithTooltip/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextWithTooltip/types.ts b/src/components/TextWithTooltip/types.ts index 9199cc34ad5f..262a1f7dba03 100644 --- a/src/components/TextWithTooltip/types.ts +++ b/src/components/TextWithTooltip/types.ts @@ -6,7 +6,7 @@ type TextWithTooltipProps = { /** Whether to show the toolip text */ shouldShowTooltip: boolean; - + /** Additional text styles */ textStyles?: StyleProp; From 989da96fc41b26f6454c341aa010629ee0d4847f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 Feb 2024 11:15:12 +0800 Subject: [PATCH 164/213] only reset params if coming from global create --- src/pages/iou/request/IOURequestStartPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.js index 20abb48cd9e1..b71a69c893b7 100644 --- a/src/pages/iou/request/IOURequestStartPage.js +++ b/src/pages/iou/request/IOURequestStartPage.js @@ -121,13 +121,13 @@ function IOURequestStartPage({ if (newIouType === previousIOURequestType) { return; } - if (iouType === CONST.IOU.TYPE.SPLIT) { + if (iouType === CONST.IOU.TYPE.SPLIT && transaction.isFromGlobalCreate) { IOU.updateMoneyRequestTypeParams(navigation.getState().routes, CONST.IOU.TYPE.REQUEST, newIouType); } IOU.initMoneyRequest(reportID, isFromGlobalCreate, newIouType); transactionRequestType.current = newIouType; }, - [previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation], + [previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation, transaction.isFromGlobalCreate], ); if (!transaction.transactionID) { From b4cc17f7c3d40a8fba27b72e94cc7b735187d635 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 Feb 2024 11:20:45 +0800 Subject: [PATCH 165/213] prettier --- src/pages/iou/request/IOURequestStartPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.js index b71a69c893b7..8e50577ede1f 100644 --- a/src/pages/iou/request/IOURequestStartPage.js +++ b/src/pages/iou/request/IOURequestStartPage.js @@ -1,4 +1,4 @@ -import {useNavigation, useFocusEffect} from '@react-navigation/native'; +import {useFocusEffect, useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; From 07ebdf0a77b571642f33eb3b30d1ffea884f5a53 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 28 Feb 2024 12:27:19 +0800 Subject: [PATCH 166/213] update params --- ...yForRefactorRequestParticipantsSelector.js | 21 ++++++++----------- .../step/IOURequestStepParticipants.js | 6 ++++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 3804fef8c70e..2865316b7fd5 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -189,16 +189,13 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ * @param {Object} option */ const addSingleParticipant = (option) => { - onParticipantsAdded( - [ - { - ..._.pick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText'), - selected: true, - }, - ], - false, - ); - onFinish(false); + onParticipantsAdded([ + { + ..._.pick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText'), + selected: true, + }, + ]); + onFinish(); }; /** @@ -237,7 +234,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ ]; } - onParticipantsAdded(newSelectedOptions, newSelectedOptions.length !== 0); + onParticipantsAdded(newSelectedOptions, newSelectedOptions.length !== 0 ? CONST.IOU.TYPE.SPLIT : undefined); }, [participants, onParticipantsAdded], ); @@ -266,7 +263,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ return; } - onFinish(true); + onFinish(CONST.IOU.TYPE.SPLIT); }, [shouldShowSplitBillErrorMessage, onFinish]); const footerContent = useMemo( diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index b5c1e5c2c6f3..b2f5cbb68cd1 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -71,7 +71,8 @@ function IOURequestStepParticipants({ }, [participants, updateRouteParams]); const addParticipant = useCallback( - (val, isSplit) => { + (val, selectedIouType) => { + const isSplit = selectedIouType === CONST.IOU.TYPE.SPLIT; // It's only possible to switch between REQUEST and SPLIT. // We want to update the IOU type only if it's not updated yet to prevent unnecessary updates. if (isSplit && iouType !== CONST.IOU.TYPE.SPLIT) { @@ -106,7 +107,8 @@ function IOURequestStepParticipants({ ); const goToNextStep = useCallback( - (isSplit) => { + (selectedIouType) => { + const isSplit = selectedIouType === CONST.IOU.TYPE.SPLIT; const nextStepIOUType = !isSplit && iouType !== CONST.IOU.TYPE.REQUEST ? CONST.IOU.TYPE.REQUEST : iouType; IOU.setMoneyRequestTag(transactionID, ''); IOU.setMoneyRequestCategory(transactionID, ''); From dd6703aacc2647341c23cf76531bb7ab471eb979 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 28 Feb 2024 11:42:34 +0700 Subject: [PATCH 167/213] Get correct default category for split scan request --- src/pages/iou/request/step/IOURequestStepCategory.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepCategory.js b/src/pages/iou/request/step/IOURequestStepCategory.js index 3e0feec02854..1945edbc24c4 100644 --- a/src/pages/iou/request/step/IOURequestStepCategory.js +++ b/src/pages/iou/request/step/IOURequestStepCategory.js @@ -1,4 +1,5 @@ import lodashGet from 'lodash/get'; +import lodashIsEmpty from 'lodash/isEmpty'; import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; @@ -72,7 +73,7 @@ function IOURequestStepCategory({ const {translate} = useLocalize(); const isEditing = action === CONST.IOU.ACTION.EDIT; const isEditingSplitBill = isEditing && iouType === CONST.IOU.TYPE.SPLIT; - const {category: transactionCategory} = ReportUtils.getTransactionDetails(isEditingSplitBill ? splitDraftTransaction : transaction); + const {category: transactionCategory} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction); const isPolicyExpenseChat = ReportUtils.isGroupPolicy(report); // eslint-disable-next-line rulesdir/no-negated-variables From f3cdd232b6d1f1c32537030a99040d68f5df8c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Wed, 28 Feb 2024 09:05:13 +0100 Subject: [PATCH 168/213] Fix volume button on iOS/Safari --- src/components/VideoPlayerContexts/VolumeContext.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/VideoPlayerContexts/VolumeContext.js b/src/components/VideoPlayerContexts/VolumeContext.js index 2df463654075..a0b972d37a0d 100644 --- a/src/components/VideoPlayerContexts/VolumeContext.js +++ b/src/components/VideoPlayerContexts/VolumeContext.js @@ -14,7 +14,7 @@ function VolumeContextProvider({children}) { if (!currentVideoPlayerRef.current) { return; } - currentVideoPlayerRef.current.setStatusAsync({volume: newVolume}); + currentVideoPlayerRef.current.setStatusAsync({volume: newVolume, isMuted: newVolume === 0}); volume.value = newVolume; }, [currentVideoPlayerRef, volume], From 64368d5784c3ad869251eb6845070fbfb5ef8f23 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 28 Feb 2024 10:49:28 +0100 Subject: [PATCH 169/213] fix: typecheck --- src/libs/EmojiUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 353ced9e1e45..cab0f48d75fd 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -10,7 +10,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {FrequentlyUsedEmoji, Locale} from '@src/types/onyx'; import type {ReportActionReaction, UsersReactions} from '@src/types/onyx/ReportActionReactions'; import type IconAsset from '@src/types/utils/IconAsset'; -import {SupportedLanguage} from './EmojiTrie'; type HeaderIndice = {code: string; index: number; icon: IconAsset}; type EmojiSpacer = {code: string; spacer: boolean}; @@ -384,7 +383,7 @@ function replaceAndExtractEmojis(text: string, preferredSkinTone: number = CONST * Suggest emojis when typing emojis prefix after colon * @param [limit] - matching emojis limit */ -function suggestEmojis(text: string, lang: SupportedLanguage, limit: number = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { +function suggestEmojis(text: string, lang: Locale, limit: number = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined { // emojisTrie is importing the emoji JSON file on the app starting and we want to avoid it const emojisTrie = require('./EmojiTrie').default; From b1bf314b4c1560b14cc2f25af0e51ecf60e448fd Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 28 Feb 2024 11:21:14 +0100 Subject: [PATCH 170/213] preserve credentials when clearing onyx --- src/pages/settings/AboutPage/TroubleshootPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/AboutPage/TroubleshootPage.tsx b/src/pages/settings/AboutPage/TroubleshootPage.tsx index ade6932c4b2c..9bc756df03cb 100644 --- a/src/pages/settings/AboutPage/TroubleshootPage.tsx +++ b/src/pages/settings/AboutPage/TroubleshootPage.tsx @@ -40,6 +40,7 @@ const keysToPreserve: OnyxKey[] = [ ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, + ONYXKEYS.CREDENTIALS, ]; type BaseMenuItem = { From 66a4c0a299db42b596e6b90a1ce269161175bcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 28 Feb 2024 11:34:10 +0100 Subject: [PATCH 171/213] feat: add optional checkbox press handled --- src/components/SelectionList/BaseListItem.tsx | 17 +++++++++++++++-- .../SelectionList/BaseSelectionList.tsx | 2 ++ src/components/SelectionList/TableListItem.tsx | 2 ++ src/components/SelectionList/types.ts | 6 ++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index 2f853dc55839..824ad757cb38 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -19,6 +19,7 @@ function BaseListItem({ shouldPreventDefaultFocusOnSelectRow = false, canSelectMultiple = false, onSelectRow, + onCheckboxPress, onDismissError = () => {}, rightHandSideComponent, keyForList, @@ -43,6 +44,15 @@ function BaseListItem({ return rightHandSideComponent; }; + const handleCheckboxPress = () => { + if (onCheckboxPress) { + onCheckboxPress(item); + } else { + // If base list show checkbox, but there is no onCheckboxPress, then onSelectRow should be called as primary action for the entire row + onSelectRow(item); + } + }; + return ( onDismissError(item)} @@ -66,8 +76,11 @@ function BaseListItem({ <> {canSelectMultiple && ( - @@ -80,7 +93,7 @@ function BaseListItem({ /> )} - + )} {typeof children === 'function' ? children(hovered) : children} diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 1c69d00b3910..1e75aaeac93e 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -30,6 +30,7 @@ function BaseSelectionList( ListItem, canSelectMultiple = false, onSelectRow, + onCheckboxPress, onSelectAll, onDismissError, textInputLabel = '', @@ -289,6 +290,7 @@ function BaseSelectionList( showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} onSelectRow={() => selectRow(item)} + onCheckboxPress={() => onCheckboxPress?.(item)} onDismissError={() => onDismissError?.(item)} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} diff --git a/src/components/SelectionList/TableListItem.tsx b/src/components/SelectionList/TableListItem.tsx index 922937c72219..2acad7f40f20 100644 --- a/src/components/SelectionList/TableListItem.tsx +++ b/src/components/SelectionList/TableListItem.tsx @@ -15,6 +15,7 @@ function TableListItem({ isDisabled, canSelectMultiple, onSelectRow, + onCheckboxPress, onDismissError, shouldPreventDefaultFocusOnSelectRow, rightHandSideComponent, @@ -37,6 +38,7 @@ function TableListItem({ showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} onSelectRow={onSelectRow} + onCheckboxPress={onCheckboxPress} onDismissError={onDismissError} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 59f6b14cfb1f..b6cc0c786adb 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -23,6 +23,9 @@ type CommonListItemProps = { /** Callback to fire when the item is pressed */ onSelectRow: (item: TItem) => void; + /** Callback to fire when a checkbox is pressed */ + onCheckboxPress?: (item: TItem) => void; + /** Callback to fire when an error is dismissed */ onDismissError?: (item: TItem) => void; @@ -157,6 +160,9 @@ type BaseSelectionListProps = Partial & { /** Callback to fire when a row is pressed */ onSelectRow: (item: TItem) => void; + /** Optional callback function triggered upon pressing a checkbox. If undefined and the list displays checkboxes, checkbox interactions are managed by onSelectRow, allowing for pressing anywhere on the list. */ + onCheckboxPress?: (item: TItem) => void; + /** Callback to fire when "Select All" checkbox is pressed. Only use along with `canSelectMultiple` */ onSelectAll?: () => void; From ad6aa3811d4cf223a365a492d202bc64050014b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 28 Feb 2024 11:34:50 +0100 Subject: [PATCH 172/213] chore: temporary navigate to categories settings when pressing row --- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 7cd9972a6f57..721341073d72 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -129,7 +129,8 @@ function WorkspaceCategoriesPage({policyCategories, route}: WorkspaceCategoriesP Date: Wed, 28 Feb 2024 11:44:26 +0100 Subject: [PATCH 173/213] fix: pass undefined if onCheckboxPress is undefined --- src/components/SelectionList/BaseListItem.tsx | 2 -- src/components/SelectionList/BaseSelectionList.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index 824ad757cb38..98b1999625ee 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -48,7 +48,6 @@ function BaseListItem({ if (onCheckboxPress) { onCheckboxPress(item); } else { - // If base list show checkbox, but there is no onCheckboxPress, then onSelectRow should be called as primary action for the entire row onSelectRow(item); } }; @@ -77,7 +76,6 @@ function BaseListItem({ {canSelectMultiple && ( ( showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} onSelectRow={() => selectRow(item)} - onCheckboxPress={() => onCheckboxPress?.(item)} + onCheckboxPress={onCheckboxPress ? () => onCheckboxPress?.(item) : undefined} onDismissError={() => onDismissError?.(item)} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} From 645a1a402a9478af43a999d5404c2b4a39f9e3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 28 Feb 2024 11:51:35 +0100 Subject: [PATCH 174/213] feat: add support for radio and user list item --- src/components/SelectionList/RadioListItem.tsx | 2 ++ src/components/SelectionList/UserListItem.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index b3340a0faf7a..d56e0330de32 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -13,6 +13,7 @@ function RadioListItem({ isDisabled, canSelectMultiple, onSelectRow, + onCheckboxPress, onDismissError, shouldPreventDefaultFocusOnSelectRow, rightHandSideComponent, @@ -30,6 +31,7 @@ function RadioListItem({ showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} onSelectRow={onSelectRow} + onCheckboxPress={onCheckboxPress} onDismissError={onDismissError} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 06d0f6452117..fc39bb4b1259 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -18,6 +18,7 @@ function UserListItem({ isDisabled, canSelectMultiple, onSelectRow, + onCheckboxPress, onDismissError, shouldPreventDefaultFocusOnSelectRow, rightHandSideComponent, @@ -41,6 +42,7 @@ function UserListItem({ showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} onSelectRow={onSelectRow} + onCheckboxPress={onCheckboxPress} onDismissError={onDismissError} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} From 3f4172ccc694821ec04301236ff4472a17d47e22 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi <64629613+neonbhai@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:20:15 +0530 Subject: [PATCH 175/213] Add default prop value to numberOfLines Co-authored-by: Fedi Rajhi --- src/components/TextWithTooltip/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextWithTooltip/index.native.tsx b/src/components/TextWithTooltip/index.native.tsx index 345f1fb770a5..f8f81cc7181c 100644 --- a/src/components/TextWithTooltip/index.native.tsx +++ b/src/components/TextWithTooltip/index.native.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Text from '@components/Text'; import type TextWithTooltipProps from './types'; -function TextWithTooltip({text, textStyles, numberOfLines}: TextWithTooltipProps) { +function TextWithTooltip({text, textStyles, numberOfLines = 1}: TextWithTooltipProps) { return ( Date: Wed, 28 Feb 2024 19:22:08 +0530 Subject: [PATCH 176/213] Add default prop value to numberOfLines (web) Co-authored-by: Fedi Rajhi --- src/components/TextWithTooltip/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextWithTooltip/index.tsx b/src/components/TextWithTooltip/index.tsx index 0b03c9fcc592..755959d52cfc 100644 --- a/src/components/TextWithTooltip/index.tsx +++ b/src/components/TextWithTooltip/index.tsx @@ -7,7 +7,7 @@ type LayoutChangeEvent = { target: HTMLElement; }; -function TextWithTooltip({text, shouldShowTooltip, textStyles, numberOfLines}: TextWithTooltipProps) { +function TextWithTooltip({text, shouldShowTooltip, textStyles, numberOfLines = 1}: TextWithTooltipProps) { const [showTooltip, setShowTooltip] = useState(false); return ( From 6d50cb5a8b12c514e82e267189e92641e7809f41 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi <64629613+neonbhai@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:23:41 +0530 Subject: [PATCH 177/213] Remove null coalescing operator in favor of default value Co-authored-by: Fedi Rajhi --- src/components/TextWithTooltip/index.native.tsx | 2 +- src/components/TextWithTooltip/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TextWithTooltip/index.native.tsx b/src/components/TextWithTooltip/index.native.tsx index f8f81cc7181c..3780df6362be 100644 --- a/src/components/TextWithTooltip/index.native.tsx +++ b/src/components/TextWithTooltip/index.native.tsx @@ -6,7 +6,7 @@ function TextWithTooltip({text, textStyles, numberOfLines = 1}: TextWithTooltipP return ( {text} diff --git a/src/components/TextWithTooltip/index.tsx b/src/components/TextWithTooltip/index.tsx index 755959d52cfc..96721488c6db 100644 --- a/src/components/TextWithTooltip/index.tsx +++ b/src/components/TextWithTooltip/index.tsx @@ -17,7 +17,7 @@ function TextWithTooltip({text, shouldShowTooltip, textStyles, numberOfLines = 1 > { const target = (e.nativeEvent as unknown as LayoutChangeEvent).target; if (!shouldShowTooltip) { From 4fcfbb0cb58c6be865368cc30df660e8f84b833c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 28 Feb 2024 14:30:35 +0000 Subject: [PATCH 178/213] Update version to 1.4.44-8 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9563e410dcb6..2528e23ebfe4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001044407 - versionName "1.4.44-7" + versionCode 1001044408 + versionName "1.4.44-8" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index a8f8937da93e..b8cbf2e78f99 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.44.7 + 1.4.44.8 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 95afa8d35c0a..48b003f4185e 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.44.7 + 1.4.44.8 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 31de98f3dcc2..45b77311fb36 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.44 CFBundleVersion - 1.4.44.7 + 1.4.44.8 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index dd072352d464..42795645cbe4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.44-7", + "version": "1.4.44-8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.44-7", + "version": "1.4.44-8", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 335c28a21586..7df7dcfcb93f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.44-7", + "version": "1.4.44-8", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 2e1472b152c8f9ea7984e17ad7f3fce4dc6f4f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 28 Feb 2024 15:49:16 +0100 Subject: [PATCH 179/213] refactor: select all checkbox only in checkbox area --- .../SelectionList/BaseSelectionList.tsx | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index f0fd096a9d95..5b5a67dc1e28 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -8,7 +8,6 @@ import Button from '@components/Button'; import Checkbox from '@components/Checkbox'; import FixedFooter from '@components/FixedFooter'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import SafeAreaConsumer from '@components/SafeAreaConsumer'; import SectionList from '@components/SectionList'; import Text from '@components/Text'; @@ -431,28 +430,19 @@ function BaseSelectionList( ) : ( <> {!headerMessage && canSelectMultiple && shouldShowSelectAll && ( - e.preventDefault() : undefined} - > + {customListHeader ?? ( {translate('workspace.people.selectAll')} )} - + )} {!headerMessage && !canSelectMultiple && customListHeader} Date: Wed, 28 Feb 2024 15:12:03 +0000 Subject: [PATCH 180/213] Update version to 1.4.44-9 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2528e23ebfe4..cb113bd4ffce 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001044408 - versionName "1.4.44-8" + versionCode 1001044409 + versionName "1.4.44-9" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index b8cbf2e78f99..580aa09f9d03 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.44.8 + 1.4.44.9 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 48b003f4185e..156366295c82 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.44.8 + 1.4.44.9 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 45b77311fb36..6b3e1231eab2 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.44 CFBundleVersion - 1.4.44.8 + 1.4.44.9 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 42795645cbe4..17e705c2bc8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.44-8", + "version": "1.4.44-9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.44-8", + "version": "1.4.44-9", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7df7dcfcb93f..e6d643517a5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.44-8", + "version": "1.4.44-9", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 4576f782b3ba52f4aa69f33fe6e327a7ed46d033 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi <64629613+neonbhai@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:53:59 +0530 Subject: [PATCH 181/213] Update src/components/SelectionList/types.ts Co-authored-by: Puneet Lath --- src/components/SelectionList/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 550276166e9e..54728307fd41 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -38,7 +38,7 @@ type CommonListItemProps = { /** Styles for the checkbox wrapper view if select multiple option is on */ selectMultipleStyle?: StyleProp; - /** Whether to wrap large text up to 2 lines */ + /** Whether to wrap long text up to 2 lines */ isMultilineSupported?: boolean; }; From d928ea06a8bdfee4dd1068a88627d3cfdb30884f Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi <64629613+neonbhai@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:54:06 +0530 Subject: [PATCH 182/213] Update src/components/SelectionList/types.ts Co-authored-by: Puneet Lath --- src/components/SelectionList/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 54728307fd41..0a550f95dbd4 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -87,7 +87,7 @@ type ListItem = { /** Whether this option should show subscript */ shouldShowSubscript?: boolean; - /** Whether to wrap large text up to 2 lines */ + /** Whether to wrap long text up to 2 lines */ isMultilineSupported?: boolean; }; From 0253712257c16b3b3a52b83a70775dc180a1a433 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi <64629613+neonbhai@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:54:13 +0530 Subject: [PATCH 183/213] Update src/components/SelectionList/types.ts Co-authored-by: Puneet Lath --- src/components/SelectionList/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 0a550f95dbd4..620f1d4cb39b 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -265,7 +265,7 @@ type BaseSelectionListProps = Partial & { /** Styles for the list header wrapper */ listHeaderWrapperStyle?: StyleProp; - /** Whether to wrap large text up to 2 lines */ + /** Whether to wrap long text up to 2 lines */ isRowMultilineSupported?: boolean; }; From a3334e7590d6acdd4a94bd29b6cbd5f4d2ca7177 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi <64629613+neonbhai@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:54:19 +0530 Subject: [PATCH 184/213] Update src/components/TextWithTooltip/types.ts Co-authored-by: Puneet Lath --- src/components/TextWithTooltip/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextWithTooltip/types.ts b/src/components/TextWithTooltip/types.ts index 262a1f7dba03..19c0b0dca6ed 100644 --- a/src/components/TextWithTooltip/types.ts +++ b/src/components/TextWithTooltip/types.ts @@ -10,7 +10,7 @@ type TextWithTooltipProps = { /** Additional text styles */ textStyles?: StyleProp; - /** Custom number of lines for Text */ + /** Custom number of lines for text wrapping */ numberOfLines?: number; }; From 2abcf727f390493c1b65086b5c269232cedd6da8 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:28:58 -0500 Subject: [PATCH 185/213] when getting the navigation subtitle, do not recursively go through parents, but rather always use the immediate parent --- src/libs/ReportUtils.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0f8656adfa51..520a76306047 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2558,19 +2558,14 @@ function getReportName(report: OnyxEntry, policy: OnyxEntry = nu } /** - * Recursively navigates through thread parents to get the root report and workspace name. - * The recursion stops when we find a non thread or money request report, whichever comes first. + * Get the report name and workspace name for a report based on the type of the report */ -function getRootReportAndWorkspaceName(report: OnyxEntry): ReportAndWorkspaceName { +function getReportAndWorkspaceName(report: OnyxEntry): ReportAndWorkspaceName { if (!report) { return { rootReportName: '', }; } - if (isChildReport(report) && !isMoneyRequestReport(report) && !isTaskReport(report)) { - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - return getRootReportAndWorkspaceName(parentReport); - } if (isIOURequest(report)) { return { @@ -2617,16 +2612,17 @@ function getChatRoomSubtitle(report: OnyxEntry): string | undefined { * Gets the parent navigation subtitle for the report */ function getParentNavigationSubtitle(report: OnyxEntry): ParentNavigationSummaryParams { - if (isThread(report)) { - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - const {rootReportName, workspaceName} = getRootReportAndWorkspaceName(parentReport); - if (!rootReportName) { - return {}; - } + if (!isThread(report)) { + return {}; + } - return {rootReportName, workspaceName}; + const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; + const {rootReportName, workspaceName} = getReportAndWorkspaceName(parentReport); + if (!rootReportName) { + return {}; } - return {}; + + return {rootReportName, workspaceName}; } /** From 4d9846a4f0ed0775ecdb3622f6003e5938c7c56c Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:35:47 -0500 Subject: [PATCH 186/213] combine getParentNavigationSubtitle and getReportAndWorkspaceName --- src/libs/ReportUtils.ts | 54 ++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 520a76306047..867e1e3f5a12 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -98,11 +98,6 @@ type SpendBreakdown = { type ParticipantDetails = [number, string, UserUtils.AvatarSource, UserUtils.AvatarSource]; -type ReportAndWorkspaceName = { - rootReportName: string; - workspaceName?: string; -}; - type OptimisticAddCommentReportAction = Pick< ReportAction, | 'reportActionID' @@ -2557,34 +2552,6 @@ function getReportName(report: OnyxEntry, policy: OnyxEntry = nu return participantsWithoutCurrentUser.map((accountID) => getDisplayNameForParticipant(accountID, isMultipleParticipantReport)).join(', '); } -/** - * Get the report name and workspace name for a report based on the type of the report - */ -function getReportAndWorkspaceName(report: OnyxEntry): ReportAndWorkspaceName { - if (!report) { - return { - rootReportName: '', - }; - } - - if (isIOURequest(report)) { - return { - rootReportName: getReportName(report), - }; - } - if (isExpenseRequest(report)) { - return { - rootReportName: getReportName(report), - workspaceName: isIOUReport(report) ? CONST.POLICY.OWNER_EMAIL_FAKE : getPolicyName(report, true), - }; - } - - return { - rootReportName: getReportName(report), - workspaceName: getPolicyName(report, true), - }; -} - /** * Get either the policyName or domainName the chat is tied to */ @@ -2617,12 +2584,27 @@ function getParentNavigationSubtitle(report: OnyxEntry): ParentNavigatio } const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - const {rootReportName, workspaceName} = getReportAndWorkspaceName(parentReport); - if (!rootReportName) { + if (!parentReport) { return {}; } - return {rootReportName, workspaceName}; + if (isIOURequest(parentReport)) { + return { + rootReportName: getReportName(parentReport), + }; + } + + if (isExpenseRequest(parentReport)) { + return { + rootReportName: getReportName(parentReport), + workspaceName: isIOUReport(parentReport) ? CONST.POLICY.OWNER_EMAIL_FAKE : getPolicyName(parentReport, true), + }; + } + + return { + rootReportName: getReportName(parentReport), + workspaceName: getPolicyName(parentReport, true), + }; } /** From c18774fff02b3684e32a9614806ae9dc3781f0ea Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 28 Feb 2024 15:36:49 +0000 Subject: [PATCH 187/213] Update version to 1.4.44-10 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index cb113bd4ffce..f2bddfacf64e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001044409 - versionName "1.4.44-9" + versionCode 1001044410 + versionName "1.4.44-10" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 580aa09f9d03..45ab7d5c9fb4 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.44.9 + 1.4.44.10 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 156366295c82..c2859f3a9182 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.44.9 + 1.4.44.10 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 6b3e1231eab2..a8b93d00800d 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.44 CFBundleVersion - 1.4.44.9 + 1.4.44.10 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 17e705c2bc8c..97748111bd78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.44-9", + "version": "1.4.44-10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.44-9", + "version": "1.4.44-10", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e6d643517a5e..94c89b24f1fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.44-9", + "version": "1.4.44-10", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From f9eb6839d410e27c69ee7c365c203c86913e3056 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 28 Feb 2024 15:38:53 +0000 Subject: [PATCH 188/213] Update version to 1.4.44-11 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f2bddfacf64e..90eed5dcc6e1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001044410 - versionName "1.4.44-10" + versionCode 1001044411 + versionName "1.4.44-11" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 45ab7d5c9fb4..b9fe0b79777d 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.44.10 + 1.4.44.11 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index c2859f3a9182..fc761f96dc06 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.44.10 + 1.4.44.11 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index a8b93d00800d..619ac43c4a02 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.44 CFBundleVersion - 1.4.44.10 + 1.4.44.11 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 97748111bd78..78dc8c8e45bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.44-10", + "version": "1.4.44-11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.44-10", + "version": "1.4.44-11", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 94c89b24f1fa..c8ee6c75f9dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.44-10", + "version": "1.4.44-11", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 610ce2667f7b4104ecb157a8c399249a15ff91cf Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:39:52 -0500 Subject: [PATCH 189/213] renamed rootReportName to reportNameon ParentNavigationSummaryParams type --- src/components/ParentNavigationSubtitle.tsx | 6 +++--- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- src/languages/types.ts | 2 +- src/libs/ReportUtils.ts | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/ParentNavigationSubtitle.tsx b/src/components/ParentNavigationSubtitle.tsx index 2816715dae2c..3109453ca6b0 100644 --- a/src/components/ParentNavigationSubtitle.tsx +++ b/src/components/ParentNavigationSubtitle.tsx @@ -21,7 +21,7 @@ type ParentNavigationSubtitleProps = { function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportID = '', pressableStyles}: ParentNavigationSubtitleProps) { const styles = useThemeStyles(); - const {workspaceName, rootReportName} = parentNavigationSubtitleData; + const {workspaceName, reportName} = parentNavigationSubtitleData; const {translate} = useLocalize(); @@ -30,7 +30,7 @@ function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportID onPress={() => { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(parentReportID)); }} - accessibilityLabel={translate('threads.parentNavigationSummary', {rootReportName, workspaceName})} + accessibilityLabel={translate('threads.parentNavigationSummary', {reportName, workspaceName})} role={CONST.ROLE.LINK} style={pressableStyles} > @@ -39,7 +39,7 @@ function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportID numberOfLines={1} > {`${translate('threads.from')} `} - {rootReportName} + {reportName} {Boolean(workspaceName) && {` ${translate('threads.in')} ${workspaceName}`}} diff --git a/src/languages/en.ts b/src/languages/en.ts index cf8823f5b2be..35b9dd9a18d1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2171,7 +2171,7 @@ export default { reply: 'Reply', from: 'From', in: 'in', - parentNavigationSummary: ({rootReportName, workspaceName}: ParentNavigationSummaryParams) => `From ${rootReportName}${workspaceName ? ` in ${workspaceName}` : ''}`, + parentNavigationSummary: ({reportName, workspaceName}: ParentNavigationSummaryParams) => `From ${reportName}${workspaceName ? ` in ${workspaceName}` : ''}`, }, qrCodes: { copy: 'Copy URL', diff --git a/src/languages/es.ts b/src/languages/es.ts index b3a8eef73d7c..398010caf656 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2659,7 +2659,7 @@ export default { reply: 'Respuesta', from: 'De', in: 'en', - parentNavigationSummary: ({rootReportName, workspaceName}: ParentNavigationSummaryParams) => `De ${rootReportName}${workspaceName ? ` en ${workspaceName}` : ''}`, + parentNavigationSummary: ({reportName, workspaceName}: ParentNavigationSummaryParams) => `De ${reportName}${workspaceName ? ` en ${workspaceName}` : ''}`, }, qrCodes: { copy: 'Copiar URL', diff --git a/src/languages/types.ts b/src/languages/types.ts index bb56d5c38cd3..ed82bcd99a92 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -195,7 +195,7 @@ type OOOEventSummaryFullDayParams = {summary: string; dayCount: number; date: st type OOOEventSummaryPartialDayParams = {summary: string; timePeriod: string; date: string}; -type ParentNavigationSummaryParams = {rootReportName?: string; workspaceName?: string}; +type ParentNavigationSummaryParams = {reportName?: string; workspaceName?: string}; type SetTheRequestParams = {valueName: string; newValueToDisplay: string}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 867e1e3f5a12..97b4d93790c7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2590,19 +2590,19 @@ function getParentNavigationSubtitle(report: OnyxEntry): ParentNavigatio if (isIOURequest(parentReport)) { return { - rootReportName: getReportName(parentReport), + reportName: getReportName(parentReport), }; } if (isExpenseRequest(parentReport)) { return { - rootReportName: getReportName(parentReport), + reportName: getReportName(parentReport), workspaceName: isIOUReport(parentReport) ? CONST.POLICY.OWNER_EMAIL_FAKE : getPolicyName(parentReport, true), }; } return { - rootReportName: getReportName(parentReport), + reportName: getReportName(parentReport), workspaceName: getPolicyName(parentReport, true), }; } From 928e44515211fad1f4f65ce3dc5604879bba5005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Wed, 28 Feb 2024 17:08:43 +0100 Subject: [PATCH 190/213] fix autoreporting optimistic data --- src/libs/actions/Policy.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index b9a2e8535b62..e1da946e81d8 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -392,6 +392,9 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { autoReporting: enabled, + harvesting: { + enabled: true, + }, pendingFields: {isAutoApprovalEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, }, }, @@ -403,7 +406,7 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { autoReporting: !enabled, - pendingFields: {isAutoApprovalEnabled: null}, + pendingFields: {isAutoApprovalEnabled: null, harvesting: null}, }, }, ]; @@ -413,7 +416,7 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - pendingFields: {isAutoApprovalEnabled: null}, + pendingFields: {isAutoApprovalEnabled: null, harvesting: null}, }, }, ]; From 3779e7746571f746cc3919060aec30760d669f3f Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 28 Feb 2024 10:47:30 -0700 Subject: [PATCH 191/213] Remove deprecated method and pass down parentReportAction --- src/pages/home/ReportScreen.js | 1 + src/pages/home/report/ReportActionsList.js | 5 +++++ src/pages/home/report/ReportActionsListItemRenderer.js | 10 +++++++--- src/pages/home/report/ReportActionsView.js | 4 ++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 7fba188dcedd..da5a8e4aae27 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -561,6 +561,7 @@ function ReportScreen({ ( Date: Wed, 28 Feb 2024 10:59:11 -0700 Subject: [PATCH 192/213] Fix lint --- src/pages/home/report/ReportActionsList.js | 3 ++- src/pages/home/report/ReportActionsListItemRenderer.js | 1 + src/pages/home/report/ReportActionsView.js | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 19bad7e61634..3ace2ebeb436 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -82,6 +82,7 @@ const defaultProps = { isLoadingNewerReportActions: false, ...withCurrentUserPersonalDetailsDefaultProps, policy: {}, + parentReportAction: {}, }; const VERTICAL_OFFSET_THRESHOLD = 200; @@ -426,7 +427,7 @@ function ReportActionsList({ shouldDisplayNewMarker={shouldDisplayNewMarker(reportAction, index)} /> ), - [report, linkedReportActionID, sortedReportActions, mostRecentIOUReportActionID, shouldHideThreadDividerLine, shouldDisplayNewMarker], + [report, linkedReportActionID, sortedReportActions, mostRecentIOUReportActionID, shouldHideThreadDividerLine, shouldDisplayNewMarker, parentReportAction], ); // Native mobile does not render updates flatlist the changes even though component did update called. diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index aa8ed9e71e2e..530480f326ed 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -41,6 +41,7 @@ const propTypes = { const defaultProps = { mostRecentIOUReportActionID: '', linkedReportActionID: '', + parentReportAction: {}, }; function ReportActionsListItemRenderer({ diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 810b752e63ff..91a8810e91ff 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -81,6 +81,7 @@ const defaultProps = { session: { authTokenType: '', }, + parentReportAction: {}, }; function ReportActionsView(props) { From 31ed26f141598a1ade00717425205cba787f3f00 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 29 Feb 2024 01:05:49 +0700 Subject: [PATCH 193/213] fix: Bank account icon is a green square --- src/pages/settings/Wallet/PaymentMethodList.tsx | 17 +++++++++++++++-- .../settings/Wallet/WalletPage/WalletPage.tsx | 8 ++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 9193074f96c8..0af2748a2d76 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -236,12 +236,25 @@ function PaymentMethodList({ (paymentMethod) => paymentMethod.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !isEmptyObject(paymentMethod.errors), ); } - combinedPaymentMethods = combinedPaymentMethods.map((paymentMethod) => { const isMethodActive = isPaymentMethodActive(actionPaymentMethodType, activePaymentMethodID, paymentMethod); return { ...paymentMethod, - onPress: (e: GestureResponderEvent) => onPress(e, paymentMethod.accountType, paymentMethod.accountData, paymentMethod.icon, paymentMethod.isDefault, paymentMethod.methodID), + onPress: (e: GestureResponderEvent) => + onPress( + e, + paymentMethod.accountType, + paymentMethod.accountData, + { + icon: paymentMethod.icon, + iconSize: paymentMethod.iconSize, + iconHeight: paymentMethod.iconHeight, + iconWidth: paymentMethod.iconWidth, + iconStyles: paymentMethod.iconStyles, + }, + paymentMethod.isDefault, + paymentMethod.methodID, + ), wrapperStyle: isMethodActive ? [StyleUtils.getButtonBackgroundColorStyle(CONST.BUTTON_STATES.PRESSED)] : null, disabled: paymentMethod.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, isMethodActive, diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index 13e2877d3ec7..c20a0d053998 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -344,7 +344,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi const isPopoverBottomMount = anchorPosition.anchorPositionTop === 0 || isSmallScreenWidth; const alertTextStyle = [styles.inlineSystemMessage, styles.flexShrink1]; const alertViewStyle = [styles.flexRow, styles.alignItemsCenter, styles.w100, styles.ph5]; - return ( <> {shouldShowEmptyState ? ( @@ -538,10 +537,15 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi {isPopoverBottomMount && ( )} {shouldShowMakeDefaultButton && ( From 3d32ca3fa229788e5bf50d62c15140ba919a69b6 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 29 Feb 2024 01:14:53 +0700 Subject: [PATCH 194/213] fix ts check --- src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index c20a0d053998..e42caa93f54c 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, RefObject} from 'react'; import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; -import type {GestureResponderEvent} from 'react-native'; +import type {GestureResponderEvent, ViewStyle} from 'react-native'; import {ActivityIndicator, Dimensions, ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu'; @@ -42,7 +42,13 @@ import type {WalletPageOnyxProps, WalletPageProps} from './types'; type FormattedSelectedPaymentMethod = { title: string; - icon?: IconAsset; + icon?: { + icon: IconAsset; + iconSize?: number; + iconHeight?: number; + iconWidth?: number; + iconStyles?: ViewStyle[]; + }; description?: string; type?: string; }; From c1737590faa50ca05e03a2f15548aa3462b5c754 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 28 Feb 2024 11:18:19 -0700 Subject: [PATCH 195/213] Update src/pages/home/report/ReportActionsListItemRenderer.js Co-authored-by: Rajat --- src/pages/home/report/ReportActionsListItemRenderer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index 530480f326ed..142be03517df 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -123,7 +123,6 @@ function ReportActionsListItemRenderer({ ], ); - console.debug('timddd', shouldDisplayParentAction); return shouldDisplayParentAction ? ( Date: Thu, 29 Feb 2024 01:28:59 +0700 Subject: [PATCH 196/213] fix ts check --- src/pages/settings/Wallet/PaymentMethodList.tsx | 11 +++++++++-- .../settings/Wallet/WalletPage/WalletPage.tsx | 16 ++++------------ src/pages/settings/Wallet/WalletPage/types.ts | 11 ++++++++++- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 0af2748a2d76..f76ea34bc424 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -37,6 +37,7 @@ import type PaymentMethod from '@src/types/onyx/PaymentMethod'; import type {FilterMethodPaymentType} from '@src/types/onyx/WalletTransfer'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; +import {FormattedSelectedPaymentMethodIcon} from './WalletPage/types'; type PaymentMethodListOnyxProps = { /** List of bank accounts */ @@ -99,7 +100,14 @@ 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?: FormattedSelectedPaymentMethodIcon, + isDefault?: boolean, + methodID?: number, + ) => void; }; type PaymentMethodItem = PaymentMethod & { @@ -247,7 +255,6 @@ function PaymentMethodList({ paymentMethod.accountData, { icon: paymentMethod.icon, - iconSize: paymentMethod.iconSize, iconHeight: paymentMethod.iconHeight, iconWidth: paymentMethod.iconWidth, iconStyles: paymentMethod.iconStyles, diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index e42caa93f54c..c73dcf941d26 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, RefObject} from 'react'; import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; -import type {GestureResponderEvent, ViewStyle} 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'; @@ -37,18 +37,11 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; 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 type {FormattedSelectedPaymentMethodIcon, WalletPageOnyxProps, WalletPageProps} from './types'; type FormattedSelectedPaymentMethod = { title: string; - icon?: { - icon: IconAsset; - iconSize?: number; - iconHeight?: number; - iconWidth?: number; - iconStyles?: ViewStyle[]; - }; + icon?: FormattedSelectedPaymentMethodIcon; description?: string; type?: string; }; @@ -157,7 +150,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi nativeEvent?: GestureResponderEvent | KeyboardEvent, accountType?: string, account?: AccountData, - icon?: IconAsset, + icon?: FormattedSelectedPaymentMethodIcon, isDefault?: boolean, methodID?: string | number, ) => { @@ -547,7 +540,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi iconHeight={paymentMethod.formattedSelectedPaymentMethod.icon?.iconHeight} iconWidth={paymentMethod.formattedSelectedPaymentMethod.icon?.iconWidth} iconStyles={paymentMethod.formattedSelectedPaymentMethod.icon?.iconStyles} - iconSize={paymentMethod.formattedSelectedPaymentMethod.icon?.iconSize} description={paymentMethod.formattedSelectedPaymentMethod.description} wrapperStyle={[styles.mb4, styles.ph5, styles.pv0]} interactive={false} diff --git a/src/pages/settings/Wallet/WalletPage/types.ts b/src/pages/settings/Wallet/WalletPage/types.ts index 0c3db1a2df65..937353b92759 100644 --- a/src/pages/settings/Wallet/WalletPage/types.ts +++ b/src/pages/settings/Wallet/WalletPage/types.ts @@ -1,5 +1,7 @@ +import {ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {BankAccountList, CardList, FundList, UserWallet, WalletTerms, WalletTransfer} from '@src/types/onyx'; +import IconAsset from '@src/types/utils/IconAsset'; type WalletPageOnyxProps = { /** Wallet balance transfer props */ @@ -28,4 +30,11 @@ type WalletPageProps = WalletPageOnyxProps & { shouldListenForResize?: boolean; }; -export type {WalletPageOnyxProps, WalletPageProps}; +type FormattedSelectedPaymentMethodIcon = { + icon: IconAsset; + iconHeight?: number; + iconWidth?: number; + iconStyles?: ViewStyle[]; +}; + +export type {WalletPageOnyxProps, WalletPageProps, FormattedSelectedPaymentMethodIcon}; From 056bc1d76a4daf3062911f0f9f22a8ff30dae1ac Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 29 Feb 2024 01:38:28 +0700 Subject: [PATCH 197/213] fix lint --- src/pages/settings/Wallet/PaymentMethodList.tsx | 3 +-- src/pages/settings/Wallet/WalletPage/types.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index f76ea34bc424..ea399677898e 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -36,8 +36,7 @@ import type {Errors} from '@src/types/onyx/OnyxCommon'; import type PaymentMethod from '@src/types/onyx/PaymentMethod'; import type {FilterMethodPaymentType} from '@src/types/onyx/WalletTransfer'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import type IconAsset from '@src/types/utils/IconAsset'; -import {FormattedSelectedPaymentMethodIcon} from './WalletPage/types'; +import type {FormattedSelectedPaymentMethodIcon} from './WalletPage/types'; type PaymentMethodListOnyxProps = { /** List of bank accounts */ diff --git a/src/pages/settings/Wallet/WalletPage/types.ts b/src/pages/settings/Wallet/WalletPage/types.ts index 937353b92759..7ad46a6b348f 100644 --- a/src/pages/settings/Wallet/WalletPage/types.ts +++ b/src/pages/settings/Wallet/WalletPage/types.ts @@ -1,7 +1,7 @@ -import {ViewStyle} from 'react-native'; +import type {ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {BankAccountList, CardList, FundList, UserWallet, WalletTerms, WalletTransfer} from '@src/types/onyx'; -import IconAsset from '@src/types/utils/IconAsset'; +import type IconAsset from '@src/types/utils/IconAsset'; type WalletPageOnyxProps = { /** Wallet balance transfer props */ From 815dae1a86a9880cc3f17aafb4bdf816c15ebeeb Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 28 Feb 2024 11:49:54 -0700 Subject: [PATCH 198/213] Remove extra whitespace --- src/pages/home/report/ReportActionsListItemRenderer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index 142be03517df..bc8e6a94359f 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -123,7 +123,6 @@ function ReportActionsListItemRenderer({ ], ); - return shouldDisplayParentAction ? ( Date: Wed, 28 Feb 2024 14:50:54 -0500 Subject: [PATCH 199/213] simplified getParentNavigationSubtitle logic --- src/libs/ReportUtils.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 97b4d93790c7..3eea36727ea0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2579,12 +2579,8 @@ function getChatRoomSubtitle(report: OnyxEntry): string | undefined { * Gets the parent navigation subtitle for the report */ function getParentNavigationSubtitle(report: OnyxEntry): ParentNavigationSummaryParams { - if (!isThread(report)) { - return {}; - } - - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - if (!parentReport) { + const parentReport = getParentReport(report); + if (isEmptyObject(parentReport)) { return {}; } @@ -2594,13 +2590,6 @@ function getParentNavigationSubtitle(report: OnyxEntry): ParentNavigatio }; } - if (isExpenseRequest(parentReport)) { - return { - reportName: getReportName(parentReport), - workspaceName: isIOUReport(parentReport) ? CONST.POLICY.OWNER_EMAIL_FAKE : getPolicyName(parentReport, true), - }; - } - return { reportName: getReportName(parentReport), workspaceName: getPolicyName(parentReport, true), From afac4f45b5790da4af173f4a6a0d8de09465e173 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 29 Feb 2024 03:06:37 +0700 Subject: [PATCH 200/213] fallback iconSize --- src/pages/settings/Wallet/PaymentMethodList.tsx | 7 ++++--- src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 7 +++++-- src/pages/settings/Wallet/WalletPage/types.ts | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index ea399677898e..556634269f5c 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -254,9 +254,10 @@ function PaymentMethodList({ paymentMethod.accountData, { icon: paymentMethod.icon, - iconHeight: paymentMethod.iconHeight, - iconWidth: paymentMethod.iconWidth, - iconStyles: paymentMethod.iconStyles, + iconHeight: paymentMethod?.iconHeight, + iconWidth: paymentMethod?.iconWidth, + iconStyles: paymentMethod?.iconStyles, + iconSize: paymentMethod?.iconSize, }, paymentMethod.isDefault, paymentMethod.methodID, diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index c73dcf941d26..c9e11b31c7d6 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -154,6 +154,7 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi isDefault?: boolean, methodID?: string | number, ) => { + console.log(icon); if (shouldShowAddPaymentMenu) { setShouldShowAddPaymentMenu(false); return; @@ -343,6 +344,8 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi const isPopoverBottomMount = anchorPosition.anchorPositionTop === 0 || isSmallScreenWidth; const alertTextStyle = [styles.inlineSystemMessage, styles.flexShrink1]; const alertViewStyle = [styles.flexRow, styles.alignItemsCenter, styles.w100, styles.ph5]; + + console.log(paymentMethod.formattedSelectedPaymentMethod.icon); return ( <> {shouldShowEmptyState ? ( @@ -537,8 +540,8 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi Date: Thu, 29 Feb 2024 03:13:20 +0700 Subject: [PATCH 201/213] remove console.log --- src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index c9e11b31c7d6..b9f49049d51a 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -154,7 +154,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi isDefault?: boolean, methodID?: string | number, ) => { - console.log(icon); if (shouldShowAddPaymentMenu) { setShouldShowAddPaymentMenu(false); return; @@ -345,7 +344,6 @@ function WalletPage({bankAccountList = {}, cardList = {}, fundList = {}, isLoadi const alertTextStyle = [styles.inlineSystemMessage, styles.flexShrink1]; const alertViewStyle = [styles.flexRow, styles.alignItemsCenter, styles.w100, styles.ph5]; - console.log(paymentMethod.formattedSelectedPaymentMethod.icon); return ( <> {shouldShowEmptyState ? ( From 663eb40fe11126d083df2b858872ba848fa0a1d3 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:28:30 -0500 Subject: [PATCH 202/213] removed unnecessary if block that was removing the policy name from header when it was not supposed to. --- src/libs/ReportUtils.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ee2dcf5d02d6..be75f6c28909 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2585,12 +2585,6 @@ function getParentNavigationSubtitle(report: OnyxEntry): ParentNavigatio return {}; } - if (isIOURequest(parentReport)) { - return { - reportName: getReportName(parentReport), - }; - } - return { reportName: getReportName(parentReport), workspaceName: getPolicyName(parentReport, true), From e6c5cc419d47cfc5f807fc532818b252b2302fce Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 28 Feb 2024 20:55:41 +0000 Subject: [PATCH 203/213] Update version to 1.4.44-12 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 90eed5dcc6e1..fb798c33d1a9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001044411 - versionName "1.4.44-11" + versionCode 1001044412 + versionName "1.4.44-12" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index b9fe0b79777d..f62dd79f5ce5 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.44.11 + 1.4.44.12 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index fc761f96dc06..ddbd21b2842a 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.44.11 + 1.4.44.12 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 619ac43c4a02..4cffc1f6179a 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.44 CFBundleVersion - 1.4.44.11 + 1.4.44.12 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 78dc8c8e45bc..32039aebb62f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.44-11", + "version": "1.4.44-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.44-11", + "version": "1.4.44-12", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c8ee6c75f9dd..b1b5dc67327f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.44-11", + "version": "1.4.44-12", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From aaf9d4cb8c4dad6f7fdbd7fd44e411eca0197bde Mon Sep 17 00:00:00 2001 From: Puneet Lath Date: Wed, 28 Feb 2024 16:30:59 -0500 Subject: [PATCH 204/213] Return BaseOptionsSelector.js to class component --- .../OptionsSelector/BaseOptionsSelector.js | 1087 ++++++++--------- 1 file changed, 528 insertions(+), 559 deletions(-) diff --git a/src/components/OptionsSelector/BaseOptionsSelector.js b/src/components/OptionsSelector/BaseOptionsSelector.js index 1fa63f181dd6..3fe4711a1292 100755 --- a/src/components/OptionsSelector/BaseOptionsSelector.js +++ b/src/components/OptionsSelector/BaseOptionsSelector.js @@ -1,8 +1,6 @@ -import {useIsFocused} from '@react-navigation/native'; import lodashGet from 'lodash/get'; -import lodashIsEqual from 'lodash/isEqual'; import PropTypes from 'prop-types'; -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {Component} from 'react'; import {ScrollView, View} from 'react-native'; import _ from 'underscore'; import ArrowKeyFocusManager from '@components/ArrowKeyFocusManager'; @@ -13,10 +11,11 @@ import OptionsList from '@components/OptionsList'; import ReferralProgramCTA from '@components/ReferralProgramCTA'; import ShowMoreButton from '@components/ShowMoreButton'; import TextInput from '@components/TextInput'; -import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; -import useLocalize from '@hooks/useLocalize'; -import usePrevious from '@hooks/usePrevious'; -import useThemeStyles from '@hooks/useThemeStyles'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import withNavigationFocus from '@components/withNavigationFocus'; +import withTheme, {withThemePropTypes} from '@components/withTheme'; +import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles'; +import compose from '@libs/compose'; import getPlatform from '@libs/getPlatform'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import setSelection from '@libs/setSelection'; @@ -36,6 +35,9 @@ const propTypes = { /** List styles for OptionsList */ listStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), + /** Whether navigation is focused */ + isFocused: PropTypes.bool.isRequired, + /** Whether referral CTA should be displayed */ shouldShowReferralCTA: PropTypes.bool, @@ -43,9 +45,13 @@ const propTypes = { referralContentType: PropTypes.string, ...optionsSelectorPropTypes, + ...withLocalizePropTypes, + ...withThemeStylesPropTypes, + ...withThemePropTypes, }; const defaultProps = { + shouldDelayFocus: false, shouldShowReferralCTA: false, referralContentType: CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND, safeAreaPaddingBottomStyle: {}, @@ -55,657 +61,620 @@ const defaultProps = { ...optionsSelectorDefaultProps, }; -function BaseOptionsSelector(props) { - const isFocused = useIsFocused(); - const {translate} = useLocalize(); - const themeStyles = useThemeStyles(); - - const getInitiallyFocusedIndex = useCallback( - (allOptions) => { - let defaultIndex; - if (props.shouldTextInputAppearBelowOptions) { - defaultIndex = allOptions.length; - } else if (props.focusedIndex >= 0) { - defaultIndex = props.focusedIndex; - } else { - defaultIndex = props.selectedOptions.length; +class BaseOptionsSelector extends Component { + constructor(props) { + super(props); + + this.updateFocusedIndex = this.updateFocusedIndex.bind(this); + this.scrollToIndex = this.scrollToIndex.bind(this); + this.selectRow = this.selectRow.bind(this); + this.selectFocusedOption = this.selectFocusedOption.bind(this); + this.addToSelection = this.addToSelection.bind(this); + this.updateSearchValue = this.updateSearchValue.bind(this); + this.incrementPage = this.incrementPage.bind(this); + this.sliceSections = this.sliceSections.bind(this); + this.calculateAllVisibleOptionsCount = this.calculateAllVisibleOptionsCount.bind(this); + this.handleFocusIn = this.handleFocusIn.bind(this); + this.handleFocusOut = this.handleFocusOut.bind(this); + this.debouncedUpdateSearchValue = _.debounce(this.updateSearchValue, CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME); + this.relatedTarget = null; + this.accessibilityRoles = _.values(CONST.ROLE); + this.isWebOrDesktop = [CONST.PLATFORM.DESKTOP, CONST.PLATFORM.WEB].includes(getPlatform()); + + const allOptions = this.flattenSections(); + const sections = this.sliceSections(); + const focusedIndex = this.getInitiallyFocusedIndex(allOptions); + this.focusedOption = allOptions[focusedIndex]; + + this.state = { + sections, + allOptions, + focusedIndex, + shouldDisableRowSelection: false, + errorMessage: '', + paginationPage: 1, + disableEnterShortCut: false, + value: '', + }; + } + + componentDidMount() { + this.subscribeToEnterShortcut(); + this.subscribeToCtrlEnterShortcut(); + this.subscribeActiveElement(); + + if (this.props.isFocused && this.props.autoFocus && this.textInput) { + this.focusTimeout = setTimeout(() => { + this.textInput.focus(); + }, CONST.ANIMATED_TRANSITION); + } + + this.scrollToIndex(this.props.selectedOptions.length ? 0 : this.state.focusedIndex, false); + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.disableEnterShortCut !== this.state.disableEnterShortCut) { + // Unregister the shortcut before registering a new one to avoid lingering shortcut listener + this.unsubscribeEnter(); + if (!this.state.disableEnterShortCut) { + this.subscribeToEnterShortcut(); } - if (_.isUndefined(props.initiallyFocusedOptionKey)) { - return defaultIndex; + } + + if (prevProps.isFocused !== this.props.isFocused) { + // Unregister the shortcut before registering a new one to avoid lingering shortcut listener + this.unSubscribeFromKeyboardShortcut(); + if (this.props.isFocused) { + this.subscribeToEnterShortcut(); + this.subscribeToCtrlEnterShortcut(); } + } - const indexOfInitiallyFocusedOption = _.findIndex(allOptions, (option) => option.keyForList === props.initiallyFocusedOptionKey); - - return indexOfInitiallyFocusedOption; - }, - [props.shouldTextInputAppearBelowOptions, props.initiallyFocusedOptionKey, props.selectedOptions.length, props.focusedIndex], - ); - - const isWebOrDesktop = [CONST.PLATFORM.DESKTOP, CONST.PLATFORM.WEB].includes(getPlatform()); - const accessibilityRoles = _.values(CONST.ROLE); - - const [disabledOptionsIndexes, setDisabledOptionsIndexes] = useState([]); - const [shouldDisableRowSelection, setShouldDisableRowSelection] = useState(false); - const [errorMessage, setErrorMessage] = useState(''); - const [value, setValue] = useState(''); - const [paginationPage, setPaginationPage] = useState(1); - const [disableEnterShortCut, setDisableEnterShortCut] = useState(false); - - const relatedTarget = useRef(null); - const listRef = useRef(); - const textInputRef = useRef(); - const enterSubscription = useRef(); - const CTRLEnterSubscription = useRef(); - const focusTimeout = useRef(); - const prevLocale = useRef(props.preferredLocale); - const prevPaginationPage = useRef(paginationPage); - const prevSelectedOptions = useRef(props.selectedOptions); - const prevValue = useRef(value); - const previousSections = usePrevious(props.sections); - - useImperativeHandle(props.forwardedRef, () => textInputRef.current); + // Screen coming back into focus, for example + // when doing Cmd+Shift+K, then Cmd+K, then Cmd+Shift+K. + // Only applies to platforms that support keyboard shortcuts + if (this.isWebOrDesktop && !prevProps.isFocused && this.props.isFocused && this.props.autoFocus && this.textInput) { + setTimeout(() => { + this.textInput.focus(); + }, CONST.ANIMATED_TRANSITION); + } - /** - * Flattens the sections into a single array of options. - * Each object in this array is enhanced to have: - * - * 1. A `sectionIndex`, which represents the index of the section it came from - * 2. An `index`, which represents the index of the option within the section it came from. - * - * @returns {Array} - */ - const flattenSections = useCallback(() => { - const calcAllOptions = []; - const calcDisabledOptionsIndexes = []; - let index = 0; - _.each(props.sections, (section, sectionIndex) => { - _.each(section.data, (option, optionIndex) => { - calcAllOptions.push({ - ...option, - sectionIndex, - index: optionIndex, - }); - if (section.isDisabled || option.isDisabled) { - calcDisabledOptionsIndexes.push(index); - } - index += 1; + if (prevState.paginationPage !== this.state.paginationPage) { + const newSections = this.sliceSections(); + + this.setState({ + sections: newSections, }); - }); + } - setDisabledOptionsIndexes(calcDisabledOptionsIndexes); - return calcAllOptions; - }, [props.sections]); + if (prevState.focusedIndex !== this.state.focusedIndex) { + this.focusedOption = this.state.allOptions[this.state.focusedIndex]; + } - /** - * Maps sections to render only allowed count of them per section. - * - * @returns {Object[]} - */ - const sliceSections = useCallback( - () => - _.map(props.sections, (section) => { - if (_.isEmpty(section.data)) { - return section; + if (_.isEqual(this.props.sections, prevProps.sections)) { + return; + } + + const newSections = this.sliceSections(); + const newOptions = this.flattenSections(); + + if (prevProps.preferredLocale !== this.props.preferredLocale) { + this.setState({ + sections: newSections, + allOptions: newOptions, + }); + return; + } + 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: 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 + if (this.props.selectedOptions.length !== prevProps.selectedOptions.length || (!!prevState.value && !this.state.value)) { + this.scrollToIndex(0); + return; } - const pagination = paginationPage || 1; + // Otherwise, scroll to the focused index (as long as it's in range) + if (this.state.allOptions.length <= this.state.focusedIndex || !isNewFocusedIndex) { + return; + } + this.scrollToIndex(this.state.focusedIndex); + }, + ); + } - return { - ...section, - data: section.data.slice(0, CONST.MAX_OPTIONS_SELECTOR_PAGE_LENGTH * pagination), - }; - }), - [paginationPage, props.sections], - ); + componentWillUnmount() { + if (this.focusTimeout) { + clearTimeout(this.focusTimeout); + } - // eslint-disable-next-line react-hooks/exhaustive-deps - const initialAllOptions = useMemo(() => flattenSections(), []); - const [sections, setSections] = useState(sliceSections()); - const [allOptions, setAllOptions] = useState(initialAllOptions); - const [focusedIndex, setFocusedIndex] = useState(getInitiallyFocusedIndex(initialAllOptions)); - const [focusedOption, setFocusedOption] = useState(allOptions[focusedIndex]); + this.unSubscribeFromKeyboardShortcut(); + } /** - * Completes the follow-up actions after a row is selected - * - * @param {Object} option - * @param {Object} ref - * @returns {Promise} + * @param {Array} allOptions + * @returns {Number} */ - const selectRow = useCallback( - (option, ref) => - new Promise((resolve) => { - if (props.shouldShowTextInput && props.shouldPreventDefaultFocusOnSelectRow) { - if (relatedTarget.current && ref === relatedTarget.current) { - textInputRef.current.focus(); - relatedTarget.current = null; - } - if (textInputRef.current.isFocused()) { - setSelection(textInputRef.current, 0, value.length); - } - } - const selectedOption = props.onSelectRow(option); - resolve(selectedOption); - - if (!props.canSelectMultipleOptions) { - return; - } + getInitiallyFocusedIndex(allOptions) { + let defaultIndex; + if (this.props.shouldTextInputAppearBelowOptions) { + defaultIndex = allOptions.length; + } else if (this.props.focusedIndex >= 0) { + defaultIndex = this.props.focusedIndex; + } else { + defaultIndex = this.props.selectedOptions.length; + } + if (_.isUndefined(this.props.initiallyFocusedOptionKey)) { + return defaultIndex; + } - // Focus the first unselected item from the list (i.e: the best result according to the current search term) - setFocusedIndex(props.selectedOptions.length); - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [props.shouldShowTextInput, props.shouldPreventDefaultFocusOnSelectRow, value.length, props.canSelectMultipleOptions, props.selectedOptions.length], - ); + const indexOfInitiallyFocusedOption = _.findIndex(allOptions, (option) => option.keyForList === this.props.initiallyFocusedOptionKey); - const selectFocusedOption = useCallback( - (e) => { - const focusedItemKey = lodashGet(e, ['target', 'attributes', 'id', 'value']); - const localFocusedOption = focusedItemKey ? _.find(allOptions, (option) => option.keyForList === focusedItemKey) : allOptions[focusedIndex]; + return indexOfInitiallyFocusedOption; + } - if (!localFocusedOption || !isFocused) { - return; + /** + * Maps sections to render only allowed count of them per section. + * + * @returns {Objects[]} + */ + sliceSections() { + return _.map(this.props.sections, (section) => { + if (_.isEmpty(section.data)) { + return section; } - if (props.canSelectMultipleOptions) { - selectRow(localFocusedOption); - } else if (!shouldDisableRowSelection) { - setShouldDisableRowSelection(true); + return { + ...section, + data: section.data.slice(0, CONST.MAX_OPTIONS_SELECTOR_PAGE_LENGTH * lodashGet(this.state, 'paginationPage', 1)), + }; + }); + } - let result = selectRow(localFocusedOption); - if (!(result instanceof Promise)) { - result = Promise.resolve(); - } + /** + * Calculates all currently visible options based on the sections that are currently being shown + * and the number of items of those sections. + * + * @returns {Number} + */ + calculateAllVisibleOptionsCount() { + let count = 0; - setTimeout(() => { - result.finally(() => { - setShouldDisableRowSelection(false); - }); - }, 500); - } - }, - [props.canSelectMultipleOptions, focusedIndex, allOptions, isFocused, selectRow, shouldDisableRowSelection], - ); + _.forEach(this.state.sections, (section) => { + count += lodashGet(section, 'data.length', 0); + }); + + return count; + } + + updateSearchValue(value) { + this.setState({ + paginationPage: 1, + errorMessage: value.length > this.props.maxLength ? ['common.error.characterLimitExceedCounter', {length: value.length, limit: this.props.maxLength}] : '', + value, + }); + + this.props.onChangeText(value); + } - const handleFocusIn = () => { + handleFocusIn() { const activeElement = document.activeElement; - setDisableEnterShortCut(activeElement && accessibilityRoles.includes(activeElement.role) && activeElement.role !== CONST.ROLE.PRESENTATION); - }; + this.setState({ + disableEnterShortCut: activeElement && this.accessibilityRoles.includes(activeElement.role) && activeElement.role !== CONST.ROLE.PRESENTATION, + }); + } - const handleFocusOut = () => { - setDisableEnterShortCut(false); - }; + handleFocusOut() { + this.setState({ + disableEnterShortCut: false, + }); + } - const subscribeActiveElement = () => { - if (!isWebOrDesktop) { + subscribeActiveElement() { + if (!this.isWebOrDesktop) { return; } - document.addEventListener('focusin', handleFocusIn); - document.addEventListener('focusout', handleFocusOut); - }; + document.addEventListener('focusin', this.handleFocusIn); + document.addEventListener('focusout', this.handleFocusOut); + } - const subscribeToEnterShortcut = () => { + unSubscribeActiveElement() { + if (!this.isWebOrDesktop) { + return; + } + document.removeEventListener('focusin', this.handleFocusIn); + document.removeEventListener('focusout', this.handleFocusOut); + } + + subscribeToEnterShortcut() { const enterConfig = CONST.KEYBOARD_SHORTCUTS.ENTER; - enterSubscription.current = KeyboardShortcut.subscribe( + this.unsubscribeEnter = KeyboardShortcut.subscribe( enterConfig.shortcutKey, - selectFocusedOption, + this.selectFocusedOption, enterConfig.descriptionKey, enterConfig.modifiers, true, - () => !allOptions[focusedIndex], + () => !this.state.allOptions[this.state.focusedIndex], ); - }; + } - const subscribeToCtrlEnterShortcut = () => { + subscribeToCtrlEnterShortcut() { const CTRLEnterConfig = CONST.KEYBOARD_SHORTCUTS.CTRL_ENTER; - CTRLEnterSubscription.current = KeyboardShortcut.subscribe( + this.unsubscribeCTRLEnter = KeyboardShortcut.subscribe( CTRLEnterConfig.shortcutKey, () => { - if (props.canSelectMultipleOptions) { - props.onConfirmSelection(); + if (this.props.canSelectMultipleOptions) { + this.props.onConfirmSelection(); return; } - const localFocusedOption = allOptions[focusedIndex]; - if (!localFocusedOption) { + const focusedOption = this.state.allOptions[this.state.focusedIndex]; + if (!focusedOption) { return; } - selectRow(localFocusedOption); + this.selectRow(focusedOption); }, CTRLEnterConfig.descriptionKey, CTRLEnterConfig.modifiers, true, ); - }; + } - const unSubscribeFromKeyboardShortcut = () => { - if (enterSubscription.current) { - enterSubscription.current(); + unSubscribeFromKeyboardShortcut() { + if (this.unsubscribeEnter) { + this.unsubscribeEnter(); } - if (CTRLEnterSubscription.current) { - CTRLEnterSubscription.current(); + if (this.unsubscribeCTRLEnter) { + this.unsubscribeCTRLEnter(); } - }; + } - const selectOptions = useCallback(() => { - if (props.canSelectMultipleOptions) { - props.onConfirmSelection(); - return; - } + selectFocusedOption(e) { + const focusedItemKey = lodashGet(e, ['target', 'attributes', 'id', 'value']); + const focusedOption = focusedItemKey ? _.find(this.state.allOptions, (option) => option.keyForList === focusedItemKey) : this.state.allOptions[this.state.focusedIndex]; - const localFocusedOption = allOptions[focusedIndex]; - if (!localFocusedOption) { + if (!focusedOption || !this.props.isFocused) { return; } - selectRow(localFocusedOption); - // we don't need to include the whole props object as the dependency - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [allOptions, focusedIndex, props.canSelectMultipleOptions, props.onConfirmSelection, selectRow]); - - useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ENTER, selectFocusedOption, { - shouldBubble: !allOptions[focusedIndex], - captureOnInputs: true, - }); - useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.CTRL_ENTER, selectOptions, {captureOnInputs: true}); - - /** - * Scrolls to the focused index within the SectionList - * - * @param {Number} index - * @param {Boolean} animated - */ - const scrollToIndex = useCallback( - (index, animated = true) => { - const option = allOptions[index]; - if (!listRef.current || !option) { - return; - } - - const itemIndex = option.index; - const sectionIndex = option.sectionIndex; - - if (!lodashGet(sections, `[${sectionIndex}].data[${itemIndex}]`, null)) { - return; - } - - // Note: react-native's SectionList automatically strips out any empty sections. - // So we need to reduce the sectionIndex to remove any empty sections in front of the one we're trying to scroll to. - // Otherwise, it will cause an index-out-of-bounds error and crash the app. - let adjustedSectionIndex = sectionIndex; - for (let i = 0; i < sectionIndex; i++) { - if (_.isEmpty(lodashGet(sections, `[${i}].data`))) { - adjustedSectionIndex--; - } - } - - listRef.current.scrollToLocation({sectionIndex: adjustedSectionIndex, itemIndex, animated}); - }, - [allOptions, sections], - ); - - useEffect(() => { - subscribeToEnterShortcut(); - subscribeToCtrlEnterShortcut(); - subscribeActiveElement(); - - if (props.isFocused && props.autoFocus && textInputRef.current) { - focusTimeout.current = setTimeout(() => { - textInputRef.current.focus(); - }, CONST.ANIMATED_TRANSITION); - } - - scrollToIndex(props.selectedOptions.length ? 0 : focusedIndex, false); + if (this.props.canSelectMultipleOptions) { + this.selectRow(focusedOption); + } else if (!this.state.shouldDisableRowSelection) { + this.setState({shouldDisableRowSelection: true}); - return () => { - if (focusTimeout.current) { - clearTimeout(focusTimeout.current); + let result = this.selectRow(focusedOption); + if (!(result instanceof Promise)) { + result = Promise.resolve(); } - unSubscribeFromKeyboardShortcut(); - }; - // we want to run this effect only once, when the component is mounted - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - // Unregister the shortcut before registering a new one to avoid lingering shortcut listener - enterSubscription.current(); - if (!disableEnterShortCut) { - subscribeToEnterShortcut(); + setTimeout(() => { + result.finally(() => { + this.setState({shouldDisableRowSelection: false}); + }); + }, 500); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disableEnterShortCut]); + } - useEffect(() => { - if (props.isFocused) { - subscribeToEnterShortcut(); - subscribeToCtrlEnterShortcut(); - } else { - unSubscribeFromKeyboardShortcut(); + focus() { + if (!this.textInput) { + return; } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.isFocused]); - - useEffect(() => { - const newSections = sliceSections(); - if (prevPaginationPage.current !== paginationPage) { - prevPaginationPage.current = paginationPage; - setSections(newSections); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [paginationPage]); + this.textInput.focus(); + } - useEffect(() => { - setFocusedOption(allOptions[focusedIndex]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [focusedIndex]); + /** + * Flattens the sections into a single array of options. + * Each object in this array is enhanced to have: + * + * 1. A `sectionIndex`, which represents the index of the section it came from + * 2. An `index`, which represents the index of the option within the section it came from. + * + * @returns {Array} + */ + flattenSections() { + const allOptions = []; + this.disabledOptionsIndexes = []; + let index = 0; + _.each(this.props.sections, (section, sectionIndex) => { + _.each(section.data, (option, optionIndex) => { + allOptions.push({ + ...option, + sectionIndex, + index: optionIndex, + }); + if (section.isDisabled || option.isDisabled) { + this.disabledOptionsIndexes.push(index); + } + index += 1; + }); + }); + return allOptions; + } - // eslint-disable-next-line rulesdir/prefer-early-return - useEffect(() => { - // Screen coming back into focus, for example - // when doing Cmd+Shift+K, then Cmd+K, then Cmd+Shift+K. - // Only applies to platforms that support keyboard shortcuts - if (isWebOrDesktop && isFocused && props.autoFocus && textInputRef.current) { - setTimeout(() => { - textInputRef.current.focus(); - }, CONST.ANIMATED_TRANSITION); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isFocused, props.autoFocus]); + /** + * @param {Number} index + */ + updateFocusedIndex(index) { + this.setState({focusedIndex: index}, () => this.scrollToIndex(index)); + } - useEffect(() => { - if (lodashIsEqual(props.sections, previousSections)) { + /** + * Scrolls to the focused index within the SectionList + * + * @param {Number} index + * @param {Boolean} animated + */ + scrollToIndex(index, animated = true) { + const option = this.state.allOptions[index]; + if (!this.list || !option) { return; } - const newSections = sliceSections(); - const newOptions = flattenSections(); + const itemIndex = option.index; + const sectionIndex = option.sectionIndex; - if (prevLocale.current !== props.preferredLocale) { - prevLocale.current = props.preferredLocale; - setAllOptions(newOptions); - setSections(newSections); + if (!lodashGet(this.state.sections, `[${sectionIndex}].data[${itemIndex}]`, null)) { return; } - const newFocusedIndex = props.selectedOptions.length; - const prevFocusedOption = _.find(newOptions, (option) => focusedOption && option.keyForList === focusedOption.keyForList); - const prevFocusedOptionIndex = prevFocusedOption ? _.findIndex(newOptions, (option) => focusedOption && option.keyForList === focusedOption.keyForList) : undefined; - - setSections(newSections); - setAllOptions(newOptions); - setFocusedIndex(prevFocusedOptionIndex || (_.isNumber(props.focusedIndex) ? props.focusedIndex : newFocusedIndex)); - // we want to run this effect only when the sections change - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.sections, previousSections]); - - useEffect(() => { - // If we just toggled an option on a multi-selection page or cleared the search input, scroll to top - if (props.selectedOptions.length !== prevSelectedOptions.current.length || (!!prevValue.current && !value)) { - prevSelectedOptions.current = props.selectedOptions; - prevValue.current = value; - scrollToIndex(0); - return; - } - - // Otherwise, scroll to the focused index (as long as it's in range) - if (allOptions.length <= focusedIndex) { - return; - } - scrollToIndex(focusedIndex); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [allOptions.length, focusedIndex, props.focusedIndex, props.selectedOptions, value]); - - const updateSearchValue = useCallback( - (searchValue) => { - setValue(searchValue); - setErrorMessage( - searchValue.length > props.maxLength - ? translate('common.error.characterLimitExceedCounter', { - length: searchValue.length, - limit: props.maxLength, - }) - : '', - ); - props.onChangeText(searchValue); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [props.onChangeText, props.maxLength, translate], - ); - - const debouncedUpdateSearchValue = _.debounce(updateSearchValue, CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME); + this.list.scrollToLocation({sectionIndex, itemIndex, animated}); + } /** - * Calculates all currently visible options based on the sections that are currently being shown - * and the number of items of those sections. + * Completes the follow-up actions after a row is selected * - * @returns {Number} + * @param {Object} option + * @param {Object} ref + * @returns {Promise} */ - const calculateAllVisibleOptionsCount = useCallback(() => { - let count = 0; - - _.forEach(sections, (section) => { - count += lodashGet(section, 'data.length', 0); - }); + selectRow(option, ref) { + return new Promise((resolve) => { + if (this.props.shouldShowTextInput && this.props.shouldPreventDefaultFocusOnSelectRow) { + if (this.relatedTarget && ref === this.relatedTarget) { + this.textInput.focus(); + this.relatedTarget = null; + } + if (this.textInput.isFocused()) { + setSelection(this.textInput, 0, this.state.value.length); + } + } + const selectedOption = this.props.onSelectRow(option); + resolve(selectedOption); - return count; - }, [sections]); + if (!this.props.canSelectMultipleOptions) { + return; + } - /** - * @param {Number} index - */ - const updateFocusedIndex = useCallback((index) => { - setFocusedIndex(index); - }, []); + // Focus the first unselected item from the list (i.e: the best result according to the current search term) + this.setState({ + focusedIndex: this.props.selectedOptions.length, + }); + }); + } /** * Completes the follow-up action after clicking on multiple select button * @param {Object} option */ - const addToSelection = useCallback( - (option) => { - if (props.shouldShowTextInput && props.shouldPreventDefaultFocusOnSelectRow) { - textInputRef.current.focus(); - if (textInputRef.current.isFocused()) { - setSelection(textInputRef.current, 0, value.length); - } + addToSelection(option) { + if (this.props.shouldShowTextInput && this.props.shouldPreventDefaultFocusOnSelectRow) { + this.textInput.focus(); + if (this.textInput.isFocused()) { + setSelection(this.textInput, 0, this.state.value.length); } - props.onAddToSelection(option); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [props.onAddToSelection, props.shouldShowTextInput, props.shouldPreventDefaultFocusOnSelectRow, value.length], - ); + } + this.props.onAddToSelection(option); + } /** * Increments a pagination page to show more items */ - const incrementPage = useCallback(() => { - setPaginationPage((prev) => prev + 1); - }, []); - - const shouldShowShowMoreButton = allOptions.length > CONST.MAX_OPTIONS_SELECTOR_PAGE_LENGTH * paginationPage; - const shouldShowFooter = !props.isReadOnly && (props.shouldShowConfirmButton || props.footerContent) && !(props.canSelectMultipleOptions && _.isEmpty(props.selectedOptions)); - const defaultConfirmButtonText = _.isUndefined(props.confirmButtonText) ? translate('common.confirm') : props.confirmButtonText; - const shouldShowDefaultConfirmButton = !props.footerContent && defaultConfirmButtonText; - const safeAreaPaddingBottomStyle = shouldShowFooter ? undefined : props.safeAreaPaddingBottomStyle; - const listContainerStyles = props.listContainerStyles || [themeStyles.flex1]; - const optionHoveredStyle = props.optionHoveredStyle || themeStyles.hoveredComponentBG; - - const textInput = ( - { - if (!props.shouldPreventDefaultFocusOnSelectRow) { - return; - } - relatedTarget.current = e.relatedTarget; - }} - selectTextOnFocus - blurOnSubmit={Boolean(allOptions.length)} - spellCheck={false} - shouldInterceptSwipe={props.shouldTextInputInterceptSwipe} - isLoading={props.isLoadingNewOptions} - iconLeft={props.textIconLeft} - testID="options-selector-input" - /> - ); - const optionsList = ( - { - if (props.selectedOptions.length === 0) { - scrollToIndex(focusedIndex, false); - } + incrementPage() { + this.setState((prev) => ({ + paginationPage: prev.paginationPage + 1, + })); + } + + render() { + const shouldShowShowMoreButton = this.state.allOptions.length > CONST.MAX_OPTIONS_SELECTOR_PAGE_LENGTH * this.state.paginationPage; + const shouldShowFooter = + !this.props.isReadOnly && (this.props.shouldShowConfirmButton || this.props.footerContent) && !(this.props.canSelectMultipleOptions && _.isEmpty(this.props.selectedOptions)); + const defaultConfirmButtonText = _.isUndefined(this.props.confirmButtonText) ? this.props.translate('common.confirm') : this.props.confirmButtonText; + const shouldShowDefaultConfirmButton = !this.props.footerContent && defaultConfirmButtonText; + const safeAreaPaddingBottomStyle = shouldShowFooter ? undefined : this.props.safeAreaPaddingBottomStyle; + const listContainerStyles = this.props.listContainerStyles || [this.props.themeStyles.flex1]; + const optionHoveredStyle = this.props.optionHoveredStyle || this.props.themeStyles.hoveredComponentBG; + + const textInput = ( + (this.textInput = el)} + label={this.props.textInputLabel} + accessibilityLabel={this.props.textInputLabel} + role={CONST.ROLE.PRESENTATION} + onChangeText={this.debouncedUpdateSearchValue} + errorText={this.state.errorMessage} + onSubmitEditing={this.selectFocusedOption} + placeholder={this.props.placeholderText} + maxLength={this.props.maxLength + CONST.ADDITIONAL_ALLOWED_CHARACTERS} + keyboardType={this.props.keyboardType} + onBlur={(e) => { + if (!this.props.shouldPreventDefaultFocusOnSelectRow) { + return; + } + this.relatedTarget = e.relatedTarget; + }} + selectTextOnFocus + blurOnSubmit={Boolean(this.state.allOptions.length)} + spellCheck={false} + shouldInterceptSwipe={this.props.shouldTextInputInterceptSwipe} + isLoading={this.props.isLoadingNewOptions} + iconLeft={this.props.textIconLeft} + testID="options-selector-input" + /> + ); + const optionsList = ( + (this.list = el)} + optionHoveredStyle={optionHoveredStyle} + onSelectRow={this.props.onSelectRow ? this.selectRow : undefined} + sections={this.state.sections} + focusedIndex={this.state.focusedIndex} + disableFocusOptions={this.props.disableFocusOptions} + selectedOptions={this.props.selectedOptions} + canSelectMultipleOptions={this.props.canSelectMultipleOptions} + shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton} + multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText} + onAddToSelection={this.addToSelection} + hideSectionHeaders={this.props.hideSectionHeaders} + headerMessage={this.state.errorMessage ? '' : this.props.headerMessage} + boldStyle={this.props.boldStyle} + showTitleTooltip={this.props.showTitleTooltip} + isDisabled={this.props.isDisabled} + shouldHaveOptionSeparator={this.props.shouldHaveOptionSeparator} + highlightSelectedOptions={this.props.highlightSelectedOptions} + onLayout={() => { + if (this.props.selectedOptions.length === 0) { + this.scrollToIndex(this.state.focusedIndex, false); + } - if (props.onLayout) { - props.onLayout(); + if (this.props.onLayout) { + this.props.onLayout(); + } + }} + contentContainerStyles={[safeAreaPaddingBottomStyle, ...this.props.contentContainerStyles]} + sectionHeaderStyle={this.props.sectionHeaderStyle} + listContainerStyles={listContainerStyles} + listStyles={this.props.listStyles} + isLoading={!this.props.shouldShowOptions} + showScrollIndicator={this.props.showScrollIndicator} + isRowMultilineSupported={this.props.isRowMultilineSupported} + isLoadingNewOptions={this.props.isLoadingNewOptions} + shouldPreventDefaultFocusOnSelectRow={this.props.shouldPreventDefaultFocusOnSelectRow} + nestedScrollEnabled={this.props.nestedScrollEnabled} + bounces={!this.props.shouldTextInputAppearBelowOptions || !this.props.shouldAllowScrollingChildren} + renderFooterContent={ + shouldShowShowMoreButton && ( + + ) } - }} - contentContainerStyles={[safeAreaPaddingBottomStyle, ...props.contentContainerStyles]} - sectionHeaderStyle={props.sectionHeaderStyle} - listContainerStyles={listContainerStyles} - listStyles={props.listStyles} - isLoading={!props.shouldShowOptions} - showScrollIndicator={props.showScrollIndicator} - isRowMultilineSupported={props.isRowMultilineSupported} - isLoadingNewOptions={props.isLoadingNewOptions} - shouldPreventDefaultFocusOnSelectRow={props.shouldPreventDefaultFocusOnSelectRow} - nestedScrollEnabled={props.nestedScrollEnabled} - bounces={!props.shouldTextInputAppearBelowOptions || !props.shouldAllowScrollingChildren} - renderFooterContent={() => - shouldShowShowMoreButton && ( - - ) - } - /> - ); - - const optionsAndInputsBelowThem = ( - <> - {optionsList} - - {props.children} - {props.shouldShowTextInput && textInput} - - - ); - - return ( - {} : updateFocusedIndex} - shouldResetIndexOnEndReached={false} - > - - {/* - * The OptionsList component uses a SectionList which uses a VirtualizedList internally. - * VirtualizedList cannot be directly nested within ScrollViews of the same orientation. - * To work around this, we wrap the OptionsList component with a horizontal ScrollView. - */} - {props.shouldTextInputAppearBelowOptions && props.shouldAllowScrollingChildren && ( - - - {optionsAndInputsBelowThem} + /> + ); + + const optionsAndInputsBelowThem = ( + <> + + {optionsList} + + + {this.props.children} + {this.props.shouldShowTextInput && textInput} + + + ); + + return ( + {} : this.updateFocusedIndex} + shouldResetIndexOnEndReached={false} + > + + {/* + * The OptionsList component uses a SectionList which uses a VirtualizedList internally. + * VirtualizedList cannot be directly nested within ScrollViews of the same orientation. + * To work around this, we wrap the OptionsList component with a horizontal ScrollView. + */} + {this.props.shouldTextInputAppearBelowOptions && this.props.shouldAllowScrollingChildren && ( + + + {optionsAndInputsBelowThem} + - + )} + + {this.props.shouldTextInputAppearBelowOptions && !this.props.shouldAllowScrollingChildren && optionsAndInputsBelowThem} + + {!this.props.shouldTextInputAppearBelowOptions && ( + <> + + {this.props.children} + {this.props.shouldShowTextInput && textInput} + {Boolean(this.props.textInputAlert) && ( + + )} + + {optionsList} + + )} + + {this.props.shouldShowReferralCTA && ( + + + )} - {props.shouldTextInputAppearBelowOptions && !props.shouldAllowScrollingChildren && optionsAndInputsBelowThem} - - {!props.shouldTextInputAppearBelowOptions && ( - <> - - {props.children} - {props.shouldShowTextInput && textInput} - {Boolean(props.textInputAlert) && ( - - )} - - {optionsList} - + {shouldShowFooter && ( + + {shouldShowDefaultConfirmButton && ( +

      ^UEQLRrhOK&h3#Zuf+Mep9P>1okDN-X7blO zn~VRAWi&`%%(l|bky>H47nkH;IsA^hG_s}NEOWodb0lkGrLPk_m7*|P{y4qPDhk(| zE6^EE3@mAbr{bF)qN@?YI{j@g9F@4Wz~QRBZ^b~Ot)c1;LVL3FM{_WbkMw)k4~ZtBscg z!U~kL4~rrHiE*99lQ%6l1lfw8c10q#<>9cmY4DhJg<~cZMJRDt{5+3pjrKC4?9W>c z&b>CY9&ZUIe|kA#!ho`GIdMajFX2KxL~j{6Hide?Hi9@FXN#LouzH>baRog)gIIWP z-wO{dM`>w;iTMwu56HWyjb=#o^JWhvh4%#o2^C>@?RVEMYUj_3m$=tZ+79KmjKY3} zj+b!RjmJLa@cAbViz`4$*f9XqEiRM+H)=BUINy2NkoETIWvZ2Kr@2oI!#a=8zkJNO z5vcV3`iwXld7<8ow>{1|SBHLiK>hB@f0ki-lHoFK$J^wV!cRnFtY$@s0vuvb3NzIu zC(bluLiBJ6KxnNnY}9cY-B~Bmd(Sj?Pm11Rc&C{4l)}Ri_EJtR1)7FaNvVzt{}(}) zi|H~%$epZGVOVK-`s3BDvRRyvLKTR99ClqKGD58C-Y5To<)Sz~KXeMX`mqKW^*c4cyfGpY-OKr4u!-uD6HBrOP~uaijsstJmLD zoNW7=sX+m3%5z@G=6DbB{BY$BL!_zJ3q}Iq8s)J`J{;WCZnDdSTz4qOCYhAN+LzC? zHvZ_}V;bdSY;F5SA;$gm`~kNWQ=StwqWy0Pv}s#C8VQsZoY2DxhL;!ssk*G+&c7-@ z0nb!_yFB|^79f1Ko8`TBKN~AJ^D&GCpfccDrBrA0Uo&qvD&V3><@|{oxxZXT&-oZl?qedI{eA*$1P_&1;yd?-T!Qq$Ov1+qUs{3DUN!s_j|aPz|^ zk^0mi`-a#wQ${gV)3K|3h^qBd-@++FyaV_5kzf)cLr&@bCoT>@ysA5Ov92qP;$m|3 zG6?l}oZ3mN!k-t&-(aQ25_^_48uowA%zZ=t8HIt>Z&S_u;;*pa13R!K!*VPybd`Nf z@4TYI&aHvBX1n4q@&g71{CueF2f?u76=sIu0VVO>pkyunGUnP7^_$p_AC~Tc1Baigjaik7A$%BiJB-m-HOlW zS6Qjy`d&R%cYLAzGAFcxZEmoC2i=BTHb^YSE>1R{qY?0STprRs1VdX@JOU?Z(WXBU~It|4i?a8TuhZF~_zK zpYYo}x)ZZ;1e{@n3>p1L<(Ed{Z*&_pK5*@9!6*yB7$Hl_17@(_X}#Kz+Qpfs^#Z;m(*tZu$ODlV;ofy36_LChFJ5n-;tZt%^^`_NAiA)>8>&!?IC?1 zK?!JXZf#`6m&ia6-SXd0YBENo;jrtbFVH|9KGv16=l4a{=M{V;aOzQMVe)|I5|ivrgKbU2IU;d z%Gg1|Hhctxiq_?Uy$n>7VyHkx5h^2V62v_?y zsQWyvyeT{qINgL(rRQn&FaX}6DUY;a?UR9q+8`q%-nBTaTHVJip2s1#Ir@o+WsAWz z2}%oHdxC|CQcEQjjC5@n(YIO%L_}I?d8%#hIwYw#3rS)g(%+wt1RFCq zRlEf>hn2>(6Dup&qi^Qxnb8I$cJAwQ!LJ%=p|bx^6L`#{3NF_}-(hhkQ!&Fs z@@!&c2eb`q?BZmf$p(NM#_LEjk|skU_Zj4zkDX8Hl{)_YRc^;!B9FLT&ueaRr{npz z^_<(S#8OG&(s-KPGhsO(Zzw!otbQ_h(?!hGtjykZ|8Akybgu*Nx*GQSn_bLPFMT|} z_|vs1ljFX(@8WpV72n6Oi#+0NNT<Sy3*nxm<}Mr%-MyMdEp@ByDvLP z%0A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 ^XI=bf!&Pp( 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Ⓢ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