From 229a4331aeddc934748757b76b226361f93ce6dc Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Thu, 2 Nov 2023 21:37:02 -0400 Subject: [PATCH 01/51] update subsribe on leave --- src/libs/actions/Report.js | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 1de15c1184cb..5ddb2355f1c9 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2059,6 +2059,32 @@ function leaveRoom(reportID, isWorkspaceMemberLeavingWorkspaceRoom = false) { }, ]; + const failureData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: report, + }, + ]; + + if (report.parentReportID && report.parentReportActionID) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, + value: {[report.parentReportActionID]: {childReportNotificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN}}, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, + value: {[report.parentReportActionID]: {childReportNotificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN}}, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, + value: {[parentReportActionID]: {childReportNotificationPreference: report.notificationPreference}}, + }); + } + API.write( 'LeaveRoom', { @@ -2067,13 +2093,7 @@ function leaveRoom(reportID, isWorkspaceMemberLeavingWorkspaceRoom = false) { { optimisticData, successData, - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: report, - }, - ], + failureData, }, ); From 1e060e2bc927673cb8988e9da5e97bb9c7a728f4 Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Thu, 9 Nov 2023 04:35:12 -0500 Subject: [PATCH 02/51] fix variable names --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 27396c875d34..638c83862cfc 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2085,7 +2085,7 @@ function leaveRoom(reportID, isWorkspaceMemberLeavingWorkspaceRoom = false) { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, - value: {[parentReportActionID]: {childReportNotificationPreference: report.notificationPreference}}, + value: {[report.parentReportActionID]: {childReportNotificationPreference: report.notificationPreference}}, }); } From 010bc09c7b71bdf89808c3df7a58a4f6a516f7bb Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Wed, 13 Dec 2023 19:23:35 +0530 Subject: [PATCH 03/51] fix duplicate endpoints --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/pages/iou/request/step/IOURequestStepDistance.js | 10 +++++++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index c4a481cb71c0..8b860372d730 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -612,6 +612,7 @@ export default { genericDeleteFailureMessage: 'Unexpected error deleting the money request, please try again later', genericEditFailureMessage: 'Unexpected error editing the money request, please try again later', genericSmartscanFailureMessage: 'Transaction is missing fields', + duplicateWaypointsErrorMessage: 'Please remove duplicate waypoints', atLeastTwoDifferentWaypoints: 'Please enter at least two different addresses', splitBillMultipleParticipantsErrorMessage: 'Split bill is only allowed between a single workspace or individual users. Please update your selection.', }, diff --git a/src/languages/es.ts b/src/languages/es.ts index a91a8768a3ee..b0bc725351aa 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -607,6 +607,7 @@ export default { genericDeleteFailureMessage: 'Error inesperado eliminando la solicitud de dinero. Por favor, inténtalo más tarde', genericEditFailureMessage: 'Error inesperado al guardar la solicitud de dinero. Por favor, inténtalo más tarde', genericSmartscanFailureMessage: 'La transacción tiene campos vacíos', + duplicateWaypointsErrorMessage: 'Por favor elimina los puntos de ruta duplicados', atLeastTwoDifferentWaypoints: 'Por favor introduce al menos dos direcciones diferentes', splitBillMultipleParticipantsErrorMessage: 'Solo puedes dividir una cuenta entre un único espacio de trabajo o con usuarios individuales. Por favor actualiza tu selección.', }, diff --git a/src/pages/iou/request/step/IOURequestStepDistance.js b/src/pages/iou/request/step/IOURequestStepDistance.js index 39e9d6f03afa..0270ec724e0d 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.js +++ b/src/pages/iou/request/step/IOURequestStepDistance.js @@ -131,6 +131,10 @@ function IOURequestStepDistance({ return ErrorUtils.getLatestErrorField(transaction, 'route'); } + if (_.keys(waypoints).length > 2 && _.size(validatedWaypoints) !== _.keys(waypoints).length) { + return {0: translate('iou.error.duplicateWaypointsErrorMessage')}; + } + if (_.size(validatedWaypoints) < 2) { return {0: translate('iou.error.atLeastTwoDifferentWaypoints')}; } @@ -159,12 +163,12 @@ function IOURequestStepDistance({ const submitWaypoints = useCallback(() => { // If there is any error or loading state, don't let user go to next page. - if (_.size(validatedWaypoints) < 2 || hasRouteError || isLoadingRoute || isLoading) { + if (_.size(validatedWaypoints) < 2 || (_.keys(waypoints).length > 2 && _.size(validatedWaypoints) !== _.keys(waypoints).length) || hasRouteError || isLoadingRoute || isLoading) { setHasError(true); return; } navigateToNextStep(); - }, [setHasError, hasRouteError, isLoadingRoute, isLoading, validatedWaypoints, navigateToNextStep]); + }, [setHasError, waypoints, hasRouteError, isLoadingRoute, isLoading, validatedWaypoints, navigateToNextStep]); return ( {/* Show error message if there is route error or there are less than 2 routes and user has tried submitting, */} - {((hasError && _.size(validatedWaypoints) < 2) || hasRouteError) && ( + {((hasError && _.size(validatedWaypoints) < 2) || (_.keys(waypoints).length > 2 && _.size(validatedWaypoints) !== _.keys(waypoints).length) || hasRouteError) && ( Date: Thu, 14 Dec 2023 17:07:30 +0700 Subject: [PATCH 04/51] hide thread option in context menu when deleting acion in offline mode --- src/libs/ReportUtils.ts | 9 +++++++++ .../report/ContextMenu/ContextMenuActions.js | 16 +++++++++++++--- src/pages/home/report/ReportActionItem.js | 3 +-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 63037416c923..fa8add8f666f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4314,6 +4314,14 @@ function navigateToPrivateNotes(report: Report, session: Session) { Navigation.navigate(ROUTES.PRIVATE_NOTES_LIST.getRoute(report.reportID)); } +/** + * Check whether should display thread reply + */ +function shouldDisplayThreadReplies(reportAction: ReportAction, reportID: string): boolean { + const hasReplies = (reportAction.childVisibleActionCount ?? 0) > 0; + return hasReplies && !!reportAction.childCommenterCount && !isThreadFirstChat(reportAction, reportID); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -4485,6 +4493,7 @@ export { canEditWriteCapability, hasSmartscanError, shouldAutoFocusOnKeyPress, + shouldDisplayThreadReplies, }; export type {OptionData, OptimisticChatReport}; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 5e6f2d46abda..38a2c1e5510e 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -126,12 +126,18 @@ export default [ if (type !== CONTEXT_MENU_TYPES.REPORT_ACTION) { return false; } + const isDeletedAction = ReportActionsUtils.isDeletedAction(reportAction); + const shouldDisplayThreadReplies = ReportUtils.shouldDisplayThreadReplies(reportAction, reportID); const isCommentAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT; const isReportPreviewAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW; const isIOUAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && !ReportActionsUtils.isSplitBillAction(reportAction); const isModifiedExpenseAction = ReportActionsUtils.isModifiedExpenseAction(reportAction); const isTaskAction = ReportActionsUtils.isTaskAction(reportAction); - return (isCommentAction || isReportPreviewAction || isIOUAction || isModifiedExpenseAction || isTaskAction) && !ReportUtils.isThreadFirstChat(reportAction, reportID); + return ( + (isCommentAction || isReportPreviewAction || isIOUAction || isModifiedExpenseAction || isTaskAction) && + !ReportUtils.isThreadFirstChat(reportAction, reportID) && + (!isDeletedAction || shouldDisplayThreadReplies) + ); }, onPress: (closePopover, {reportAction, reportID}) => { if (closePopover) { @@ -158,11 +164,13 @@ export default [ const isActionCreator = ReportUtils.isActionCreator(reportAction); childReportNotificationPreference = isActionCreator ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; } + const isDeletedAction = ReportActionsUtils.isDeletedAction(reportAction); + const shouldDisplayThreadReplies = ReportUtils.shouldDisplayThreadReplies(reportAction, reportID); const subscribed = childReportNotificationPreference !== 'hidden'; const isCommentAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && !ReportUtils.isThreadFirstChat(reportAction, reportID); const isReportPreviewAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW; const isIOUAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && !ReportActionsUtils.isSplitBillAction(reportAction); - return !subscribed && (isCommentAction || isReportPreviewAction || isIOUAction); + return !subscribed && (isCommentAction || isReportPreviewAction || isIOUAction) && (!isDeletedAction || shouldDisplayThreadReplies); }, onPress: (closePopover, {reportAction, reportID}) => { let childReportNotificationPreference = lodashGet(reportAction, 'childReportNotificationPreference', ''); @@ -195,6 +203,8 @@ export default [ const isActionCreator = ReportUtils.isActionCreator(reportAction); childReportNotificationPreference = isActionCreator ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; } + const isDeletedAction = ReportActionsUtils.isDeletedAction(reportAction); + const shouldDisplayThreadReplies = ReportUtils.shouldDisplayThreadReplies(reportAction, reportID); const subscribed = childReportNotificationPreference !== 'hidden'; if (type !== CONTEXT_MENU_TYPES.REPORT_ACTION) { return false; @@ -202,7 +212,7 @@ export default [ const isCommentAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && !ReportUtils.isThreadFirstChat(reportAction, reportID); const isReportPreviewAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW; const isIOUAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && !ReportActionsUtils.isSplitBillAction(reportAction); - return subscribed && (isCommentAction || isReportPreviewAction || isIOUAction); + return subscribed && (isCommentAction || isReportPreviewAction || isIOUAction) && (!isDeletedAction || shouldDisplayThreadReplies); }, onPress: (closePopover, {reportAction, reportID}) => { let childReportNotificationPreference = lodashGet(reportAction, 'childReportNotificationPreference', ''); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 2e888a5471b8..5fafda690d9f 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -491,9 +491,8 @@ function ReportActionItem(props) { ); } const numberOfThreadReplies = _.get(props, ['action', 'childVisibleActionCount'], 0); - const hasReplies = numberOfThreadReplies > 0; - const shouldDisplayThreadReplies = hasReplies && props.action.childCommenterCount && !ReportUtils.isThreadFirstChat(props.action, props.report.reportID); + const shouldDisplayThreadReplies = ReportUtils.shouldDisplayThreadReplies(props.action, props.report.reportID); const oldestFourAccountIDs = _.map(lodashGet(props.action, 'childOldestFourAccountIDs', '').split(','), (accountID) => Number(accountID)); const draftMessageRightAlign = props.draftMessage ? styles.chatItemReactionsDraftRight : {}; From cc8c5edae9656b928d31ed76f9192e58b20b8ca0 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 18 Dec 2023 20:05:41 +0530 Subject: [PATCH 05/51] fixed mapbox zoom for single waypoint --- src/CONST.ts | 1 + src/components/MapView/MapView.tsx | 2 +- src/components/MapView/MapView.website.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index b29456ba170b..862cc25efbf2 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2931,6 +2931,7 @@ const CONST = { MAPBOX: { PADDING: 50, DEFAULT_ZOOM: 10, + SINGLE_MARKER_ZOOM: 15, DEFAULT_COORDINATE: [-122.4021, 37.7911], STYLE_URL: 'mapbox://styles/expensify/cllcoiqds00cs01r80kp34tmq', }, diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 7b3d73479dde..577325e53e7c 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -98,7 +98,7 @@ const MapView = forwardRef( if (waypoints.length === 1) { cameraRef.current?.setCamera({ - zoomLevel: 15, + zoomLevel: CONST.MAPBOX.SINGLE_MARKER_ZOOM, animationDuration: 1500, centerCoordinate: waypoints[0].coordinate, }); diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 7910d7f93a29..55e99110737d 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -109,7 +109,7 @@ const MapView = forwardRef( if (waypoints.length === 1) { mapRef.flyTo({ center: waypoints[0].coordinate, - zoom: CONST.MAPBOX.DEFAULT_ZOOM, + zoom: CONST.MAPBOX.SINGLE_MARKER_ZOOM, }); return; } From fc60723e70dac9bdd07e318fd7ce096a6818a4a1 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Wed, 20 Dec 2023 03:04:46 +0530 Subject: [PATCH 06/51] fix the issue #33006 --- .../DatePicker/CalendarPicker/index.js | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/components/DatePicker/CalendarPicker/index.js b/src/components/DatePicker/CalendarPicker/index.js index a404c4746397..a4aedcf9a6be 100644 --- a/src/components/DatePicker/CalendarPicker/index.js +++ b/src/components/DatePicker/CalendarPicker/index.js @@ -112,14 +112,46 @@ class CalendarPicker extends React.PureComponent { * Handles the user pressing the previous month arrow of the calendar picker. */ moveToPrevMonth() { - this.setState((prev) => ({currentDateView: subMonths(new Date(prev.currentDateView), 1)})); + this.setState((prev) => { + const prevMonth = subMonths(new Date(prev.currentDateView), 1); + // if year is subtracted, we need to update the years list + let newYears = prev.years; + if (prevMonth.getFullYear() < prev.currentDateView.getFullYear()) { + newYears = _.map(prev.years, (item) => ({ + ...item, + isSelected: item.value === prevMonth.getFullYear(), + })); + } + + return { + ...prev, + currentDateView: prevMonth, + years: newYears, + }; + }); } /** * Handles the user pressing the next month arrow of the calendar picker. */ moveToNextMonth() { - this.setState((prev) => ({currentDateView: addMonths(new Date(prev.currentDateView), 1)})); + this.setState((prev) => { + const nextMonth = addMonths(new Date(prev.currentDateView), 1); + // if year is added, we need to update the years list + let newYears = prev.years; + if (nextMonth.getFullYear() > prev.currentDateView.getFullYear()) { + newYears = _.map(prev.years, (item) => ({ + ...item, + isSelected: item.value === nextMonth.getFullYear(), + })); + } + + return { + ...prev, + currentDateView: nextMonth, + years: newYears, + }; + }); } render() { From f0f34676d99f8cb323c8dd67b28aeea4aa24b5d1 Mon Sep 17 00:00:00 2001 From: Kamil Owczarz Date: Wed, 20 Dec 2023 15:03:28 +0100 Subject: [PATCH 07/51] Refactor get physical card flow --- src/components/CheckboxWithLabel.tsx | 2 +- src/pages/EnablePayments/AdditionalDetailsStep.js | 4 ++-- src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js | 6 +++--- src/pages/settings/Wallet/Card/GetPhysicalCardName.js | 7 +++++-- src/pages/settings/Wallet/Card/GetPhysicalCardPhone.js | 4 +++- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/CheckboxWithLabel.tsx b/src/components/CheckboxWithLabel.tsx index 9660c9e1a2e5..1dfe137269e4 100644 --- a/src/components/CheckboxWithLabel.tsx +++ b/src/components/CheckboxWithLabel.tsx @@ -40,7 +40,7 @@ type CheckboxWithLabelProps = RequiredLabelProps & { /** Error text to display */ errorText?: string; - /** Value for checkbox. This prop is intended to be set by Form.js only */ + /** Value for checkbox. This prop is intended to be set by FormProvider only */ value?: boolean; /** The default value for the checkbox */ diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index d937be615370..faa525a318ab 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -87,7 +87,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP const shouldAskForFullSSN = walletAdditionalDetails.errorCode === CONST.WALLET.ERROR.SSN; /** - * @param {Object} values The values object is passed from Form.js and contains info for each form element that has an inputID + * @param {Object} values The values object is passed from FormProvider and contains info for each form element that has an inputID * @returns {Object} */ const validate = (values) => { @@ -128,7 +128,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP }; /** - * @param {Object} values The values object is passed from Form.js and contains info for each form element that has an inputID + * @param {Object} values The values object is passed from FormProvider and contains info for each form element that has an inputID */ const activateWallet = (values) => { const personalDetails = { diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js index 1d1ce906189b..cd1f4591a61a 100644 --- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js +++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js @@ -3,7 +3,7 @@ import React, {useCallback, useEffect, useRef} from 'react'; import {Text} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import Form from '@components/Form'; +import FormProvider from '@components/Form/FormProvider'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -114,7 +114,7 @@ const defaultProps = { loginList: {}, isConfirmation: false, renderContent: (onSubmit, submitButtonText, styles, children = () => {}, onValidate = () => ({})) => ( -
{children} -
+ ), onValidate: () => ({}), }; diff --git a/src/pages/settings/Wallet/Card/GetPhysicalCardName.js b/src/pages/settings/Wallet/Card/GetPhysicalCardName.js index 0040dac8b75f..5b954d432cce 100644 --- a/src/pages/settings/Wallet/Card/GetPhysicalCardName.js +++ b/src/pages/settings/Wallet/Card/GetPhysicalCardName.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import InputWrapper from '@components/Form/InputWrapper'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -72,7 +73,8 @@ function GetPhysicalCardName({ title={translate('getPhysicalCard.header')} onValidate={onValidate} > - - - Date: Thu, 21 Dec 2023 16:39:55 +0700 Subject: [PATCH 08/51] remove trailing slash in route --- src/ROUTES.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ca1fe9f0e81a..32b7e915b10a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -314,61 +314,61 @@ const ROUTES = { getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}` as const, }, MONEY_REQUEST_STEP_CONFIRMATION: { - route: 'create/:iouType/confirmation/:transactionID/:reportID/', + route: 'create/:iouType/confirmation/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/confirmation/${transactionID}/${reportID}/` as const, }, MONEY_REQUEST_STEP_AMOUNT: { - route: 'create/:iouType/amount/:transactionID/:reportID/', + route: 'create/:iouType/amount/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_CATEGORY: { - route: 'create/:iouType/category/:transactionID/:reportID/', + route: 'create/:iouType/category/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/category/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_CURRENCY: { - route: 'create/:iouType/currency/:transactionID/:reportID/:pageIndex?/', + route: 'create/:iouType/currency/:transactionID/:reportID/:pageIndex?', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, pageIndex = '', backTo = '') => getUrlWithBackToParam(`create/${iouType}/currency/${transactionID}/${reportID}/${pageIndex}`, backTo), }, MONEY_REQUEST_STEP_DATE: { - route: 'create/:iouType/date/:transactionID/:reportID/', + route: 'create/:iouType/date/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/date/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_DESCRIPTION: { - route: 'create/:iouType/description/:transactionID/:reportID/', + route: 'create/:iouType/description/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/description/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_DISTANCE: { - route: 'create/:iouType/distance/:transactionID/:reportID/', + route: 'create/:iouType/distance/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_MERCHANT: { - route: 'create/:iouType/merchante/:transactionID/:reportID/', + route: 'create/:iouType/merchante/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/merchante/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_PARTICIPANTS: { - route: 'create/:iouType/participants/:transactionID/:reportID/', + route: 'create/:iouType/participants/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/participants/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_SCAN: { - route: 'create/:iouType/scan/:transactionID/:reportID/', + route: 'create/:iouType/scan/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/scan/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_TAG: { - route: 'create/:iouType/tag/:transactionID/:reportID/', + route: 'create/:iouType/tag/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/tag/${transactionID}/${reportID}/`, backTo), }, MONEY_REQUEST_STEP_WAYPOINT: { - route: 'create/:iouType/waypoint/:transactionID/:reportID/:pageIndex/', + route: 'create/:iouType/waypoint/:transactionID/:reportID/:pageIndex', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, pageIndex = '', backTo = '') => getUrlWithBackToParam(`create/${iouType}/waypoint/${transactionID}/${reportID}/${pageIndex}`, backTo), }, From 8a04fb11bb73c799133340feb031bce5cad197e8 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 22 Dec 2023 03:00:45 +0700 Subject: [PATCH 09/51] remove redundant slashes in route --- src/libs/Navigation/Navigation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index a3e89a983f98..85e4c0abb1cd 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -117,7 +117,7 @@ function getActiveRoute(): string { */ function isActiveRoute(routePath: Route): boolean { // We remove First forward slash from the URL before matching - return getActiveRoute().substring(1) === routePath; + return getActiveRoute().substring(1) === routePath.replace(/\/{2,}/, '/').replace(/\/$/, ''); } /** From 99ff60abb243d4192a5970e8d0b49d7e1ac40d8d Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Thu, 21 Dec 2023 15:43:24 -0500 Subject: [PATCH 10/51] optimistic data for join room --- src/pages/home/HeaderView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 2182cc5f6359..5c0be79d1d85 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -147,7 +147,7 @@ function HeaderView(props) { icon: Expensicons.ChatBubbles, text: translate('common.join'), onSelected: Session.checkIfActionIsAllowed(() => - Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false), + Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false, props.report.parentReportID, parentReportAction), ), }); } else if ((isChatThread && props.report.notificationPreference.length) || isUserCreatedPolicyRoom || canLeaveRoom) { From 2cd3a4a9dabd143a155e109ed6fc2a692d17e7c5 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 22 Dec 2023 01:12:00 +0200 Subject: [PATCH 11/51] Parse welcomeNote before sending to Web --- src/libs/actions/Policy.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index f33e6637e2de..0cf31d8b5666 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -18,6 +18,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; const allPolicies = {}; Onyx.connect({ @@ -601,10 +602,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs, welcomeNote, policyID) const params = { employees: JSON.stringify(_.map(logins, (login) => ({email: login}))), - - // Do not escape HTML special chars for welcomeNote as this will be handled in the backend. - // See https://github.com/Expensify/App/issues/20081 for more details. - welcomeNote, + welcomeNote: new ExpensiMark().replace(welcomeNote), policyID, }; if (!_.isEmpty(membersChats.reportCreationData)) { From 3be2115c18707b373817d25608e7a307320ce921 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 22 Dec 2023 01:25:59 +0200 Subject: [PATCH 12/51] Run prettier --- src/libs/actions/Policy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 0cf31d8b5666..da5580c19bfd 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1,4 +1,5 @@ import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import {escapeRegExp} from 'lodash'; import filter from 'lodash/filter'; @@ -18,7 +19,6 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; const allPolicies = {}; Onyx.connect({ From bb85de6f3bb9f70d8428a66fcb4a920eb609dad5 Mon Sep 17 00:00:00 2001 From: dukenv0307 <129500732+dukenv0307@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:29:11 +0700 Subject: [PATCH 13/51] Update src/libs/ReportUtils.ts Co-authored-by: Eugene Voloshchak --- 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 fa8add8f666f..ab60cd650545 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4315,7 +4315,7 @@ function navigateToPrivateNotes(report: Report, session: Session) { } /** - * Check whether should display thread reply + * Checks if thread replies should be displayed */ function shouldDisplayThreadReplies(reportAction: ReportAction, reportID: string): boolean { const hasReplies = (reportAction.childVisibleActionCount ?? 0) > 0; From e7e7762b6faa94896b9b4028369ebb417d2f5b20 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 22 Dec 2023 15:02:38 +0700 Subject: [PATCH 14/51] global regex to replace all rendundant slashes --- src/libs/Navigation/Navigation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 85e4c0abb1cd..657ee68b1206 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -117,7 +117,7 @@ function getActiveRoute(): string { */ function isActiveRoute(routePath: Route): boolean { // We remove First forward slash from the URL before matching - return getActiveRoute().substring(1) === routePath.replace(/\/{2,}/, '/').replace(/\/$/, ''); + return getActiveRoute().substring(1) === routePath.replace(/\/{2,}/g, '/').replace(/\/$/, ''); } /** From f46f2b724a2ecf2b16c7135f67614615eaeed016 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Fri, 22 Dec 2023 17:59:31 +0530 Subject: [PATCH 15/51] added requested changes --- src/components/DatePicker/CalendarPicker/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/DatePicker/CalendarPicker/index.js b/src/components/DatePicker/CalendarPicker/index.js index a4aedcf9a6be..7df7d81b961d 100644 --- a/src/components/DatePicker/CalendarPicker/index.js +++ b/src/components/DatePicker/CalendarPicker/index.js @@ -124,7 +124,6 @@ class CalendarPicker extends React.PureComponent { } return { - ...prev, currentDateView: prevMonth, years: newYears, }; @@ -147,7 +146,6 @@ class CalendarPicker extends React.PureComponent { } return { - ...prev, currentDateView: nextMonth, years: newYears, }; From f4708f9b20887b975d86d678b691ab2a2f672f52 Mon Sep 17 00:00:00 2001 From: sourcecodedeveloper Date: Sun, 24 Dec 2023 03:13:24 -0800 Subject: [PATCH 16/51] Add regex for accented chars --- src/CONST.ts | 1 + src/libs/ValidationUtils.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index aca59bd831e6..e5d369026714 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1352,6 +1352,7 @@ const CONST = { DIGITS_AND_PLUS: /^\+?[0-9]*$/, ALPHABETIC_AND_LATIN_CHARS: /^[\p{Script=Latin} ]*$/u, NON_ALPHABETIC_AND_NON_LATIN_CHARS: /[^\p{Script=Latin}]/gu, + ACCENT_LATIN_CHARS: /[\u00C0-\u017F]/g, POSITIVE_INTEGER: /^\d+$/, PO_BOX: /\b[P|p]?(OST|ost)?\.?\s*[O|o|0]?(ffice|FFICE)?\.?\s*[B|b][O|o|0]?[X|x]?\.?\s+[#]?(\d+)\b/, ANY_VALUE: /^.+$/, diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index ba977312fcfb..88d8438df511 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -317,7 +317,8 @@ function isValidDisplayName(name: string): boolean { * Checks that the provided legal name doesn't contain special characters */ function isValidLegalName(name: string): boolean { - return CONST.REGEX.ALPHABETIC_AND_LATIN_CHARS.test(name); + const hasAccentedChars = Boolean(name.match(CONST.REGEX.ACCENT_LATIN_CHARS)); + return CONST.REGEX.ALPHABETIC_AND_LATIN_CHARS.test(name) && !hasAccentedChars; } /** From 1bdc86667e1e097e64ec323420ff54ffcc6fc128 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Dec 2023 15:06:41 +0700 Subject: [PATCH 17/51] add regex to CONST --- src/CONST.ts | 1 + src/libs/Navigation/Navigation.ts | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 812fb4d5335d..3527902dff40 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1406,6 +1406,7 @@ const CONST = { ROUTES: { VALIDATE_LOGIN: /\/v($|(\/\/*))/, UNLINK_LOGIN: /\/u($|(\/\/*))/, + REDUNDANT_SLASHES: /(\/{2,})|(\/$)/g, }, TIME_STARTS_01: /^01:\d{2} [AP]M$/, diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index e62b2e057d44..d1c6459a8aee 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -114,8 +114,9 @@ function getActiveRoute(): string { * @return is active */ function isActiveRoute(routePath: Route): boolean { - // We remove First forward slash from the URL before matching - return getActiveRoute().substring(1) === routePath.replace(/\/{2,}/g, '/').replace(/\/$/, ''); + // We remove First forward slash from the URL + // And redundant (consecutive and trailing) slashes from path before matching + return getActiveRoute().substring(1) === routePath.replace(CONST.REGEX.ROUTES.REDUNDANT_SLASHES, (_, p1) => (p1 ? '/' : '')); } /** From fa4a8e442b22ff07c518d443549bca5861860480 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Dec 2023 15:41:04 +0700 Subject: [PATCH 18/51] fix lint --- src/libs/Navigation/Navigation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index d1c6459a8aee..bc8c998a9f87 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -116,7 +116,7 @@ function getActiveRoute(): string { function isActiveRoute(routePath: Route): boolean { // We remove First forward slash from the URL // And redundant (consecutive and trailing) slashes from path before matching - return getActiveRoute().substring(1) === routePath.replace(CONST.REGEX.ROUTES.REDUNDANT_SLASHES, (_, p1) => (p1 ? '/' : '')); + return getActiveRoute().substring(1) === routePath.replace(CONST.REGEX.ROUTES.REDUNDANT_SLASHES, (match, p1) => (p1 ? '/' : '')); } /** From 9072dbc094572a6a59c8eb1342fb551eef3b35e9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Dec 2023 17:03:34 +0100 Subject: [PATCH 19/51] [TS migration] Migrate 'Section.js' component to TypeScript --- src/components/MenuItem.tsx | 2 +- src/components/MenuItemList.js | 62 --------- src/components/MenuItemList.tsx | 65 ++++++++++ src/components/Section/IconSection.js | 41 ------ src/components/Section/IconSection.tsx | 30 +++++ src/components/Section/index.js | 122 ------------------ src/components/Section/index.tsx | 104 +++++++++++++++ .../ContextMenu/ReportActionContextMenu.ts | 6 +- 8 files changed, 203 insertions(+), 229 deletions(-) delete mode 100644 src/components/MenuItemList.js create mode 100644 src/components/MenuItemList.tsx delete mode 100644 src/components/Section/IconSection.js create mode 100644 src/components/Section/IconSection.tsx delete mode 100644 src/components/Section/index.js create mode 100644 src/components/Section/index.tsx diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index db150d55f0d2..9e5d84166c0e 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -220,7 +220,7 @@ type MenuItemProps = (ResponsiveProps | UnresponsiveProps) & furtherDetails?: string; /** The function that should be called when this component is LongPressed or right-clicked. */ - onSecondaryInteraction: () => void; + onSecondaryInteraction?: (event: GestureResponderEvent | MouseEvent) => void; /** Array of objects that map display names to their corresponding tooltip */ titleWithTooltips: DisplayNameWithTooltip[]; diff --git a/src/components/MenuItemList.js b/src/components/MenuItemList.js deleted file mode 100644 index c9eee8e888e1..000000000000 --- a/src/components/MenuItemList.js +++ /dev/null @@ -1,62 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import _ from 'underscore'; -import useSingleExecution from '@hooks/useSingleExecution'; -import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import CONST from '@src/CONST'; -import MenuItem from './MenuItem'; -import menuItemPropTypes from './menuItemPropTypes'; - -const propTypes = { - /** An array of props that are pass to individual MenuItem components */ - menuItems: PropTypes.arrayOf(PropTypes.shape(menuItemPropTypes)), - - /** Whether or not to use the single execution hook */ - shouldUseSingleExecution: PropTypes.bool, -}; -const defaultProps = { - menuItems: [], - shouldUseSingleExecution: false, -}; - -function MenuItemList(props) { - let popoverAnchor; - const {isExecuting, singleExecution} = useSingleExecution(); - - /** - * Handle the secondary interaction for a menu item. - * - * @param {*} link the menu item link or function to get the link - * @param {Event} e the interaction event - */ - const secondaryInteraction = (link, e) => { - if (typeof link === 'function') { - link().then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, url, popoverAnchor)); - } else if (!_.isEmpty(link)) { - ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, link, popoverAnchor); - } - }; - - return ( - <> - {_.map(props.menuItems, (menuItemProps) => ( - secondaryInteraction(menuItemProps.link, e) : undefined} - ref={(el) => (popoverAnchor = el)} - shouldBlockSelection={Boolean(menuItemProps.link)} - // eslint-disable-next-line react/jsx-props-no-spreading - {...menuItemProps} - disabled={menuItemProps.disabled || isExecuting} - onPress={props.shouldUseSingleExecution ? singleExecution(menuItemProps.onPress) : menuItemProps.onPress} - /> - ))} - - ); -} - -MenuItemList.displayName = 'MenuItemList'; -MenuItemList.propTypes = propTypes; -MenuItemList.defaultProps = defaultProps; - -export default MenuItemList; diff --git a/src/components/MenuItemList.tsx b/src/components/MenuItemList.tsx new file mode 100644 index 000000000000..2e1015d092ca --- /dev/null +++ b/src/components/MenuItemList.tsx @@ -0,0 +1,65 @@ +import React, {useRef} from 'react'; +import {GestureResponderEvent, View} from 'react-native'; +import useSingleExecution from '@hooks/useSingleExecution'; +import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; +import CONST from '@src/CONST'; +import MenuItem, {MenuItemProps} from './MenuItem'; + +type MenuItemWithLink = MenuItemProps & { + /** The link to open when the menu item is clicked */ + link: string | (() => Promise); +}; + +type MenuItemListProps = { + /** An array of props that are pass to individual MenuItem components */ + menuItems: MenuItemWithLink[]; + + /** Whether or not to use the single execution hook */ + shouldUseSingleExecution?: boolean; +}; + +function MenuItemList({menuItems = [], shouldUseSingleExecution = false}: MenuItemListProps) { + const popoverAnchor = useRef(null); + const {isExecuting, singleExecution} = useSingleExecution(); + + /** + * Handle the secondary interaction for a menu item. + * + * @param link the menu item link or function to get the link + * @param e the interaction event + */ + const secondaryInteraction = (link: MenuItemWithLink['link'], event: GestureResponderEvent | MouseEvent) => { + if (typeof link === 'function') { + link().then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); + } else if (link) { + ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, link, popoverAnchor.current); + } + }; + + return ( + <> + {menuItems.map((menuItemProps) => { + const onPress = menuItemProps.onPress ?? (() => {}); + + return ( + secondaryInteraction(menuItemProps.link, e) : undefined} + ref={popoverAnchor} + shouldBlockSelection={!!menuItemProps.link} + // eslint-disable-next-line react/jsx-props-no-spreading + {...menuItemProps} + disabled={menuItemProps.disabled ?? isExecuting} + onPress={shouldUseSingleExecution ? singleExecution(onPress) : onPress} + interactive + /> + ); + })} + + ); +} + +MenuItemList.displayName = 'MenuItemList'; + +export type {MenuItemWithLink}; +export default MenuItemList; diff --git a/src/components/Section/IconSection.js b/src/components/Section/IconSection.js deleted file mode 100644 index 307331aa36d6..000000000000 --- a/src/components/Section/IconSection.js +++ /dev/null @@ -1,41 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import Icon from '@components/Icon'; -import sourcePropTypes from '@components/Image/sourcePropTypes'; -import useThemeStyles from '@hooks/useThemeStyles'; - -const iconSectionPropTypes = { - icon: sourcePropTypes, - IconComponent: PropTypes.IconComponent, - iconContainerStyles: PropTypes.iconContainerStyles, -}; - -const defaultIconSectionPropTypes = { - icon: null, - IconComponent: null, - iconContainerStyles: [], -}; - -function IconSection({icon, IconComponent, iconContainerStyles}) { - const styles = useThemeStyles(); - - return ( - - {Boolean(icon) && ( - - )} - {Boolean(IconComponent) && } - - ); -} - -IconSection.displayName = 'IconSection'; -IconSection.propTypes = iconSectionPropTypes; -IconSection.defaultProps = defaultIconSectionPropTypes; - -export default IconSection; diff --git a/src/components/Section/IconSection.tsx b/src/components/Section/IconSection.tsx new file mode 100644 index 000000000000..ddadbcdb1d77 --- /dev/null +++ b/src/components/Section/IconSection.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import {StyleProp, View, ViewStyle} from 'react-native'; +import Icon from '@components/Icon'; +import useThemeStyles from '@hooks/useThemeStyles'; +import IconAsset from '@src/types/utils/IconAsset'; + +type IconSectionProps = { + icon?: IconAsset; + iconContainerStyles?: StyleProp; +}; + +function IconSection({icon, iconContainerStyles}: IconSectionProps) { + const styles = useThemeStyles(); + + return ( + + {!!icon && ( + + )} + + ); +} + +IconSection.displayName = 'IconSection'; + +export default IconSection; diff --git a/src/components/Section/index.js b/src/components/Section/index.js deleted file mode 100644 index 50576abef025..000000000000 --- a/src/components/Section/index.js +++ /dev/null @@ -1,122 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import sourcePropTypes from '@components/Image/sourcePropTypes'; -import MenuItemList from '@components/MenuItemList'; -import menuItemPropTypes from '@components/menuItemPropTypes'; -import Text from '@components/Text'; -import useThemeStyles from '@hooks/useThemeStyles'; -import IconSection from './IconSection'; - -const CARD_LAYOUT = { - ICON_ON_TOP: 'iconOnTop', - ICON_ON_RIGHT: 'iconOnRight', -}; - -const propTypes = { - /** An array of props that are pass to individual MenuItem components */ - menuItems: PropTypes.arrayOf(PropTypes.shape(menuItemPropTypes)), - - /** The text to display in the title of the section */ - title: PropTypes.string.isRequired, - - /** The text to display in the subtitle of the section */ - subtitle: PropTypes.string, - - /** The icon to display along with the title */ - icon: sourcePropTypes, - - /** Icon component */ - IconComponent: PropTypes.func, - - /** Card layout that affects icon positioning, margins, sizes. */ - // eslint-disable-next-line rulesdir/prefer-underscore-method - cardLayout: PropTypes.oneOf(Object.values(CARD_LAYOUT)), - - /** Contents to display inside the section */ - children: PropTypes.node, - - /** Customize the Section container */ - // eslint-disable-next-line react/forbid-prop-types - containerStyles: PropTypes.arrayOf(PropTypes.object), - - /** Customize the Section container */ - // eslint-disable-next-line react/forbid-prop-types - titleStyles: PropTypes.arrayOf(PropTypes.object), - - /** Customize the Section container */ - // eslint-disable-next-line react/forbid-prop-types - subtitleStyles: PropTypes.arrayOf(PropTypes.object), - - /** Whether the subtitle should have a muted style */ - subtitleMuted: PropTypes.bool, - - /** Customize the Section container */ - // eslint-disable-next-line react/forbid-prop-types - childrenStyles: PropTypes.arrayOf(PropTypes.object), - - /** Customize the Icon container */ - // eslint-disable-next-line react/forbid-prop-types - iconContainerStyles: PropTypes.arrayOf(PropTypes.object), -}; - -const defaultProps = { - menuItems: null, - children: null, - icon: null, - IconComponent: null, - cardLayout: CARD_LAYOUT.ICON_ON_RIGHT, - containerStyles: [], - iconContainerStyles: [], - titleStyles: [], - subtitleStyles: [], - subtitleMuted: false, - childrenStyles: [], - subtitle: null, -}; - -function Section({children, childrenStyles, containerStyles, icon, IconComponent, cardLayout, iconContainerStyles, menuItems, subtitle, subtitleStyles, subtitleMuted, title, titleStyles}) { - const styles = useThemeStyles(); - - return ( - <> - - {cardLayout === CARD_LAYOUT.ICON_ON_TOP && ( - - )} - - - {title} - - {cardLayout === CARD_LAYOUT.ICON_ON_RIGHT && ( - - )} - - - {Boolean(subtitle) && ( - - {subtitle} - - )} - - {children} - - {Boolean(menuItems) && } - - - ); -} -Section.displayName = 'Section'; -Section.propTypes = propTypes; -Section.defaultProps = defaultProps; - -export {CARD_LAYOUT}; -export default Section; diff --git a/src/components/Section/index.tsx b/src/components/Section/index.tsx new file mode 100644 index 000000000000..7f458d2ed8a5 --- /dev/null +++ b/src/components/Section/index.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import {StyleProp, View, ViewStyle} from 'react-native'; +import {ValueOf} from 'type-fest'; +import MenuItemList, {MenuItemWithLink} from '@components/MenuItemList'; +import Text from '@components/Text'; +import useThemeStyles from '@hooks/useThemeStyles'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; +import IconAsset from '@src/types/utils/IconAsset'; +import IconSection from './IconSection'; + +const CARD_LAYOUT = { + ICON_ON_TOP: 'iconOnTop', + ICON_ON_RIGHT: 'iconOnRight', +} as const; + +type SectionProps = ChildrenProps & { + /** An array of props that are passed to individual MenuItem components */ + menuItems?: MenuItemWithLink[]; + + /** The text to display in the title of the section */ + title: string; + + /** The text to display in the subtitle of the section */ + subtitle?: string; + + /** The icon to display along with the title */ + icon?: IconAsset; + + /** Card layout that affects icon positioning, margins, sizes. */ + cardLayout?: ValueOf; + + /** Whether the subtitle should have a muted style */ + subtitleMuted?: boolean; + + /** Customize the Section container */ + containerStyles?: StyleProp; + + /** Customize the Section container */ + titleStyles?: StyleProp; + + /** Customize the Section container */ + subtitleStyles?: StyleProp; + + /** Customize the Section container */ + childrenStyles?: StyleProp; + + /** Customize the Icon container */ + iconContainerStyles?: StyleProp; +}; + +function Section({ + children, + childrenStyles, + containerStyles, + icon, + cardLayout = CARD_LAYOUT.ICON_ON_RIGHT, + iconContainerStyles, + menuItems, + subtitle, + subtitleStyles, + subtitleMuted = false, + title, + titleStyles, +}: SectionProps) { + const styles = useThemeStyles(); + + return ( + <> + + {cardLayout === CARD_LAYOUT.ICON_ON_TOP && ( + + )} + + + {title} + + {cardLayout === CARD_LAYOUT.ICON_ON_RIGHT && ( + + )} + + + {!!subtitle && ( + + {subtitle} + + )} + + {children} + + {!!menuItems && } + + + ); +} +Section.displayName = 'Section'; + +export {CARD_LAYOUT}; +export default Section; diff --git a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts index b269bc276b55..c7e712afc559 100644 --- a/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts +++ b/src/pages/home/report/ContextMenu/ReportActionContextMenu.ts @@ -1,5 +1,5 @@ import React from 'react'; -import {GestureResponderEvent, Text as RNText} from 'react-native'; +import {GestureResponderEvent, Text as RNText, View} from 'react-native'; import {OnyxEntry} from 'react-native-onyx'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; @@ -17,7 +17,7 @@ type ShowContextMenu = ( type: ContextMenuType, event: GestureResponderEvent | MouseEvent, selection: string, - contextMenuAnchor: RNText | null, + contextMenuAnchor: View | RNText | null, reportID?: string, reportActionID?: string, originalReportID?: string, @@ -94,7 +94,7 @@ function showContextMenu( type: ContextMenuType, event: GestureResponderEvent | MouseEvent, selection: string, - contextMenuAnchor: RNText | null, + contextMenuAnchor: View | RNText | null, reportID = '0', reportActionID = '0', originalReportID = '0', From fe574012fb26f2d2be3567ea1a69b0cc79a9e02a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Dec 2023 18:07:23 +0100 Subject: [PATCH 20/51] Adjust MenuItem and MenuItemList --- package.json | 2 +- src/components/MenuItem.tsx | 241 +++++++++++++------------- src/components/MenuItemList.tsx | 39 ++--- src/components/Section/index.tsx | 2 +- src/hooks/useSingleExecution/index.ts | 4 +- 5 files changed, 138 insertions(+), 150 deletions(-) diff --git a/package.json b/package.json index df30c549c515..43b74ab0de55 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "private": true, "scripts": { "configure-mapbox": "scripts/setup-mapbox-sdk-walkthrough.sh", - "setupNewDotWebForEmulators": "scripts/setup-newdot-web-emulators.sh", + "setupNewDotWebForEmulators": "scripts/psetup-newdot-web-emulators.sh", "startAndroidEmulator": "scripts/start-android.sh", "postinstall": "scripts/postInstall.sh", "clean": "npx react-native clean-project-auto", diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 9e5d84166c0e..9ac65e438a48 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -33,20 +33,6 @@ import RenderHTML from './RenderHTML'; import SelectCircle from './SelectCircle'; import Text from './Text'; -type ResponsiveProps = { - /** Function to fire when component is pressed */ - onPress: (event: GestureResponderEvent | KeyboardEvent) => void; - - interactive?: true; -}; - -type UnresponsiveProps = { - onPress?: undefined; - - /** Whether the menu item should be interactive at all */ - interactive: false; -}; - type IconProps = { /** Flag to choose between avatar image or an icon */ iconType: typeof CONST.ICON_TYPE_ICON; @@ -67,170 +53,175 @@ type NoIcon = { icon?: undefined; }; -type MenuItemProps = (ResponsiveProps | UnresponsiveProps) & - (IconProps | AvatarProps | NoIcon) & { - /** Text to be shown as badge near the right end. */ - badgeText?: string; +type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { + /** Function to fire when component is pressed */ + onPress?: (event: GestureResponderEvent | KeyboardEvent) => void; - /** Used to apply offline styles to child text components */ - style?: ViewStyle; + /** Whether the menu item should be interactive at all */ + interactive?: boolean; - /** Any additional styles to apply */ - wrapperStyle?: StyleProp; + /** Text to be shown as badge near the right end. */ + badgeText?: string; - /** Any additional styles to apply on the outer element */ - containerStyle?: StyleProp; + /** Used to apply offline styles to child text components */ + style?: ViewStyle; - /** Used to apply styles specifically to the title */ - titleStyle?: ViewStyle; + /** Any additional styles to apply */ + wrapperStyle?: StyleProp; - /** Any adjustments to style when menu item is hovered or pressed */ - hoverAndPressStyle: StyleProp>; + /** Any additional styles to apply on the outer element */ + containerStyle?: StyleProp; - /** Additional styles to style the description text below the title */ - descriptionTextStyle?: StyleProp; + /** Used to apply styles specifically to the title */ + titleStyle?: ViewStyle; - /** The fill color to pass into the icon. */ - iconFill?: string; + /** Any adjustments to style when menu item is hovered or pressed */ + hoverAndPressStyle: StyleProp>; - /** Secondary icon to display on the left side of component, right of the icon */ - secondaryIcon?: IconAsset; + /** Additional styles to style the description text below the title */ + descriptionTextStyle?: StyleProp; - /** The fill color to pass into the secondary icon. */ - secondaryIconFill?: string; + /** The fill color to pass into the icon. */ + iconFill?: string; - /** Icon Width */ - iconWidth?: number; + /** Secondary icon to display on the left side of component, right of the icon */ + secondaryIcon?: IconAsset; - /** Icon Height */ - iconHeight?: number; + /** The fill color to pass into the secondary icon. */ + secondaryIconFill?: string; - /** Any additional styles to pass to the icon container. */ - iconStyles?: StyleProp; + /** Icon Width */ + iconWidth?: number; - /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon?: IconAsset; + /** Icon Height */ + iconHeight?: number; - /** An icon to display under the main item */ - furtherDetailsIcon?: IconAsset; + /** Any additional styles to pass to the icon container. */ + iconStyles?: StyleProp; - /** Boolean whether to display the title right icon */ - shouldShowTitleIcon?: boolean; + /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ + fallbackIcon?: IconAsset; - /** Icon to display at right side of title */ - titleIcon?: IconAsset; + /** An icon to display under the main item */ + furtherDetailsIcon?: IconAsset; - /** Boolean whether to display the right icon */ - shouldShowRightIcon?: boolean; + /** Boolean whether to display the title right icon */ + shouldShowTitleIcon?: boolean; - /** Overrides the icon for shouldShowRightIcon */ - iconRight?: IconAsset; + /** Icon to display at right side of title */ + titleIcon?: IconAsset; - /** Should render component on the right */ - shouldShowRightComponent?: boolean; + /** Boolean whether to display the right icon */ + shouldShowRightIcon?: boolean; - /** Component to be displayed on the right */ - rightComponent?: ReactNode; + /** Overrides the icon for shouldShowRightIcon */ + iconRight?: IconAsset; - /** A description text to show under the title */ - description?: string; + /** Should render component on the right */ + shouldShowRightComponent?: boolean; - /** Should the description be shown above the title (instead of the other way around) */ - shouldShowDescriptionOnTop?: boolean; + /** Component to be displayed on the right */ + rightComponent?: ReactNode; - /** Error to display below the title */ - error?: string; + /** A description text to show under the title */ + description?: string; - /** Error to display at the bottom of the component */ - errorText?: string; + /** Should the description be shown above the title (instead of the other way around) */ + shouldShowDescriptionOnTop?: boolean; - /** A boolean flag that gives the icon a green fill if true */ - success?: boolean; + /** Error to display below the title */ + error?: string; - /** Whether item is focused or active */ - focused?: boolean; + /** Error to display at the bottom of the component */ + errorText?: string; - /** Should we disable this menu item? */ - disabled?: boolean; + /** A boolean flag that gives the icon a green fill if true */ + success?: boolean; - /** Text that appears above the title */ - label?: string; + /** Whether item is focused or active */ + focused?: boolean; - /** Label to be displayed on the right */ - rightLabel?: string; + /** Should we disable this menu item? */ + disabled?: boolean; - /** Text to display for the item */ - title?: string; + /** Text that appears above the title */ + label?: string; - /** A right-aligned subtitle for this menu option */ - subtitle?: string | number; + /** Label to be displayed on the right */ + rightLabel?: string; - /** Should the title show with normal font weight (not bold) */ - shouldShowBasicTitle?: boolean; + /** Text to display for the item */ + title?: string; - /** Should we make this selectable with a checkbox */ - shouldShowSelectedState?: boolean; + /** A right-aligned subtitle for this menu option */ + subtitle?: string | number; - /** Whether this item is selected */ - isSelected?: boolean; + /** Should the title show with normal font weight (not bold) */ + shouldShowBasicTitle?: boolean; - /** Prop to identify if we should load avatars vertically instead of diagonally */ - shouldStackHorizontally: boolean; + /** Should we make this selectable with a checkbox */ + shouldShowSelectedState?: boolean; - /** Prop to represent the size of the avatar images to be shown */ - avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; + /** Whether this item is selected */ + isSelected?: boolean; - /** Avatars to show on the right of the menu item */ - floatRightAvatars?: IconType[]; + /** Prop to identify if we should load avatars vertically instead of diagonally */ + shouldStackHorizontally: boolean; - /** Prop to represent the size of the float right avatar images to be shown */ - floatRightAvatarSize?: ValueOf; + /** Prop to represent the size of the avatar images to be shown */ + avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; - /** Affects avatar size */ - viewMode?: ValueOf; + /** Avatars to show on the right of the menu item */ + floatRightAvatars?: IconType[]; - /** Used to truncate the text with an ellipsis after computing the text layout */ - numberOfLinesTitle?: number; + /** Prop to represent the size of the float right avatar images to be shown */ + floatRightAvatarSize?: ValueOf; - /** Whether we should use small avatar subscript sizing the for menu item */ - isSmallAvatarSubscriptMenu?: boolean; + /** Affects avatar size */ + viewMode?: ValueOf; - /** The type of brick road indicator to show. */ - brickRoadIndicator?: ValueOf; + /** Used to truncate the text with an ellipsis after computing the text layout */ + numberOfLinesTitle?: number; - /** Should render the content in HTML format */ - shouldRenderAsHTML?: boolean; + /** Whether we should use small avatar subscript sizing the for menu item */ + isSmallAvatarSubscriptMenu?: boolean; - /** Should we grey out the menu item when it is disabled? */ - shouldGreyOutWhenDisabled?: boolean; + /** The type of brick road indicator to show. */ + brickRoadIndicator?: ValueOf; - /** The action accept for anonymous user or not */ - isAnonymousAction?: boolean; + /** Should render the content in HTML format */ + shouldRenderAsHTML?: boolean; - /** Flag to indicate whether or not text selection should be disabled from long-pressing the menu item. */ - shouldBlockSelection?: boolean; + /** Should we grey out the menu item when it is disabled? */ + shouldGreyOutWhenDisabled?: boolean; - /** Whether should render title as HTML or as Text */ - shouldParseTitle?: false; + /** The action accept for anonymous user or not */ + isAnonymousAction?: boolean; - /** Should check anonymous user in onPress function */ - shouldCheckActionAllowedOnPress?: boolean; + /** Flag to indicate whether or not text selection should be disabled from long-pressing the menu item. */ + shouldBlockSelection?: boolean; - /** Text to display under the main item */ - furtherDetails?: string; + /** Whether should render title as HTML or as Text */ + shouldParseTitle?: false; - /** The function that should be called when this component is LongPressed or right-clicked. */ - onSecondaryInteraction?: (event: GestureResponderEvent | MouseEvent) => void; + /** Should check anonymous user in onPress function */ + shouldCheckActionAllowedOnPress?: boolean; - /** Array of objects that map display names to their corresponding tooltip */ - titleWithTooltips: DisplayNameWithTooltip[]; + /** Text to display under the main item */ + furtherDetails?: string; - /** Icon should be displayed in its own color */ - displayInDefaultIconColor?: boolean; + /** The function that should be called when this component is LongPressed or right-clicked. */ + onSecondaryInteraction?: (event: GestureResponderEvent | MouseEvent) => void; - /** Determines how the icon should be resized to fit its container */ - contentFit?: ImageContentFit; - }; + /** Array of objects that map display names to their corresponding tooltip */ + titleWithTooltips: DisplayNameWithTooltip[]; + + /** Icon should be displayed in its own color */ + displayInDefaultIconColor?: boolean; + + /** Determines how the icon should be resized to fit its container */ + contentFit?: ImageContentFit; +}; function MenuItem( { diff --git a/src/components/MenuItemList.tsx b/src/components/MenuItemList.tsx index 2e1015d092ca..82097309c580 100644 --- a/src/components/MenuItemList.tsx +++ b/src/components/MenuItemList.tsx @@ -5,9 +5,11 @@ import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportA import CONST from '@src/CONST'; import MenuItem, {MenuItemProps} from './MenuItem'; +type MenuItemLink = string | (() => Promise); + type MenuItemWithLink = MenuItemProps & { /** The link to open when the menu item is clicked */ - link: string | (() => Promise); + link: MenuItemLink; }; type MenuItemListProps = { @@ -19,16 +21,16 @@ type MenuItemListProps = { }; function MenuItemList({menuItems = [], shouldUseSingleExecution = false}: MenuItemListProps) { - const popoverAnchor = useRef(null); + const popoverAnchor = useRef(null); const {isExecuting, singleExecution} = useSingleExecution(); /** * Handle the secondary interaction for a menu item. * * @param link the menu item link or function to get the link - * @param e the interaction event + * @param event the interaction event */ - const secondaryInteraction = (link: MenuItemWithLink['link'], event: GestureResponderEvent | MouseEvent) => { + const secondaryInteraction = (link: MenuItemLink, event: GestureResponderEvent | MouseEvent) => { if (typeof link === 'function') { link().then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); } else if (link) { @@ -38,23 +40,18 @@ function MenuItemList({menuItems = [], shouldUseSingleExecution = false}: MenuIt return ( <> - {menuItems.map((menuItemProps) => { - const onPress = menuItemProps.onPress ?? (() => {}); - - return ( - secondaryInteraction(menuItemProps.link, e) : undefined} - ref={popoverAnchor} - shouldBlockSelection={!!menuItemProps.link} - // eslint-disable-next-line react/jsx-props-no-spreading - {...menuItemProps} - disabled={menuItemProps.disabled ?? isExecuting} - onPress={shouldUseSingleExecution ? singleExecution(onPress) : onPress} - interactive - /> - ); - })} + {menuItems.map((menuItemProps) => ( + secondaryInteraction(menuItemProps.link, e) : undefined} + ref={popoverAnchor} + shouldBlockSelection={!!menuItemProps.link} + // eslint-disable-next-line react/jsx-props-no-spreading + {...menuItemProps} + disabled={!!menuItemProps.disabled || isExecuting} + onPress={shouldUseSingleExecution ? singleExecution(menuItemProps.onPress) : menuItemProps.onPress} + /> + ))} ); } diff --git a/src/components/Section/index.tsx b/src/components/Section/index.tsx index 7f458d2ed8a5..47f7605d278b 100644 --- a/src/components/Section/index.tsx +++ b/src/components/Section/index.tsx @@ -26,7 +26,7 @@ type SectionProps = ChildrenProps & { /** The icon to display along with the title */ icon?: IconAsset; - /** Card layout that affects icon positioning, margins, sizes. */ + /** Card layout that affects icon positioning, margins, sizes */ cardLayout?: ValueOf; /** Whether the subtitle should have a muted style */ diff --git a/src/hooks/useSingleExecution/index.ts b/src/hooks/useSingleExecution/index.ts index c37087d27c5f..0522303d553a 100644 --- a/src/hooks/useSingleExecution/index.ts +++ b/src/hooks/useSingleExecution/index.ts @@ -9,9 +9,9 @@ type Action = (...params: T) => void | Promise; */ export default function useSingleExecution() { const singleExecution = useCallback( - (action: Action) => + (action?: Action) => (...params: T) => { - action(...params); + action?.(...params); }, [], ); From 2a50e51d96ad58f2894d0a0345c191fe1492d391 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Dec 2023 18:07:54 +0100 Subject: [PATCH 21/51] Fix package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43b74ab0de55..df30c549c515 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "private": true, "scripts": { "configure-mapbox": "scripts/setup-mapbox-sdk-walkthrough.sh", - "setupNewDotWebForEmulators": "scripts/psetup-newdot-web-emulators.sh", + "setupNewDotWebForEmulators": "scripts/setup-newdot-web-emulators.sh", "startAndroidEmulator": "scripts/start-android.sh", "postinstall": "scripts/postInstall.sh", "clean": "npx react-native clean-project-auto", From ccf9b5eb10f0815b39d1315290037bcfad848f66 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 28 Dec 2023 17:38:03 +0700 Subject: [PATCH 22/51] fix: Red dot does not appear in LHN conversation when IOU deletion error occurs --- src/libs/actions/IOU.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d43fefca20bc..068a542838fe 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2488,7 +2488,10 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, value: { - [reportPreviewAction.reportActionID]: reportPreviewAction, + [reportPreviewAction.reportActionID]: { + ...reportPreviewAction, + errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericDeleteFailureMessage'), + }, }, }, ...(shouldDeleteIOUReport From 2a7ed64c312cd69eb0d1e52a2829cba5d8777b50 Mon Sep 17 00:00:00 2001 From: Ionatan Wiznia Date: Fri, 29 Dec 2023 13:27:53 -0600 Subject: [PATCH 23/51] Add dev env param --- src/libs/Network/enhanceParameters.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/Network/enhanceParameters.ts b/src/libs/Network/enhanceParameters.ts index 3fadeea7447c..479335254d34 100644 --- a/src/libs/Network/enhanceParameters.ts +++ b/src/libs/Network/enhanceParameters.ts @@ -1,6 +1,7 @@ import getPlatform from '@libs/getPlatform'; import CONFIG from '@src/CONFIG'; import * as NetworkStore from './NetworkStore'; +import * as Environment from "@libs/Environment/Environment"; /** * Does this command require an authToken? @@ -37,6 +38,8 @@ export default function enhanceParameters(command: string, parameters: Record Date: Fri, 29 Dec 2023 13:35:01 -0600 Subject: [PATCH 24/51] Fix tests --- tests/unit/enhanceParametersTest.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/enhanceParametersTest.js b/tests/unit/enhanceParametersTest.js index 513206b42614..6829732f1633 100644 --- a/tests/unit/enhanceParametersTest.js +++ b/tests/unit/enhanceParametersTest.js @@ -18,6 +18,7 @@ test('Enhance parameters adds correct parameters for Log command with no authTok testParameter: 'test', api_setCookie: false, email, + isFromDevEnv: true, platform: 'ios', referer: CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER, }); @@ -36,6 +37,7 @@ test('Enhance parameters adds correct parameters for a command that requires aut testParameter: 'test', api_setCookie: false, email, + isFromDevEnv: true, platform: 'ios', authToken, referer: CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER, From 6daaea63f1c786416155ff3529fbb7421eabf0eb Mon Sep 17 00:00:00 2001 From: Ionatan Wiznia Date: Fri, 29 Dec 2023 13:35:47 -0600 Subject: [PATCH 25/51] Style --- src/libs/Network/enhanceParameters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Network/enhanceParameters.ts b/src/libs/Network/enhanceParameters.ts index 479335254d34..6ab674e585be 100644 --- a/src/libs/Network/enhanceParameters.ts +++ b/src/libs/Network/enhanceParameters.ts @@ -1,7 +1,7 @@ import getPlatform from '@libs/getPlatform'; import CONFIG from '@src/CONFIG'; -import * as NetworkStore from './NetworkStore'; import * as Environment from "@libs/Environment/Environment"; +import * as NetworkStore from './NetworkStore'; /** * Does this command require an authToken? From 336332a2e8fb2cbab36abfda7a8e0a2464f46320 Mon Sep 17 00:00:00 2001 From: Ionatan Wiznia Date: Fri, 29 Dec 2023 13:40:39 -0600 Subject: [PATCH 26/51] Style --- src/libs/Network/enhanceParameters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Network/enhanceParameters.ts b/src/libs/Network/enhanceParameters.ts index 6ab674e585be..e14acda5ff56 100644 --- a/src/libs/Network/enhanceParameters.ts +++ b/src/libs/Network/enhanceParameters.ts @@ -1,6 +1,6 @@ +import * as Environment from '@libs/Environment/Environment'; import getPlatform from '@libs/getPlatform'; import CONFIG from '@src/CONFIG'; -import * as Environment from "@libs/Environment/Environment"; import * as NetworkStore from './NetworkStore'; /** From beb66011470d5d47db7471be9fbf94a7e426857b Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Sun, 31 Dec 2023 19:03:54 -0500 Subject: [PATCH 27/51] parent report action id --- src/pages/home/HeaderView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 5c0be79d1d85..a3dcb41d1bfc 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -147,7 +147,7 @@ function HeaderView(props) { icon: Expensicons.ChatBubbles, text: translate('common.join'), onSelected: Session.checkIfActionIsAllowed(() => - Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false, props.report.parentReportID, parentReportAction), + Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false, props.report.parentReportID, props.report.parentReportActionID), ), }); } else if ((isChatThread && props.report.notificationPreference.length) || isUserCreatedPolicyRoom || canLeaveRoom) { From e76dd0187889a2188d02574a74fd498ad866442d Mon Sep 17 00:00:00 2001 From: Srikar Parsi Date: Sun, 31 Dec 2023 19:08:37 -0500 Subject: [PATCH 28/51] prettier --- src/pages/home/HeaderView.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index f79cac315d71..e50f0e81b9de 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -147,7 +147,14 @@ function HeaderView(props) { icon: Expensicons.ChatBubbles, text: translate('common.join'), onSelected: Session.checkIfActionIsAllowed(() => - Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false, props.report.parentReportID, props.report.parentReportActionID), + Report.updateNotificationPreference( + props.report.reportID, + props.report.notificationPreference, + CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + false, + props.report.parentReportID, + props.report.parentReportActionID, + ), ), }); } else if ((isChatThread && props.report.notificationPreference.length) || isUserCreatedPolicyRoom || canLeaveRoom) { From ea5384ede666b86f5a223ad1e109b94ebaca569e Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Tue, 2 Jan 2024 17:54:39 +0700 Subject: [PATCH 29/51] fix incorrect text color in light mode in some places --- src/pages/ErrorPage/GenericErrorPage.js | 2 +- src/styles/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/ErrorPage/GenericErrorPage.js b/src/pages/ErrorPage/GenericErrorPage.js index 3b1b9ef9567c..56fb5b970084 100644 --- a/src/pages/ErrorPage/GenericErrorPage.js +++ b/src/pages/ErrorPage/GenericErrorPage.js @@ -85,7 +85,7 @@ function GenericErrorPage({translate}) { src={LogoWordmark} height={30} width={80} - fill={theme.textLight} + fill={theme.text} /> diff --git a/src/styles/index.ts b/src/styles/index.ts index da3c2bc2608c..35b5b5297e03 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2089,7 +2089,7 @@ const styles = (theme: ThemeColors) => }, avatarInnerTextSmall: { - color: theme.textLight, + color: theme.text, fontSize: variables.fontSizeExtraSmall, lineHeight: undefined, marginLeft: -2, @@ -2445,7 +2445,7 @@ const styles = (theme: ThemeColors) => RHPNavigatorContainerNavigatorContainerStyles: (isSmallScreenWidth: boolean) => ({marginLeft: isSmallScreenWidth ? 0 : variables.sideBarWidth, flex: 1} satisfies ViewStyle), avatarInnerTextChat: { - color: theme.textLight, + color: theme.text, fontSize: variables.fontSizeXLarge, fontFamily: fontFamily.EXP_NEW_KANSAS_MEDIUM, textAlign: 'center', From 60e0c990e48bf3fa9ebf1804557f329047332c87 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 2 Jan 2024 14:52:59 +0100 Subject: [PATCH 30/51] Rerun checks From 60b8a68d7e57543e6d18ff9a36a2031f6dab39f5 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 3 Jan 2024 11:00:29 +0700 Subject: [PATCH 31/51] disable checkbox of task preview if not visit the task report --- src/components/ReportActionItem/TaskPreview.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/TaskPreview.js b/src/components/ReportActionItem/TaskPreview.js index a7728045f407..414f030d4fc7 100644 --- a/src/components/ReportActionItem/TaskPreview.js +++ b/src/components/ReportActionItem/TaskPreview.js @@ -123,7 +123,10 @@ function TaskPreview(props) { style={[styles.mr2]} containerStyle={[styles.taskCheckbox]} isChecked={isTaskCompleted} - disabled={!Task.canModifyTask(props.taskReport, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportpolicy, 'role', ''))} + disabled={ + _.isEmpty(props.taskReport) || + !Task.canModifyTask(props.taskReport, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportpolicy, 'role', '')) + } onPress={Session.checkIfActionIsAllowed(() => { if (isTaskCompleted) { Task.reopenTask(props.taskReport); From eec02f92868354c5d9786726964607fa4981e6e2 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 3 Jan 2024 14:58:53 +0700 Subject: [PATCH 32/51] remove trailing slash from getRoute --- src/ROUTES.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 90c0114eaa56..a720c526b58d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -315,17 +315,17 @@ const ROUTES = { }, MONEY_REQUEST_STEP_CONFIRMATION: { route: 'create/:iouType/confirmation/:transactionID/:reportID', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/confirmation/${transactionID}/${reportID}/` as const, + getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/confirmation/${transactionID}/${reportID}` as const, }, MONEY_REQUEST_STEP_AMOUNT: { route: 'create/:iouType/amount/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_CATEGORY: { route: 'create/:iouType/category/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/category/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/category/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_CURRENCY: { route: 'create/:iouType/currency/:transactionID/:reportID/:pageIndex?', @@ -335,37 +335,37 @@ const ROUTES = { MONEY_REQUEST_STEP_DATE: { route: 'create/:iouType/date/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/date/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/date/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_DESCRIPTION: { route: 'create/:iouType/description/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/description/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/description/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_DISTANCE: { route: 'create/:iouType/distance/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_MERCHANT: { route: 'create/:iouType/merchante/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/merchante/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/merchante/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_PARTICIPANTS: { route: 'create/:iouType/participants/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/participants/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/participants/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_SCAN: { route: 'create/:iouType/scan/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/scan/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/scan/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_TAG: { route: 'create/:iouType/tag/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/tag/${transactionID}/${reportID}/`, backTo), + getUrlWithBackToParam(`create/${iouType}/tag/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_WAYPOINT: { route: 'create/:iouType/waypoint/:transactionID/:reportID/:pageIndex', From da025f6acda5cf318ea70db37b9b20c3c8a2e218 Mon Sep 17 00:00:00 2001 From: Fitsum Abebe Date: Wed, 3 Jan 2024 16:27:26 +0300 Subject: [PATCH 33/51] update pressOnEnter to unsubscribe when isFocused is false --- src/components/Button/index.tsx | 6 +++--- .../Button/validateSubmitShortcut/index.native.ts | 5 ++--- src/components/Button/validateSubmitShortcut/index.ts | 5 ++--- src/components/Button/validateSubmitShortcut/types.ts | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index cdafd0b0b93b..d418815ce575 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -169,16 +169,16 @@ function Button( const keyboardShortcutCallback = useCallback( (event?: GestureResponderEvent | KeyboardEvent) => { - if (!validateSubmitShortcut(isFocused, isDisabled, isLoading, event)) { + if (!validateSubmitShortcut(isDisabled, isLoading, event)) { return; } onPress(); }, - [isDisabled, isFocused, isLoading, onPress], + [isDisabled, isLoading, onPress], ); useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ENTER, keyboardShortcutCallback, { - isActive: pressOnEnter && !shouldDisableEnterShortcut, + isActive: pressOnEnter && !shouldDisableEnterShortcut && isFocused, shouldBubble: allowBubble, priority: enterKeyEventListenerPriority, shouldPreventDefault: false, diff --git a/src/components/Button/validateSubmitShortcut/index.native.ts b/src/components/Button/validateSubmitShortcut/index.native.ts index 7687855f109b..c0895be8485b 100644 --- a/src/components/Button/validateSubmitShortcut/index.native.ts +++ b/src/components/Button/validateSubmitShortcut/index.native.ts @@ -3,14 +3,13 @@ import ValidateSubmitShortcut from './types'; /** * Validate if the submit shortcut should be triggered depending on the button state * - * @param isFocused Whether Button is on active screen * @param isDisabled Indicates whether the button should be disabled * @param isLoading Indicates whether the button should be disabled and in the loading state * @return Returns `true` if the shortcut should be triggered */ -const validateSubmitShortcut: ValidateSubmitShortcut = (isFocused, isDisabled, isLoading) => { - if (!isFocused || isDisabled || isLoading) { +const validateSubmitShortcut: ValidateSubmitShortcut = (isDisabled, isLoading) => { + if (isDisabled || isLoading) { return false; } diff --git a/src/components/Button/validateSubmitShortcut/index.ts b/src/components/Button/validateSubmitShortcut/index.ts index 55b3e44192e4..05c3aaed77b7 100644 --- a/src/components/Button/validateSubmitShortcut/index.ts +++ b/src/components/Button/validateSubmitShortcut/index.ts @@ -3,16 +3,15 @@ import ValidateSubmitShortcut from './types'; /** * Validate if the submit shortcut should be triggered depending on the button state * - * @param isFocused Whether Button is on active screen * @param isDisabled Indicates whether the button should be disabled * @param isLoading Indicates whether the button should be disabled and in the loading state * @param event Focused input event * @returns Returns `true` if the shortcut should be triggered */ -const validateSubmitShortcut: ValidateSubmitShortcut = (isFocused, isDisabled, isLoading, event) => { +const validateSubmitShortcut: ValidateSubmitShortcut = (isDisabled, isLoading, event) => { const eventTarget = event?.target as HTMLElement; - if (!isFocused || isDisabled || isLoading || eventTarget.nodeName === 'TEXTAREA') { + if (isDisabled || isLoading || eventTarget.nodeName === 'TEXTAREA') { return false; } diff --git a/src/components/Button/validateSubmitShortcut/types.ts b/src/components/Button/validateSubmitShortcut/types.ts index 9970e1478a4c..38071d588c56 100644 --- a/src/components/Button/validateSubmitShortcut/types.ts +++ b/src/components/Button/validateSubmitShortcut/types.ts @@ -1,5 +1,5 @@ import {GestureResponderEvent} from 'react-native'; -type ValidateSubmitShortcut = (isFocused: boolean, isDisabled: boolean, isLoading: boolean, event?: GestureResponderEvent | KeyboardEvent) => boolean; +type ValidateSubmitShortcut = (isDisabled: boolean, isLoading: boolean, event?: GestureResponderEvent | KeyboardEvent) => boolean; export default ValidateSubmitShortcut; From 4cebe6a365e8791f435a17f06c6aee651a693fff Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 4 Jan 2024 09:47:42 +0100 Subject: [PATCH 34/51] Fix typecheck --- src/components/MenuItem.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index db3a77ccee35..368a3148d0b4 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -78,7 +78,7 @@ type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { titleStyle?: ViewStyle; /** Any adjustments to style when menu item is hovered or pressed */ - hoverAndPressStyle: StyleProp>; + hoverAndPressStyle?: StyleProp>; /** Additional styles to style the description text below the title */ descriptionTextStyle?: StyleProp; @@ -168,7 +168,7 @@ type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { isSelected?: boolean; /** Prop to identify if we should load avatars vertically instead of diagonally */ - shouldStackHorizontally: boolean; + shouldStackHorizontally?: boolean; /** Prop to represent the size of the avatar images to be shown */ avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; @@ -216,7 +216,7 @@ type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { onSecondaryInteraction?: (event: GestureResponderEvent | MouseEvent) => void; /** Array of objects that map display names to their corresponding tooltip */ - titleWithTooltips: DisplayNameWithTooltip[]; + titleWithTooltips?: DisplayNameWithTooltip[]; /** Icon should be displayed in its own color */ displayInDefaultIconColor?: boolean; From 09a65e6b1e8416d7e5c764ac4615d9109ad120cd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 4 Jan 2024 09:59:35 +0100 Subject: [PATCH 35/51] Fix type imports --- src/components/MenuItemList.tsx | 5 +++-- src/components/Section/IconSection.tsx | 5 +++-- src/components/Section/index.tsx | 12 +++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/components/MenuItemList.tsx b/src/components/MenuItemList.tsx index 82097309c580..f83f173a644f 100644 --- a/src/components/MenuItemList.tsx +++ b/src/components/MenuItemList.tsx @@ -1,9 +1,10 @@ import React, {useRef} from 'react'; -import {GestureResponderEvent, View} from 'react-native'; +import type {GestureResponderEvent, View} from 'react-native'; import useSingleExecution from '@hooks/useSingleExecution'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import CONST from '@src/CONST'; -import MenuItem, {MenuItemProps} from './MenuItem'; +import type {MenuItemProps} from './MenuItem'; +import MenuItem from './MenuItem'; type MenuItemLink = string | (() => Promise); diff --git a/src/components/Section/IconSection.tsx b/src/components/Section/IconSection.tsx index ddadbcdb1d77..cc42c6b7ace5 100644 --- a/src/components/Section/IconSection.tsx +++ b/src/components/Section/IconSection.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import {StyleProp, View, ViewStyle} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; import Icon from '@components/Icon'; import useThemeStyles from '@hooks/useThemeStyles'; -import IconAsset from '@src/types/utils/IconAsset'; +import type IconAsset from '@src/types/utils/IconAsset'; type IconSectionProps = { icon?: IconAsset; diff --git a/src/components/Section/index.tsx b/src/components/Section/index.tsx index 47f7605d278b..f24316a5f1bb 100644 --- a/src/components/Section/index.tsx +++ b/src/components/Section/index.tsx @@ -1,11 +1,13 @@ import React from 'react'; -import {StyleProp, View, ViewStyle} from 'react-native'; -import {ValueOf} from 'type-fest'; -import MenuItemList, {MenuItemWithLink} from '@components/MenuItemList'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; +import type {MenuItemWithLink} from '@components/MenuItemList'; +import MenuItemList from '@components/MenuItemList'; import Text from '@components/Text'; import useThemeStyles from '@hooks/useThemeStyles'; -import ChildrenProps from '@src/types/utils/ChildrenProps'; -import IconAsset from '@src/types/utils/IconAsset'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; +import type IconAsset from '@src/types/utils/IconAsset'; import IconSection from './IconSection'; const CARD_LAYOUT = { From 10797bbd69faeae24de394537fa8e05f71292a0d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 4 Jan 2024 23:56:19 +0800 Subject: [PATCH 36/51] remove debounce and simplify loading logic --- .../reimburse/WorkspaceReimburseSection.js | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js index 7cf17c9d498c..a20553a19aaa 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js @@ -9,12 +9,12 @@ import * as Illustrations from '@components/Icon/Illustrations'; import networkPropTypes from '@components/networkPropTypes'; import Section from '@components/Section'; import Text from '@components/Text'; +import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import BankAccount from '@libs/models/BankAccount'; import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as Link from '@userActions/Link'; -import CONST from '@src/CONST'; const propTypes = { /** Policy values needed in the component */ @@ -35,19 +35,19 @@ const propTypes = { function WorkspaceReimburseSection(props) { const theme = useTheme(); const styles = useThemeStyles(); - const [shouldShowLoadingSpinner, setShouldShowLoadingSpinner] = useState(false); + const [shouldShowLoadingSpinner, setShouldShowLoadingSpinner] = useState(true); const achState = lodashGet(props.reimbursementAccount, 'achData.state', ''); const hasVBA = achState === BankAccount.STATE.OPEN; const reimburseReceiptsUrl = `reports?policyID=${props.policy.id}&from=all&type=expense&showStates=Archived&isAdvancedFilterMode=true`; - const debounceSetShouldShowLoadingSpinner = _.debounce(() => { - const isLoading = props.reimbursementAccount.isLoading || false; - if (isLoading !== shouldShowLoadingSpinner) { - setShouldShowLoadingSpinner(isLoading); - } - }, CONST.TIMING.SHOW_LOADING_SPINNER_DEBOUNCE_TIME); + const isLoading = lodashGet(props.reimbursementAccount, 'isLoading', false); + const prevIsLoading = usePrevious(isLoading); + useEffect(() => { - debounceSetShouldShowLoadingSpinner(); - }, [debounceSetShouldShowLoadingSpinner]); + if (prevIsLoading === isLoading) { + return; + } + setShouldShowLoadingSpinner(isLoading); + }, [isLoading]); if (props.network.isOffline) { return ( @@ -62,11 +62,6 @@ function WorkspaceReimburseSection(props) { ); } - // If the reimbursementAccount is loading but not enough time has passed to show a spinner, then render nothing. - if (props.reimbursementAccount.isLoading && !shouldShowLoadingSpinner) { - return null; - } - if (shouldShowLoadingSpinner) { return ( From 17a0a33685e8c2d9fba11b0d06267ea5bfa00460 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 5 Jan 2024 00:10:37 +0800 Subject: [PATCH 37/51] remove unused import --- src/pages/workspace/reimburse/WorkspaceReimburseSection.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js index a20553a19aaa..bce4714870b6 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js @@ -2,7 +2,6 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useEffect, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; -import _ from 'underscore'; import ConnectBankAccountButton from '@components/ConnectBankAccountButton'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; From 711bc6bc30353ec75fc44a13c8080a0f361c072d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 5 Jan 2024 00:11:06 +0800 Subject: [PATCH 38/51] add missing deps --- src/pages/workspace/reimburse/WorkspaceReimburseSection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js index bce4714870b6..00ef284c50ae 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js @@ -46,7 +46,7 @@ function WorkspaceReimburseSection(props) { return; } setShouldShowLoadingSpinner(isLoading); - }, [isLoading]); + }, [prevIsLoading, isLoading]); if (props.network.isOffline) { return ( From a015559e3e5275bfdfced0d3ae4d2d4250ca7437 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 5 Jan 2024 00:23:09 +0800 Subject: [PATCH 39/51] set a button role --- src/components/BaseMiniContextMenuItem.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/BaseMiniContextMenuItem.tsx b/src/components/BaseMiniContextMenuItem.tsx index 1f9a14cdfdee..dcb0ffc71627 100644 --- a/src/components/BaseMiniContextMenuItem.tsx +++ b/src/components/BaseMiniContextMenuItem.tsx @@ -10,6 +10,7 @@ import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManag import variables from '@styles/variables'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Tooltip from './Tooltip/PopoverAnchorTooltip'; +import CONST from '@src/CONST'; type BaseMiniContextMenuItemProps = { /** @@ -66,6 +67,7 @@ function BaseMiniContextMenuItem({tooltipText, onPress, children, isDelayButtonS event.preventDefault(); }} accessibilityLabel={tooltipText} + role={CONST.ROLE.BUTTON} style={({hovered, pressed}) => [ styles.reportActionContextMenuMiniButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, isDelayButtonStateComplete)), From ffd4db3556702a4c5875d6c15231fd3754af9e63 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 5 Jan 2024 00:23:25 +0800 Subject: [PATCH 40/51] set mini context menu unselectable --- src/styles/utils/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index b3b4924ebb19..61549fcd2177 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1345,6 +1345,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ ...(isReportActionItemGrouped ? positioning.tn8 : positioning.tn4), ...positioning.r4, ...styles.cursorDefault, + ...styles.userSelectNone, position: 'absolute', zIndex: 8, }), From edd2a205ea1aac9a416618f5595107727ace69e5 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 5 Jan 2024 00:30:53 +0800 Subject: [PATCH 41/51] prettier --- src/components/BaseMiniContextMenuItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/BaseMiniContextMenuItem.tsx b/src/components/BaseMiniContextMenuItem.tsx index dcb0ffc71627..7bed44cd8f13 100644 --- a/src/components/BaseMiniContextMenuItem.tsx +++ b/src/components/BaseMiniContextMenuItem.tsx @@ -8,9 +8,9 @@ import DomUtils from '@libs/DomUtils'; import getButtonState from '@libs/getButtonState'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Tooltip from './Tooltip/PopoverAnchorTooltip'; -import CONST from '@src/CONST'; type BaseMiniContextMenuItemProps = { /** From a70f818a5b85ca4cbf5351313c527f10b394d1de Mon Sep 17 00:00:00 2001 From: rory Date: Thu, 4 Jan 2024 22:38:23 -0800 Subject: [PATCH 42/51] Don't skip successData or failureData --- src/libs/actions/OnyxUpdates.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 2291e6d0af4a..a0772db49585 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -7,6 +7,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxUpdateEvent, OnyxUpdatesFromServer, Request} from '@src/types/onyx'; import type Response from '@src/types/onyx/Response'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as QueuedOnyxUpdates from './QueuedOnyxUpdates'; // This key needs to be separate from ONYXKEYS.ONYX_UPDATES_FROM_SERVER so that it can be updated without triggering the callback when the server IDs are updated. If that @@ -74,7 +75,18 @@ function apply({lastUpdateID, type, request, response, updates}: OnyxUpdatesFrom Log.info(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, false, {command: request?.command}); if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) <= lastUpdateIDAppliedToClient) { - Log.info('[OnyxUpdateManager] Update received was older or the same than current state, returning without applying the updates', false); + Log.info('[OnyxUpdateManager] Update received was older than or the same as current state, returning without applying the updates other than successData and failureData'); + + // In this case, we're already received the OnyxUpdate included in the response, so we don't need to apply it again. + // However, we do need to apply the successData and failureData from the request + if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response && (!isEmptyObject(request.successData) || !isEmptyObject(request.failureData))) { + Log.info('[OnyxUpdateManager] Applying success or failure data from request without onyxData from response'); + + // We use a spread here instead of delete because we don't want to change the response for other middlewares + const {onyxData, ...responseWithoutOnyxData} = response; + return applyHTTPSOnyxUpdates(request, responseWithoutOnyxData); + } + return Promise.resolve(); } if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { From 295ae98916189c4920ab53903b94f4953fde68c9 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Fri, 5 Jan 2024 22:12:59 +0700 Subject: [PATCH 43/51] fix set source and check local file --- src/components/AttachmentModal.js | 4 ++-- src/libs/ReceiptUtils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index d24d1e18907f..aa6c16798ccd 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -126,7 +126,7 @@ function AttachmentModal(props) { const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired); const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState(''); const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null); - const [source, setSource] = useState(props.source); + const [source, setSource] = useState(() => props.source); const [modalType, setModalType] = useState(CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE); const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false); const [confirmButtonFadeAnimation] = useState(() => new Animated.Value(1)); @@ -359,7 +359,7 @@ function AttachmentModal(props) { }, []); useEffect(() => { - setSource(props.source); + setSource(() => props.source); }, [props.source]); useEffect(() => { diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index 1adb5957587b..d2fddf87670c 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -66,7 +66,7 @@ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string image = ReceiptSVG; } - const isLocalFile = typeof path === 'number' || path.startsWith('blob:') || path.startsWith('file:'); + const isLocalFile = typeof path === 'number' || path.startsWith('blob:') || path.startsWith('file:') || path === ReceiptGeneric; return {thumbnail: image, image: path, isLocalFile}; } From cb007479ec523ded9c751797fefa7848e8bf5849 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Fri, 5 Jan 2024 21:20:58 +0000 Subject: [PATCH 44/51] Update version to 1.4.22-1 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index aaa6aaeb7a78..13f9dba2bbe6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -96,8 +96,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001042200 - versionName "1.4.22-0" + versionCode 1001042201 + versionName "1.4.22-1" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ecf3b8c9cad9..dd86b6fd7bd5 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.22.0 + 1.4.22.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 1ec1aeb6ce14..392403d19961 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.22.0 + 1.4.22.1 diff --git a/package-lock.json b/package-lock.json index 2c1da1670e19..f30d9bc84ec0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.22-0", + "version": "1.4.22-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.22-0", + "version": "1.4.22-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 118f63e45879..308e5808dd05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.22-0", + "version": "1.4.22-1", "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 548830a07e57985e6cbf86938fafe974c171e885 Mon Sep 17 00:00:00 2001 From: Pujan Date: Sat, 6 Jan 2024 05:03:25 +0530 Subject: [PATCH 45/51] safe check for isTaxTrackingEnabled --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 7ec95aec951f..2fee67a3d632 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -277,7 +277,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowTags = isPolicyExpenseChat && OptionsListUtils.hasEnabledOptions(_.values(policyTagList)); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy.isTaxTrackingEnabled; + const shouldShowTax = isPolicyExpenseChat && policy && policy.isTaxTrackingEnabled; // A flag for showing the billable field const shouldShowBillable = !lodashGet(policy, 'disabledFields.defaultBillable', true); From 50f20108cc02a89932df7a96056f0aef07558c1b Mon Sep 17 00:00:00 2001 From: OSBotify Date: Sat, 6 Jan 2024 00:31:15 +0000 Subject: [PATCH 46/51] Update version to 1.4.22-2 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 13f9dba2bbe6..ddef060e3a96 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -96,8 +96,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001042201 - versionName "1.4.22-1" + versionCode 1001042202 + versionName "1.4.22-2" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index dd86b6fd7bd5..1c4a33d37b91 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.22.1 + 1.4.22.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 392403d19961..31fdafe32980 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.22.1 + 1.4.22.2 diff --git a/package-lock.json b/package-lock.json index f30d9bc84ec0..065fc0055291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.22-1", + "version": "1.4.22-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.22-1", + "version": "1.4.22-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 308e5808dd05..f6d401a8266b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.22-1", + "version": "1.4.22-2", "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 56f2445f4281752405226ab6d1fe692c08631fe2 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 8 Jan 2024 10:09:43 +0700 Subject: [PATCH 47/51] revert redundant changes --- src/ROUTES.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c845a6dc11b4..e8a860582bb1 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -323,7 +323,7 @@ const ROUTES = { getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_TAX_RATE: { - route: 'create/:iouType/taxRate/:transactionID/:reportID', + route: 'create/:iouType/taxRate/:transactionID/:reportID?', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo: string) => getUrlWithBackToParam(`create/${iouType}/taxRate/${transactionID}/${reportID}`, backTo), }, @@ -333,7 +333,7 @@ const ROUTES = { getUrlWithBackToParam(`create/${iouType}/taxAmount/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_CATEGORY: { - route: 'create/:iouType/category/:transactionID/:reportID?', + route: 'create/:iouType/category/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/category/${transactionID}/${reportID}`, backTo), }, From d9e75d5b1eab9525d83facd34370a4fe21bee374 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Mon, 8 Jan 2024 10:48:07 +0700 Subject: [PATCH 48/51] fix condition check assets web --- src/libs/ReceiptUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index d2fddf87670c..bcba68a3a0bd 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -66,7 +66,7 @@ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string image = ReceiptSVG; } - const isLocalFile = typeof path === 'number' || path.startsWith('blob:') || path.startsWith('file:') || path === ReceiptGeneric; + const isLocalFile = typeof path === 'number' || path.startsWith('blob:') || path.startsWith('file:') || path.startsWith('/'); return {thumbnail: image, image: path, isLocalFile}; } From bbfa54184346c4db8d0d848037e67e99155bc270 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 8 Jan 2024 05:57:48 +0000 Subject: [PATCH 49/51] Update version to 1.4.22-3 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index ddef060e3a96..63aa4215cd90 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -96,8 +96,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001042202 - versionName "1.4.22-2" + versionCode 1001042203 + versionName "1.4.22-3" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 1c4a33d37b91..f58687c66c63 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.22.2 + 1.4.22.3 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 31fdafe32980..b7b8c9d3416b 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.22.2 + 1.4.22.3 diff --git a/package-lock.json b/package-lock.json index 065fc0055291..55bfafbec2f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.22-2", + "version": "1.4.22-3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.22-2", + "version": "1.4.22-3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f6d401a8266b..7264cb5fa25e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.22-2", + "version": "1.4.22-3", "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 3bb8349c449e52579ae64b5d905645f9bd761525 Mon Sep 17 00:00:00 2001 From: Jack Nam <30609178+thienlnam@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:58:02 +0800 Subject: [PATCH 50/51] Revert "Fix composer is focused after opening another popover" --- .../ComposerWithSuggestions.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index 1e8439c0086b..6c1d71625dc9 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -5,7 +5,6 @@ import {findNodeHandle, InteractionManager, NativeModules, View} from 'react-nat import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import Composer from '@components/Composer'; -import {PopoverContext} from '@components/PopoverProvider'; import withKeyboardState from '@components/withKeyboardState'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; @@ -108,7 +107,6 @@ function ComposerWithSuggestions({ // For testing children, }) { - const {isOpen: isPopoverOpen} = React.useContext(PopoverContext); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -411,15 +409,9 @@ function ComposerWithSuggestions({ * @param {Boolean} [shouldDelay=false] Impose delay before focusing the composer * @memberof ReportActionCompose */ - const focus = useCallback( - (shouldDelay = false) => { - if (isPopoverOpen) { - return; - } - focusComposerWithDelay(textInputRef.current)(shouldDelay); - }, - [isPopoverOpen], - ); + const focus = useCallback((shouldDelay = false) => { + focusComposerWithDelay(textInputRef.current)(shouldDelay); + }, []); const setUpComposeFocusManager = useCallback(() => { // This callback is used in the contextMenuActions to manage giving focus back to the compose input. From 72693dc25bdf4e345026933e21273d9df901e250 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 8 Jan 2024 11:33:12 +0100 Subject: [PATCH 51/51] fix routes for tax rate and amount page --- .../step/IOURequestStepTaxAmountPage.js | 2 +- .../request/step/IOURequestStepTaxRatePage.js | 25 ++++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 8ee3abb56d00..a9f3195bfdfd 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -82,7 +82,7 @@ function IOURequestStepTaxAmountPage({ ); const navigateBack = () => { - Navigation.goBack(isEditing ? ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID) : ROUTES.HOME); + Navigation.goBack(backTo || ROUTES.HOME); }; const navigateToCurrencySelectionPage = () => { diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index bae08cd8cb62..cf91ac1e1812 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -16,21 +15,13 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; const propTypes = { - /** Route from navigation */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, + /** Navigation route context info provided by react navigation */ + route: IOURequestStepRoutePropTypes.isRequired, /* Onyx Props */ /** Collection of tax rates attached to a policy */ @@ -52,16 +43,16 @@ const getTaxAmount = (taxRates, selectedTaxRate, amount) => { function IOURequestStepTaxRatePage({ route: { - params: {iouType, reportID}, + params: {backTo}, }, policyTaxRates, transaction, }) { const {translate} = useLocalize(); - function navigateBack() { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); - } + const navigateBack = () => { + Navigation.goBack(backTo || ROUTES.HOME); + }; const defaultTaxKey = policyTaxRates.defaultExternalID; const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; @@ -73,7 +64,7 @@ function IOURequestStepTaxRatePage({ IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes); IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + Navigation.goBack(backTo || ROUTES.HOME); }; return (