From 197dcc3acf5823e3c8eb470aa4f24e07238b0bca Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 6 Oct 2023 20:19:50 +0700 Subject: [PATCH 01/41] fix: 28969 --- src/pages/home/ReportScreen.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 78f6edcf7dd3..c13858d1ff05 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -39,6 +39,7 @@ import DragAndDropProvider from '../../components/DragAndDrop/Provider'; import usePrevious from '../../hooks/usePrevious'; import CONST from '../../CONST'; import withCurrentReportID, {withCurrentReportIDPropTypes, withCurrentReportIDDefaultProps} from '../../components/withCurrentReportID'; +import useNetwork from '../../hooks/useNetwork'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -156,6 +157,7 @@ function ReportScreen({ const prevReport = usePrevious(report); const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isBannerVisible, setIsBannerVisible] = useState(true); + const {isOffline} = useNetwork(); const reportID = getReportID(route); const {addWorkspaceRoomOrChatPendingAction, addWorkspaceRoomOrChatErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report); @@ -362,8 +364,8 @@ function ReportScreen({ // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useMemo( - () => (!firstRenderRef.current && !report.reportID && !isOptimisticDelete && !reportMetadata.isLoadingReportActions && !isLoading && !userLeavingStatus) || shouldHideReport, - [report, reportMetadata, isLoading, shouldHideReport, isOptimisticDelete, userLeavingStatus], + () => (!firstRenderRef.current && !report.reportID && (isOffline || (!reportMetadata.isLoadingReportActions && !isOptimisticDelete)) && !isLoading && !userLeavingStatus) || shouldHideReport, + [report, reportMetadata, isLoading, shouldHideReport, isOptimisticDelete, userLeavingStatus, isOffline], ); return ( From 338e31fb6847bbf9c1a9a53bb1d2c100c450c004 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 3 Nov 2023 13:25:45 +0700 Subject: [PATCH 02/41] fix created view is flickered when marking as unread --- src/CONST.ts | 2 +- src/pages/home/report/ReportActionItem.js | 37 +++++++++-------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 29bb0b83aaee..155d1c47550f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2786,7 +2786,7 @@ const CONST = { HORIZONTAL_SPACER: { DEFAULT_BORDER_BOTTOM_WIDTH: 1, DEFAULT_MARGIN_VERTICAL: 8, - HIDDEN_MARGIN_VERTICAL: 0, + HIDDEN_MARGIN_VERTICAL: 4, HIDDEN_BORDER_BOTTOM_WIDTH: 0, }, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 4da88fd5d352..d92d27abacd5 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -555,15 +555,9 @@ function ReportActionItem(props) { }; if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) { - let content = ( - - ); const parentReportAction = ReportActionsUtils.getParentReportAction(props.report); if (ReportActionsUtils.isTransactionThread(parentReportAction)) { - content = ( + return ( ); - } else { - content = ( - - ); } + return ( + + ); } if (ReportUtils.isExpenseReport(props.report) || ReportUtils.isIOUReport(props.report)) { - content = ( + return ( {content}; + return ( + + ); } if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED) { return ; From 78e95df73efd15e79ce0b2ff96795c64f6df6fb5 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 9 Nov 2023 15:31:33 +0700 Subject: [PATCH 03/41] fix test --- src/pages/home/report/ReportActionItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index b4b0100372a8..d9ae2aa42b4b 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -584,7 +584,7 @@ function ReportActionItem(props) { ); - } + } return ( <> From 46d5f7efb4cda0cb650310bbda7ba4ffac3b28a7 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 9 Nov 2023 15:32:07 +0700 Subject: [PATCH 04/41] fix lint --- src/pages/home/report/ReportActionItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index d9ae2aa42b4b..b4b0100372a8 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -584,7 +584,7 @@ function ReportActionItem(props) { ); - } + } return ( <> From 6a33c6118a37392eddba88b659c1c9e5bb81f479 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Mon, 13 Nov 2023 11:42:02 +0700 Subject: [PATCH 05/41] fix animated --- src/components/SpacerView.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/SpacerView.js b/src/components/SpacerView.js index 60ff7fd85e55..9509219c0ac7 100644 --- a/src/components/SpacerView.js +++ b/src/components/SpacerView.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; +import usePrevious from '@hooks/usePrevious'; import stylePropTypes from '@styles/stylePropTypes'; import * as StyleUtils from '@styles/StyleUtils'; import CONST from '@src/CONST'; @@ -23,22 +24,30 @@ const defaultProps = { }; function SpacerView({shouldShow = true, style = []}) { - const marginVertical = useSharedValue(CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL); - const borderBottomWidth = useSharedValue(CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH); + const marginVertical = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL : CONST.HORIZONTAL_SPACER.HIDDEN_MARGIN_VERTICAL); + const borderBottomWidth = useSharedValue(shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH : CONST.HORIZONTAL_SPACER.HIDDEN_BORDER_BOTTOM_WIDTH); + const prevShouldShow = usePrevious(shouldShow); + + const duration = CONST.ANIMATED_TRANSITION; const animatedStyles = useAnimatedStyle(() => ({ - marginVertical: marginVertical.value, - borderBottomWidth: borderBottomWidth.value, + borderBottomWidth: withTiming(borderBottomWidth.value, {duration}), + marginTop: withTiming(marginVertical.value, {duration}), + marginBottom: withTiming(marginVertical.value, {duration}), })); React.useEffect(() => { - const duration = CONST.ANIMATED_TRANSITION; + if (shouldShow === prevShouldShow) { + return; + } const values = { marginVertical: shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_MARGIN_VERTICAL : CONST.HORIZONTAL_SPACER.HIDDEN_MARGIN_VERTICAL, borderBottomWidth: shouldShow ? CONST.HORIZONTAL_SPACER.DEFAULT_BORDER_BOTTOM_WIDTH : CONST.HORIZONTAL_SPACER.HIDDEN_BORDER_BOTTOM_WIDTH, }; - marginVertical.value = withTiming(values.marginVertical, {duration}); - borderBottomWidth.value = withTiming(values.borderBottomWidth, {duration}); - }, [shouldShow, borderBottomWidth, marginVertical]); + marginVertical.value = values.marginVertical; + borderBottomWidth.value = values.borderBottomWidth; + + // eslint-disable-next-line react-hooks/exhaustive-deps -- we only need to trigger when shouldShow prop is changed + }, [shouldShow, prevShouldShow]); return ; } From d15cd01b7b622aa5b8e80463f94c3d707f410395 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 28 Nov 2023 15:00:10 +0100 Subject: [PATCH 06/41] mark the first item in getSortedReportActions --- src/libs/ReportActionsUtils.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index bd475a57954e..22a53bb9379c 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -155,14 +155,14 @@ function isTransactionThread(parentReportAction: OnyxEntry): boole * This gives us a stable order even in the case of multiple reportActions created on the same millisecond * */ -function getSortedReportActions(reportActions: ReportAction[] | null, shouldSortInDescendingOrder = false): ReportAction[] { +function getSortedReportActions(reportActions: ReportAction[] | null, shouldSortInDescendingOrder = false, shouldMarkTheFirstItem = false): ReportAction[] { if (!Array.isArray(reportActions)) { throw new Error(`ReportActionsUtils.getSortedReportActions requires an array, received ${typeof reportActions}`); } const invertedMultiplier = shouldSortInDescendingOrder ? -1 : 1; - return reportActions?.filter(Boolean).sort((first, second) => { + const sortedActions = reportActions?.filter(Boolean).sort((first, second) => { // First sort by timestamp if (first.created !== second.created) { return (first.created < second.created ? -1 : 1) * invertedMultiplier; @@ -182,6 +182,16 @@ function getSortedReportActions(reportActions: ReportAction[] | null, shouldSort // will be consistent across all users and devices return (first.reportActionID < second.reportActionID ? -1 : 1) * invertedMultiplier; }); + + // Mark the first item if shouldMarkTheFirstItem is true + if (shouldMarkTheFirstItem && sortedActions?.length > 0) { + sortedActions[0] = { + ...sortedActions[0], + isNewestReportAction: true, + }; + } + + return sortedActions; } /** @@ -448,7 +458,7 @@ function getSortedReportActionsForDisplay(reportActions: ReportActions | null): .filter(([key, reportAction]) => shouldReportActionBeVisible(reportAction, key)) .map((entry) => entry[1]); const baseURLAdjustedReportActions = filteredReportActions.map((reportAction) => replaceBaseURL(reportAction)); - return getSortedReportActions(baseURLAdjustedReportActions, true); + return getSortedReportActions(baseURLAdjustedReportActions, true, true); } /** From c476637583c1dec220ae04427b36bd10bd1f3d72 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 28 Nov 2023 15:01:24 +0100 Subject: [PATCH 07/41] update type ReportActionBase --- src/types/onyx/ReportAction.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 891a0ffcb7b8..d407275f614a 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -50,6 +50,9 @@ type Message = { /** ID of a task report */ taskReportID?: string; + + /** We manually add this field while sorting to detect the end of the list */ + isNewestReportAction?: boolean; }; type Person = { From 69223cac5accc93cdffe390d01027fa588d5c51e Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 28 Nov 2023 15:08:03 +0100 Subject: [PATCH 08/41] use isNewestReportAction for loadNewerChats --- src/pages/home/report/ReportActionsView.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index b4d21de919bf..6f46a6616745 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -96,6 +96,7 @@ function ReportActionsView(props) { const isFocused = useIsFocused(); const reportID = props.report.reportID; + const isNewestReportAction = lodashGet(props.reportActions[0], 'isNewestReportAction'); /** * @returns {Boolean} @@ -199,7 +200,7 @@ function ReportActionsView(props) { const loadNewerChats = useMemo( () => _.throttle(({distanceFromStart}) => { - if (props.isLoadingNewerReportActions || props.isLoadingInitialReportActions) { + if (props.isLoadingNewerReportActions || props.isLoadingInitialReportActions || isNewestReportAction) { return; } @@ -221,7 +222,7 @@ function ReportActionsView(props) { const newestReportAction = _.first(props.reportActions); Report.getNewerActions(reportID, newestReportAction.reportActionID); }, 500), - [props.isLoadingNewerReportActions, props.isLoadingInitialReportActions, props.reportActions, reportID], + [props.isLoadingNewerReportActions, props.isLoadingInitialReportActions, props.reportActions, reportID, isNewestReportAction], ); /** From 683aa4eab2f87f1c65fa4c790a207509c52cc056 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 28 Nov 2023 16:56:40 +0100 Subject: [PATCH 09/41] fix types --- src/types/onyx/ReportAction.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index d407275f614a..0668ce58ccb3 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -50,9 +50,6 @@ type Message = { /** ID of a task report */ taskReportID?: string; - - /** We manually add this field while sorting to detect the end of the list */ - isNewestReportAction?: boolean; }; type Person = { @@ -141,6 +138,9 @@ type ReportActionBase = { isAttachment?: boolean; childRecentReceiptTransactionIDs?: Record; reportID?: string; + + /** We manually add this field while sorting to detect the end of the list */ + isNewestReportAction?: boolean; }; type ReportAction = ReportActionBase & OriginalMessage; From 2c702412e0279cd3891511aecd7d0628a4e1b196 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 29 Nov 2023 08:07:17 +0100 Subject: [PATCH 10/41] tests --- src/libs/ReportActionsUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 22a53bb9379c..781392304acc 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -183,7 +183,7 @@ function getSortedReportActions(reportActions: ReportAction[] | null, shouldSort return (first.reportActionID < second.reportActionID ? -1 : 1) * invertedMultiplier; }); - // Mark the first item if shouldMarkTheFirstItem is true + // If shouldMarkTheFirstItem is true, label the first reportAction as isNewestReportAction if (shouldMarkTheFirstItem && sortedActions?.length > 0) { sortedActions[0] = { ...sortedActions[0], From 5bd131485f9848d8f16b5d53f839fb1a97cc5756 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 29 Nov 2023 08:34:36 +0100 Subject: [PATCH 11/41] update getSortedReportActionsForDisplay --- src/libs/ReportActionsUtils.ts | 10 +++++----- src/pages/home/ReportScreen.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 781392304acc..08b5ac49f301 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -155,7 +155,7 @@ function isTransactionThread(parentReportAction: OnyxEntry): boole * This gives us a stable order even in the case of multiple reportActions created on the same millisecond * */ -function getSortedReportActions(reportActions: ReportAction[] | null, shouldSortInDescendingOrder = false, shouldMarkTheFirstItem = false): ReportAction[] { +function getSortedReportActions(reportActions: ReportAction[] | null, shouldSortInDescendingOrder = false, shouldMarkTheFirstItemAsNewest = false): ReportAction[] { if (!Array.isArray(reportActions)) { throw new Error(`ReportActionsUtils.getSortedReportActions requires an array, received ${typeof reportActions}`); } @@ -183,8 +183,8 @@ function getSortedReportActions(reportActions: ReportAction[] | null, shouldSort return (first.reportActionID < second.reportActionID ? -1 : 1) * invertedMultiplier; }); - // If shouldMarkTheFirstItem is true, label the first reportAction as isNewestReportAction - if (shouldMarkTheFirstItem && sortedActions?.length > 0) { + // If shouldMarkTheFirstItemAsNewest is true, label the first reportAction as isNewestReportAction + if (shouldMarkTheFirstItemAsNewest && sortedActions?.length > 0) { sortedActions[0] = { ...sortedActions[0], isNewestReportAction: true, @@ -453,12 +453,12 @@ function filterOutDeprecatedReportActions(reportActions: ReportActions | null): * to ensure they will always be displayed in the same order (in case multiple actions have the same timestamp). * This is all handled with getSortedReportActions() which is used by several other methods to keep the code DRY. */ -function getSortedReportActionsForDisplay(reportActions: ReportActions | null): ReportAction[] { +function getSortedReportActionsForDisplay(reportActions: ReportActions | null, shouldMarkTheFirstItemAsNewest = false): ReportAction[] { const filteredReportActions = Object.entries(reportActions ?? {}) .filter(([key, reportAction]) => shouldReportActionBeVisible(reportAction, key)) .map((entry) => entry[1]); const baseURLAdjustedReportActions = filteredReportActions.map((reportAction) => replaceBaseURL(reportAction)); - return getSortedReportActions(baseURLAdjustedReportActions, true, true); + return getSortedReportActions(baseURLAdjustedReportActions, true, shouldMarkTheFirstItemAsNewest); } /** diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 33646e7129cd..782681fdf55b 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -474,7 +474,7 @@ export default compose( reportActions: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, canEvict: false, - selector: ReportActionsUtils.getSortedReportActionsForDisplay, + selector: (reportActions) => ReportActionsUtils.getSortedReportActionsForDisplay(reportActions, true), }, report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`, From ff1c9f367e147b1f848483d2d6cd39d83bd90825 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 29 Nov 2023 08:47:37 +0100 Subject: [PATCH 12/41] update test --- tests/unit/ReportActionsUtilsTest.js | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/unit/ReportActionsUtilsTest.js b/tests/unit/ReportActionsUtilsTest.js index 9973515c44de..dcc71668a823 100644 --- a/tests/unit/ReportActionsUtilsTest.js +++ b/tests/unit/ReportActionsUtilsTest.js @@ -185,10 +185,69 @@ describe('ReportActionsUtils', () => { message: [{html: 'I have changed the task'}], }, ]; + const result = ReportActionsUtils.getSortedReportActionsForDisplay(input); input.pop(); expect(result).toStrictEqual(input); }); + + describe('getSortedReportActionsForDisplay with marked the first reportAction', () => { + it('should filter out non-whitelisted actions', () => { + const input = [ + { + created: '2022-11-13 22:27:01.825', + reportActionID: '8401445780099176', + actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-12 22:27:01.825', + reportActionID: '6401435781022176', + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-11 22:27:01.825', + reportActionID: '2962390724708756', + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-10 22:27:01.825', + reportActionID: '1609646094152486', + actionName: CONST.REPORT.ACTIONS.TYPE.RENAMED, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-09 22:27:01.825', + reportActionID: '8049485084562457', + actionName: CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.UPDATE_FIELD, + message: [{html: 'updated the Approval Mode from "Submit and Approve" to "Submit and Close"'}], + }, + { + created: '2022-11-08 22:27:06.825', + reportActionID: '1661970171066216', + actionName: CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED, + message: [{html: 'Waiting for the bank account'}], + }, + { + created: '2022-11-06 22:27:08.825', + reportActionID: '1661970171066220', + actionName: CONST.REPORT.ACTIONS.TYPE.TASKEDITED, + message: [{html: 'I have changed the task'}], + }, + ]; + + const resultWithoutNewestFlag = ReportActionsUtils.getSortedReportActionsForDisplay(input); + const resultWithNewestFlag = ReportActionsUtils.getSortedReportActionsForDisplay(input, true); + input.pop(); + // Mark the newest report action as the newest report action + resultWithoutMark[0] = { + ...resultWithoutMark[0], + isNewestReportAction: true, + }; + expect(resultWithoutNewestFlag).toStrictEqual(resultWithNewestFlag); + }); it('should filter out closed actions', () => { const input = [ { From e6b6ed82054fd82acbf8fadc01b96faed02dde0f Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 29 Nov 2023 09:13:05 +0100 Subject: [PATCH 13/41] lint --- tests/unit/ReportActionsUtilsTest.js | 112 ++++++++++++++------------- 1 file changed, 57 insertions(+), 55 deletions(-) diff --git a/tests/unit/ReportActionsUtilsTest.js b/tests/unit/ReportActionsUtilsTest.js index dcc71668a823..b8b6eb5e7673 100644 --- a/tests/unit/ReportActionsUtilsTest.js +++ b/tests/unit/ReportActionsUtilsTest.js @@ -191,63 +191,65 @@ describe('ReportActionsUtils', () => { expect(result).toStrictEqual(input); }); - describe('getSortedReportActionsForDisplay with marked the first reportAction', () => { - it('should filter out non-whitelisted actions', () => { - const input = [ - { - created: '2022-11-13 22:27:01.825', - reportActionID: '8401445780099176', - actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT, - message: [{html: 'Hello world'}], - }, - { - created: '2022-11-12 22:27:01.825', - reportActionID: '6401435781022176', - actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, - message: [{html: 'Hello world'}], - }, - { - created: '2022-11-11 22:27:01.825', - reportActionID: '2962390724708756', - actionName: CONST.REPORT.ACTIONS.TYPE.IOU, - message: [{html: 'Hello world'}], - }, - { - created: '2022-11-10 22:27:01.825', - reportActionID: '1609646094152486', - actionName: CONST.REPORT.ACTIONS.TYPE.RENAMED, - message: [{html: 'Hello world'}], - }, - { - created: '2022-11-09 22:27:01.825', - reportActionID: '8049485084562457', - actionName: CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.UPDATE_FIELD, - message: [{html: 'updated the Approval Mode from "Submit and Approve" to "Submit and Close"'}], - }, - { - created: '2022-11-08 22:27:06.825', - reportActionID: '1661970171066216', - actionName: CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED, - message: [{html: 'Waiting for the bank account'}], - }, - { - created: '2022-11-06 22:27:08.825', - reportActionID: '1661970171066220', - actionName: CONST.REPORT.ACTIONS.TYPE.TASKEDITED, - message: [{html: 'I have changed the task'}], - }, - ]; + describe('getSortedReportActionsForDisplay with marked the first reportAction', () => { + it('should filter out non-whitelisted actions', () => { + const input = [ + { + created: '2022-11-13 22:27:01.825', + reportActionID: '8401445780099176', + actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-12 22:27:01.825', + reportActionID: '6401435781022176', + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-11 22:27:01.825', + reportActionID: '2962390724708756', + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-10 22:27:01.825', + reportActionID: '1609646094152486', + actionName: CONST.REPORT.ACTIONS.TYPE.RENAMED, + message: [{html: 'Hello world'}], + }, + { + created: '2022-11-09 22:27:01.825', + reportActionID: '8049485084562457', + actionName: CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.UPDATE_FIELD, + message: [{html: 'updated the Approval Mode from "Submit and Approve" to "Submit and Close"'}], + }, + { + created: '2022-11-08 22:27:06.825', + reportActionID: '1661970171066216', + actionName: CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED, + message: [{html: 'Waiting for the bank account'}], + }, + { + created: '2022-11-06 22:27:08.825', + reportActionID: '1661970171066220', + actionName: CONST.REPORT.ACTIONS.TYPE.TASKEDITED, + message: [{html: 'I have changed the task'}], + }, + ]; - const resultWithoutNewestFlag = ReportActionsUtils.getSortedReportActionsForDisplay(input); - const resultWithNewestFlag = ReportActionsUtils.getSortedReportActionsForDisplay(input, true); - input.pop(); - // Mark the newest report action as the newest report action - resultWithoutMark[0] = { - ...resultWithoutMark[0], - isNewestReportAction: true, - }; - expect(resultWithoutNewestFlag).toStrictEqual(resultWithNewestFlag); + const resultWithoutNewestFlag = ReportActionsUtils.getSortedReportActionsForDisplay(input); + const resultWithNewestFlag = ReportActionsUtils.getSortedReportActionsForDisplay(input, true); + input.pop(); + // Mark the newest report action as the newest report action + resultWithoutNewestFlag[0] = { + ...resultWithoutNewestFlag[0], + isNewestReportAction: true, + }; + expect(resultWithoutNewestFlag).toStrictEqual(resultWithNewestFlag); + }); }); + it('should filter out closed actions', () => { const input = [ { From 20ddda989d726f338fe1d898812b4a42508bacda Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 29 Nov 2023 12:38:09 +0100 Subject: [PATCH 14/41] do not display new line when creating request money --- src/pages/home/report/ReportActionsList.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index e1230d7219db..6ad79f9f488a 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -335,6 +335,12 @@ function ReportActionsList({ (reportAction, index) => { let shouldDisplay = false; if (!currentUnreadMarker) { + // Check if the report type is "REPORTREVIEW" and last actor is the current user. + // Return shouldDisplay new marker action (terminate flow). + // This is to avoid displaying the new line marker when a current userrequests money. + if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && reportAction.childLastActorAccountID === Report.getCurrentUserAccountID()) { + return shouldDisplay; + } const nextMessage = sortedReportActions[index + 1]; const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadTimeRef.current); shouldDisplay = isCurrentMessageUnread && (!nextMessage || !isMessageUnread(nextMessage, lastReadTimeRef.current)); From 6bac4f8e367029756507220784bdde6f95e6bc9f Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 29 Nov 2023 14:21:44 +0100 Subject: [PATCH 15/41] use isReportPreviewAction --- src/pages/home/report/ReportActionsList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 6ad79f9f488a..6da3c1d34eb7 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -338,7 +338,7 @@ function ReportActionsList({ // Check if the report type is "REPORTREVIEW" and last actor is the current user. // Return shouldDisplay new marker action (terminate flow). // This is to avoid displaying the new line marker when a current userrequests money. - if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && reportAction.childLastActorAccountID === Report.getCurrentUserAccountID()) { + if (ReportActionsUtils.isReportPreviewAction(reportAction) && reportAction.childLastActorAccountID === Report.getCurrentUserAccountID()) { return shouldDisplay; } const nextMessage = sortedReportActions[index + 1]; From 8d308c5e3b8211128b1bb8fff5ca3c595b784b8b Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 30 Nov 2023 06:39:42 +0100 Subject: [PATCH 16/41] fix review comment --- src/pages/home/report/ReportActionsList.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 6da3c1d34eb7..716a8ab695c2 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -335,17 +335,15 @@ function ReportActionsList({ (reportAction, index) => { let shouldDisplay = false; if (!currentUnreadMarker) { - // Check if the report type is "REPORTREVIEW" and last actor is the current user. - // Return shouldDisplay new marker action (terminate flow). - // This is to avoid displaying the new line marker when a current userrequests money. - if (ReportActionsUtils.isReportPreviewAction(reportAction) && reportAction.childLastActorAccountID === Report.getCurrentUserAccountID()) { - return shouldDisplay; - } const nextMessage = sortedReportActions[index + 1]; const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadTimeRef.current); shouldDisplay = isCurrentMessageUnread && (!nextMessage || !isMessageUnread(nextMessage, lastReadTimeRef.current)); if (!messageManuallyMarkedUnread) { - shouldDisplay = shouldDisplay && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); + // Check if the report type is "REPORTREVIEW" and last actor is the current user. + // This is to avoid displaying the new line marker when a current userrequests money. + shouldDisplay = + shouldDisplay && + (ReportActionsUtils.isReportPreviewAction(reportAction) ? reportAction.childLastActorAccountID : reportAction.actorAccountID) !== Report.getCurrentUserAccountID(); } if (shouldDisplay) { cacheUnreadMarkers.set(report.reportID, reportAction.reportActionID); From d3b47a31e727a349a0be4090a100e01094d8eb7a Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 29 Nov 2023 17:23:46 +0100 Subject: [PATCH 17/41] Migrate 'ReportActionItemDraft.js' component to TypeScript --- .../home/report/ReportActionItemDraft.js | 22 ------------------- .../home/report/ReportActionItemDraft.tsx | 17 ++++++++++++++ 2 files changed, 17 insertions(+), 22 deletions(-) delete mode 100644 src/pages/home/report/ReportActionItemDraft.js create mode 100644 src/pages/home/report/ReportActionItemDraft.tsx diff --git a/src/pages/home/report/ReportActionItemDraft.js b/src/pages/home/report/ReportActionItemDraft.js deleted file mode 100644 index 9b3839aa78f2..000000000000 --- a/src/pages/home/report/ReportActionItemDraft.js +++ /dev/null @@ -1,22 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {View} from 'react-native'; -import useThemeStyles from '@styles/useThemeStyles'; - -const propTypes = { - /** Children view component for this action item */ - children: PropTypes.node.isRequired, -}; - -function ReportActionItemDraft(props) { - const styles = useThemeStyles(); - return ( - - {props.children} - - ); -} - -ReportActionItemDraft.propTypes = propTypes; -ReportActionItemDraft.displayName = 'ReportActionItemDraft'; -export default ReportActionItemDraft; diff --git a/src/pages/home/report/ReportActionItemDraft.tsx b/src/pages/home/report/ReportActionItemDraft.tsx new file mode 100644 index 000000000000..b46af5401ee4 --- /dev/null +++ b/src/pages/home/report/ReportActionItemDraft.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import {View} from 'react-native'; +import useThemeStyles from '@styles/useThemeStyles'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; + +function ReportActionItemDraft({children}: ChildrenProps) { + const styles = useThemeStyles(); + + return ( + + {children} + + ); +} + +ReportActionItemDraft.displayName = 'ReportActionItemDraft'; +export default ReportActionItemDraft; From 001aae314b7d0185f2fac914196ae77e853ba8ad Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 30 Nov 2023 23:31:53 +0700 Subject: [PATCH 18/41] fix: Undefined displayed in header offline when opening left thread --- src/libs/actions/Report.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a03488429405..852a11f93db6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2053,6 +2053,9 @@ function leaveRoom(reportID, isWorkspaceMemberLeavingWorkspaceRoom = false) { value: { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.CLOSED, + parentReportID: report.parentReportID, + parentReportActionID: report.parentReportActionID, + type: report.type, }, }, ]; From 7f23712f67187d54f915f848a7ea70e9cc4bc547 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 1 Dec 2023 10:14:43 +0100 Subject: [PATCH 19/41] fix lint --- src/pages/home/report/ReportActionsList.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index a3d28d09da5a..99e29d3edd5e 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -342,9 +342,10 @@ function ReportActionsList({ const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; // Check if the report type is "REPORTREVIEW" and last actor is the current user. // This is to avoid displaying the new line marker when a current userrequests money. - shouldDisplay = shouldDisplay && ReportActionsUtils.isReportPreviewAction(reportAction) - ? reportAction.childLastActorAccountID - : reportAction.actorAccountID !== Report.getCurrentUserAccountID() && isWithinVisibleThreshold; + shouldDisplay = + shouldDisplay && ReportActionsUtils.isReportPreviewAction(reportAction) + ? reportAction.childLastActorAccountID + : reportAction.actorAccountID !== Report.getCurrentUserAccountID() && isWithinVisibleThreshold; } if (shouldDisplay) { cacheUnreadMarkers.set(report.reportID, reportAction.reportActionID); From baf668f1f54ee128323aa1364dda36bc8f5f4cb5 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 1 Dec 2023 17:53:55 +0700 Subject: [PATCH 20/41] fix avatar for workspace thread --- src/libs/ReportUtils.ts | 2 +- src/libs/actions/Report.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f6c3090143f4..666d90aac21e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3849,7 +3849,7 @@ function getWhisperDisplayNames(participantAccountIDs?: number[]): string | unde * Show subscript on workspace chats / threads and expense requests */ function shouldReportShowSubscript(report: OnyxEntry): boolean { - if (isArchivedRoom(report)) { + if (isArchivedRoom(report) && !isWorkspaceThread(report)) { return false; } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 852a11f93db6..c2cfa1ac92a2 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2053,8 +2053,10 @@ function leaveRoom(reportID, isWorkspaceMemberLeavingWorkspaceRoom = false) { value: { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.CLOSED, + chatType: report.chatType, parentReportID: report.parentReportID, parentReportActionID: report.parentReportActionID, + policyID: report.policyID, type: report.type, }, }, From 01b27d9180977283a8c874b591e6788283fad603 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 1 Dec 2023 14:28:33 +0100 Subject: [PATCH 21/41] fix failing test --- src/pages/home/report/ReportActionsList.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index d1050f4c0930..8e63f508f8ce 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -337,14 +337,13 @@ function ReportActionsList({ const nextMessage = sortedReportActions[index + 1]; const isCurrentMessageUnread = isMessageUnread(reportAction, lastReadTimeRef.current); shouldDisplay = isCurrentMessageUnread && (!nextMessage || !isMessageUnread(nextMessage, lastReadTimeRef.current)); - if (!messageManuallyMarkedUnread) { + if (shouldDisplay && !messageManuallyMarkedUnread) { const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; // Check if the report type is "REPORTREVIEW" and last actor is the current user. // This is to avoid displaying the new line marker when a current userrequests money. shouldDisplay = - shouldDisplay && ReportActionsUtils.isReportPreviewAction(reportAction) - ? reportAction.childLastActorAccountID - : reportAction.actorAccountID !== Report.getCurrentUserAccountID() && isWithinVisibleThreshold; + (ReportActionsUtils.isReportPreviewAction(reportAction) ? !reportAction.childLastActorAccountID : reportAction.actorAccountID !== Report.getCurrentUserAccountID()) && + isWithinVisibleThreshold; } if (shouldDisplay) { cacheUnreadMarkers.set(report.reportID, reportAction.reportActionID); From d82f0c8f1375e6fcc5ef32e7f2f86229159adf30 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Mon, 4 Dec 2023 16:41:22 +0530 Subject: [PATCH 22/41] Update hook dependency and property type --- src/pages/home/report/ReportActionsList.js | 13 ++++++------- src/types/onyx/ReportAction.ts | 3 ++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 8e63f508f8ce..183665891929 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -156,7 +156,7 @@ function ReportActionsList({ const readActionSkipped = useRef(false); const hasHeaderRendered = useRef(false); const hasFooterRendered = useRef(false); - const reportActionSize = useRef(sortedReportActions.length); + const lastVisibleActionCreatedRef = useRef(report.lastVisibleActionCreated); const lastReadTimeRef = useRef(report.lastReadTime); const linkedReportActionID = lodashGet(route, 'params.reportActionID', ''); @@ -198,15 +198,15 @@ function ReportActionsList({ } } - if (currentUnreadMarker || reportActionSize.current === sortedReportActions.length) { + if (currentUnreadMarker || lastVisibleActionCreatedRef.current === report.lastVisibleActionCreated) { return; } cacheUnreadMarkers.delete(report.reportID); - reportActionSize.current = sortedReportActions.length; + lastVisibleActionCreatedRef.current = report.lastVisibleActionCreated; setCurrentUnreadMarker(null); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [sortedReportActions.length, report.reportID]); + }, [report.lastVisibleActionCreated, report.reportID]); useEffect(() => { if (!userActiveSince.current || report.reportID !== prevReportID) { @@ -339,10 +339,9 @@ function ReportActionsList({ shouldDisplay = isCurrentMessageUnread && (!nextMessage || !isMessageUnread(nextMessage, lastReadTimeRef.current)); if (shouldDisplay && !messageManuallyMarkedUnread) { const isWithinVisibleThreshold = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; - // Check if the report type is "REPORTREVIEW" and last actor is the current user. - // This is to avoid displaying the new line marker when a current userrequests money. + // Prevent displaying a new marker line when report action is of type "REPORTPREVIEW" and last actor is the current user shouldDisplay = - (ReportActionsUtils.isReportPreviewAction(reportAction) ? !reportAction.childLastActorAccountID : reportAction.actorAccountID !== Report.getCurrentUserAccountID()) && + (ReportActionsUtils.isReportPreviewAction(reportAction) ? !reportAction.childLastActorAccountID : reportAction.actorAccountID) !== Report.getCurrentUserAccountID() && isWithinVisibleThreshold; } if (shouldDisplay) { diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 64e1eb0b7c88..01d0463abda5 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -115,12 +115,13 @@ type ReportActionBase = { childStateNum?: ValueOf; childLastReceiptTransactionIDs?: string; childLastMoneyRequestComment?: string; + childLastActorAccountID?: number; timestamp?: number; reportActionTimestamp?: number; childMoneyRequestCount?: number; isFirstItem?: boolean; - /** Informations about attachments of report action */ + /** Information about attachments of report action */ attachmentInfo?: (File & {source: string; uri: string}) | Record; /** Receipt tied to report action */ From 22ec91cee8c533ae997340fd56079bb1e53b4ac4 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 4 Dec 2023 13:00:17 -0700 Subject: [PATCH 23/41] Get the parent report action using withOnyx for the money request view --- .../ReportActionItem/MoneyRequestView.js | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 0249a9f5bb11..80725a1e2531 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -29,6 +29,7 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground'; +import reportActionPropTypes from '@pages/home/report/reportActionPropTypes'; import iouReportPropTypes from '@pages/iouReportPropTypes'; import reportPropTypes from '@pages/reportPropTypes'; import * as StyleUtils from '@styles/StyleUtils'; @@ -51,6 +52,9 @@ const propTypes = { /** The expense report or iou report (only will have a value if this is a transaction thread) */ parentReport: iouReportPropTypes, + /** The actions from the parent report */ + parentReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), + /** Collection of categories attached to a policy */ policyCategories: PropTypes.objectOf(categoryPropTypes), @@ -65,6 +69,7 @@ const propTypes = { const defaultProps = { parentReport: {}, + parentReportActions: {}, policyCategories: {}, transaction: { amount: 0, @@ -74,13 +79,13 @@ const defaultProps = { policyTags: {}, }; -function MoneyRequestView({report, parentReport, policyCategories, shouldShowHorizontalRule, transaction, policyTags, policy}) { +function MoneyRequestView({report, parentReport, parentReportActions, policyCategories, shouldShowHorizontalRule, transaction, policyTags, policy}) { const theme = useTheme(); const styles = useThemeStyles(); const {isSmallScreenWidth} = useWindowDimensions(); const {translate} = useLocalize(); const {canUseViolations} = usePermissions(); - const parentReportAction = ReportActionsUtils.getParentReportAction(report); + const parentReportAction = parentReportActions[report.parentReportActionID] || {}; const moneyRequestReport = parentReport; const { created: transactionDate, @@ -313,8 +318,8 @@ MoneyRequestView.displayName = 'MoneyRequestView'; export default compose( withCurrentUserPersonalDetails, withOnyx({ - parentReport: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, + session: { + key: ONYXKEYS.SESSION, }, policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, @@ -322,18 +327,24 @@ export default compose( policyCategories: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report.policyID}`, }, - session: { - key: ONYXKEYS.SESSION, + policyTags: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report.policyID}`, }, + parentReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`, + }, + parentReportActions: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`, + canEvict: false, + }, + }), + withOnyx({ transaction: { - key: ({report}) => { - const parentReportAction = ReportActionsUtils.getParentReportAction(report); + key: ({report, parentReportActions}) => { + const parentReportAction = parentReportActions[report.parentReportActionID]; const transactionID = lodashGet(parentReportAction, ['originalMessage', 'IOUTransactionID'], 0); return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; }, }, - policyTags: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report.policyID}`, - }, }), )(MoneyRequestView); From 455a2bcf34775e034de18656f7f967756bed87c2 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Tue, 5 Dec 2023 19:17:54 -0800 Subject: [PATCH 24/41] Make sure that split bills follow the correct routes for editing category/tag --- .../MoneyRequestConfirmationList.js | 16 +++++++- src/pages/EditSplitBillPage.js | 37 ++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 1b4967a9c54c..da3c19f48d1b 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -699,7 +699,13 @@ function MoneyRequestConfirmationList(props) { title={props.iouCategory} description={translate('common.category')} numberOfLinesTitle={2} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_CATEGORY.getRoute(props.iouType, props.reportID))} + onPress={() => { + if (props.isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.CATEGORY)); + return; + } + Navigation.navigate(ROUTES.MONEY_REQUEST_CATEGORY.getRoute(props.iouType, props.reportID)); + }} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} disabled={didConfirm} @@ -713,7 +719,13 @@ function MoneyRequestConfirmationList(props) { title={props.iouTag} description={policyTagListName} numberOfLinesTitle={2} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(props.iouType, props.reportID))} + onPress={() => { + if (props.isEditingSplitBill) { + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(props.reportID, props.reportActionID, CONST.EDIT_REQUEST_FIELD.TAG)); + return; + } + Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(props.iouType, props.reportID)); + }} style={[styles.moneyRequestMenuItem]} disabled={didConfirm} interactive={!props.isReadOnly} diff --git a/src/pages/EditSplitBillPage.js b/src/pages/EditSplitBillPage.js index c4e47e2d4c35..75618b7142d9 100644 --- a/src/pages/EditSplitBillPage.js +++ b/src/pages/EditSplitBillPage.js @@ -12,10 +12,13 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import reportPropTypes from '@pages/reportPropTypes'; import EditRequestAmountPage from './EditRequestAmountPage'; import EditRequestCreatedPage from './EditRequestCreatedPage'; import EditRequestDescriptionPage from './EditRequestDescriptionPage'; import EditRequestMerchantPage from './EditRequestMerchantPage'; +import EditRequestCategoryPage from './EditRequestCategoryPage'; +import EditRequestTagPage from './EditRequestTagPage'; const propTypes = { /** Route from navigation */ @@ -38,13 +41,16 @@ const propTypes = { /** The draft transaction that holds data to be persisted on the current transaction */ draftTransaction: transactionPropTypes, + + /** The report currently being used */ + report: reportPropTypes.isRequired, }; const defaultProps = { draftTransaction: undefined, }; -function EditSplitBillPage({route, transaction, draftTransaction}) { +function EditSplitBillPage({route, transaction, draftTransaction, report}) { const fieldToEdit = lodashGet(route, ['params', 'field'], ''); const reportID = lodashGet(route, ['params', 'reportID'], ''); const reportActionID = lodashGet(route, ['params', 'reportActionID'], ''); @@ -55,6 +61,8 @@ function EditSplitBillPage({route, transaction, draftTransaction}) { comment: transactionDescription, merchant: transactionMerchant, created: transactionCreated, + category: transactionCategory, + tag: transactionTag, } = draftTransaction ? ReportUtils.getTransactionDetails(draftTransaction) : ReportUtils.getTransactionDetails(transaction); const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; @@ -130,6 +138,30 @@ function EditSplitBillPage({route, transaction, draftTransaction}) { ); } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.CATEGORY) { + return ( + { + setDraftSplitTransaction({category: transactionChanges.category.trim()}); + }} + /> + ); + } + + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.TAG) { + return ( + { + setDraftSplitTransaction({tag: transactionChanges.tag.trim()}); + }} + /> + ); + } + return ; } @@ -142,6 +174,9 @@ export default compose( key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${route.params.reportID}`, canEvict: false, }, + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + } }), // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ From ddbe1f50f2eb95f7b96132138b90a8c1bb51a725 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Tue, 5 Dec 2023 19:32:47 -0800 Subject: [PATCH 25/41] ESLint --- src/pages/EditSplitBillPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditSplitBillPage.js b/src/pages/EditSplitBillPage.js index 75618b7142d9..8fad905e7e92 100644 --- a/src/pages/EditSplitBillPage.js +++ b/src/pages/EditSplitBillPage.js @@ -12,7 +12,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import reportPropTypes from '@pages/reportPropTypes'; +import reportPropTypes from './reportPropTypes'; import EditRequestAmountPage from './EditRequestAmountPage'; import EditRequestCreatedPage from './EditRequestCreatedPage'; import EditRequestDescriptionPage from './EditRequestDescriptionPage'; From ed724d1cb239acd9144751f7a27841a2af597e12 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Tue, 5 Dec 2023 19:42:54 -0800 Subject: [PATCH 26/41] Prettier --- src/pages/EditSplitBillPage.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/EditSplitBillPage.js b/src/pages/EditSplitBillPage.js index 8fad905e7e92..3e5a5e7f5d53 100644 --- a/src/pages/EditSplitBillPage.js +++ b/src/pages/EditSplitBillPage.js @@ -12,13 +12,13 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import reportPropTypes from './reportPropTypes'; import EditRequestAmountPage from './EditRequestAmountPage'; +import EditRequestCategoryPage from './EditRequestCategoryPage'; import EditRequestCreatedPage from './EditRequestCreatedPage'; import EditRequestDescriptionPage from './EditRequestDescriptionPage'; import EditRequestMerchantPage from './EditRequestMerchantPage'; -import EditRequestCategoryPage from './EditRequestCategoryPage'; import EditRequestTagPage from './EditRequestTagPage'; +import reportPropTypes from './reportPropTypes'; const propTypes = { /** Route from navigation */ @@ -176,7 +176,7 @@ export default compose( }, report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, - } + }, }), // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ From eb9c843ee8e474b0cd9743120bab3feb64169de7 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 6 Dec 2023 14:19:28 +0700 Subject: [PATCH 27/41] fix search term is lost --- src/pages/workspace/SearchInputManager.js | 5 +++++ src/pages/workspace/WorkspaceInvitePage.js | 15 ++++++++++++++- src/pages/workspace/WorkspaceMembersPage.js | 20 +++++++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/pages/workspace/SearchInputManager.js diff --git a/src/pages/workspace/SearchInputManager.js b/src/pages/workspace/SearchInputManager.js new file mode 100644 index 000000000000..599f7cca6cf9 --- /dev/null +++ b/src/pages/workspace/SearchInputManager.js @@ -0,0 +1,5 @@ +// eslint-disable-next-line prefer-const +let searchInput = ''; +export default { + searchInput, +}; diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 4bef69c82414..dfcd1e555ed7 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -21,6 +21,7 @@ import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SearchInputManager from './SearchInputManager'; import {policyDefaultProps, policyPropTypes} from './withPolicy'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; @@ -75,6 +76,15 @@ function WorkspaceInvitePage(props) { Policy.openWorkspaceInvitePage(props.route.params.policyID, _.keys(policyMemberEmailsToAccountIDs)); }; + useEffect(() => { + if (!SearchInputManager.searchInput) { + return; + } + if (SearchInputManager.searchInput) { + setSearchTerm(SearchInputManager.searchInput); + } + }, []); + useEffect(() => { Policy.clearErrors(props.route.params.policyID); openWorkspaceInvitePage(); @@ -255,7 +265,10 @@ function WorkspaceInvitePage(props) { sections={sections} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} textInputValue={searchTerm} - onChangeText={setSearchTerm} + onChangeText={(value) => { + SearchInputManager.searchInput = value; + setSearchTerm(value); + }} headerMessage={headerMessage} onSelectRow={toggleOption} onConfirm={inviteUser} diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 09b613350705..e055afc206a0 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -1,3 +1,4 @@ +import {useIsFocused} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; @@ -32,6 +33,7 @@ import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SearchInputManager from './SearchInputManager'; import {policyDefaultProps, policyPropTypes} from './withPolicy'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; @@ -85,6 +87,19 @@ function WorkspaceMembersPage(props) { const isOfflineAndNoMemberDataAvailable = _.isEmpty(props.policyMembers) && props.network.isOffline; const prevPersonalDetails = usePrevious(props.personalDetails); + const isFocusedScreen = useIsFocused(); + + useEffect(() => { + if (!SearchInputManager.searchInput) { + return; + } + if (SearchInputManager.searchInput) { + setSearchValue(SearchInputManager.searchInput); + } + }, [isFocusedScreen]); + + useEffect(() => () => (SearchInputManager.searchInput = ''), []); + /** * Get filtered personalDetails list with current policyMembers * @param {Object} policyMembers @@ -466,7 +481,10 @@ function WorkspaceMembersPage(props) { sections={[{data, indexOffset: 0, isDisabled: false}]} textInputLabel={props.translate('optionsSelector.findMember')} textInputValue={searchValue} - onChangeText={setSearchValue} + onChangeText={(value) => { + SearchInputManager.searchInput = value; + setSearchValue(value); + }} headerMessage={getHeaderMessage()} headerContent={getHeaderContent()} onSelectRow={(item) => toggleUser(item.accountID)} From f931e044e6bebd0468b01db477e4af47f8d94998 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 6 Dec 2023 21:10:43 +0700 Subject: [PATCH 28/41] fix update based on suggestions --- src/pages/workspace/WorkspaceInvitePage.js | 4 +--- src/pages/workspace/WorkspaceMembersPage.js | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index dfcd1e555ed7..b18c234ea44d 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -80,9 +80,7 @@ function WorkspaceInvitePage(props) { if (!SearchInputManager.searchInput) { return; } - if (SearchInputManager.searchInput) { - setSearchTerm(SearchInputManager.searchInput); - } + setSearchTerm(SearchInputManager.searchInput); }, []); useEffect(() => { diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index e055afc206a0..d5cdbcfc69d8 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -93,9 +93,7 @@ function WorkspaceMembersPage(props) { if (!SearchInputManager.searchInput) { return; } - if (SearchInputManager.searchInput) { - setSearchValue(SearchInputManager.searchInput); - } + setSearchValue(SearchInputManager.searchInput); }, [isFocusedScreen]); useEffect(() => () => (SearchInputManager.searchInput = ''), []); From 7142a459f66d653c9ed609d7974402b5ef499734 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 6 Dec 2023 23:41:12 +0700 Subject: [PATCH 29/41] fix clear input once rhn closed --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index a78b38728136..6684e476b4f3 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -13,6 +13,7 @@ import type {AuthScreensParamList} from '@navigation/types'; import DemoSetupPage from '@pages/DemoSetupPage'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import DesktopSignInRedirectPage from '@pages/signin/DesktopSignInRedirectPage'; +import SearchInputManager from '@pages/workspace/SearchInputManager'; import useThemeStyles from '@styles/useThemeStyles'; import * as App from '@userActions/App'; import * as Download from '@userActions/Download'; @@ -123,6 +124,8 @@ const modalScreenListeners = { Modal.setModalVisibility(true); }, beforeRemove: () => { + // Clear search input (WorkspaceMembersPage) when modal is closed + SearchInputManager.searchInput = ''; Modal.setModalVisibility(false); }, }; From cc68278b03756482dac7eb513d36d0a742966fc1 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 6 Dec 2023 18:29:37 +0100 Subject: [PATCH 30/41] update naming --- src/pages/home/report/ReportActionsView.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 6f46a6616745..d7eaaa340e05 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -96,7 +96,7 @@ function ReportActionsView(props) { const isFocused = useIsFocused(); const reportID = props.report.reportID; - const isNewestReportAction = lodashGet(props.reportActions[0], 'isNewestReportAction'); + const hasNewestReportAction = lodashGet(props.reportActions[0], 'isNewestReportAction'); /** * @returns {Boolean} @@ -200,7 +200,7 @@ function ReportActionsView(props) { const loadNewerChats = useMemo( () => _.throttle(({distanceFromStart}) => { - if (props.isLoadingNewerReportActions || props.isLoadingInitialReportActions || isNewestReportAction) { + if (props.isLoadingNewerReportActions || props.isLoadingInitialReportActions || hasNewestReportAction) { return; } @@ -222,7 +222,7 @@ function ReportActionsView(props) { const newestReportAction = _.first(props.reportActions); Report.getNewerActions(reportID, newestReportAction.reportActionID); }, 500), - [props.isLoadingNewerReportActions, props.isLoadingInitialReportActions, props.reportActions, reportID, isNewestReportAction], + [props.isLoadingNewerReportActions, props.isLoadingInitialReportActions, props.reportActions, reportID, hasNewestReportAction], ); /** From d180ba9d9d53898397363a1cba5add7918e31e4c Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Thu, 7 Dec 2023 00:44:15 +0700 Subject: [PATCH 31/41] fix update comment --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 6684e476b4f3..5e8a9f502dc5 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -124,7 +124,7 @@ const modalScreenListeners = { Modal.setModalVisibility(true); }, beforeRemove: () => { - // Clear search input (WorkspaceMembersPage) when modal is closed + // Clear search input (WorkspaceInvitePage) when modal is closed SearchInputManager.searchInput = ''; Modal.setModalVisibility(false); }, From af91f95e123e86f58e198a7b518377e2508cf12f Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 6 Dec 2023 18:16:02 +0000 Subject: [PATCH 32/41] Update version to 1.4.8-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 f224d895e2fa..f7a83db89d9a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001040801 - versionName "1.4.8-1" + versionCode 1001040802 + versionName "1.4.8-2" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 7c3fbf13697a..9465f768afe1 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.8.1 + 1.4.8.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 0d2561b67b74..b4ae3184e624 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.8.1 + 1.4.8.2 diff --git a/package-lock.json b/package-lock.json index 51dc9df3a5f0..5e459506dccd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.8-1", + "version": "1.4.8-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.8-1", + "version": "1.4.8-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index ac02f2db5f82..6c3eb5a589b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.8-1", + "version": "1.4.8-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 7439a3d6697bbf4715e231e388355ad3a06303ce Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 6 Dec 2023 19:46:43 +0000 Subject: [PATCH 33/41] Update version to 1.4.8-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 f7a83db89d9a..1138beb7e98b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001040802 - versionName "1.4.8-2" + versionCode 1001040803 + versionName "1.4.8-3" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 9465f768afe1..807c235eedb5 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.8.2 + 1.4.8.3 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index b4ae3184e624..3800aa4b7da1 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.8.2 + 1.4.8.3 diff --git a/package-lock.json b/package-lock.json index 5e459506dccd..ae679dfd89ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.8-2", + "version": "1.4.8-3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.8-2", + "version": "1.4.8-3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 6c3eb5a589b4..15dfe3f81d69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.8-2", + "version": "1.4.8-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 deda6ae510f1e1ef1599c8c6ee45d91fe58660c5 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Wed, 6 Dec 2023 20:26:54 +0000 Subject: [PATCH 34/41] Update version to 1.4.9-0 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 4 ++-- ios/NewExpensifyTests/Info.plist | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1138beb7e98b..25010c93da6f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001040803 - versionName "1.4.8-3" + versionCode 1001040900 + versionName "1.4.9-0" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 807c235eedb5..9657a6d9b4e6 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.8 + 1.4.9 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.8.3 + 1.4.9.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 3800aa4b7da1..77b0f97c9e9b 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.8 + 1.4.9 CFBundleSignature ???? CFBundleVersion - 1.4.8.3 + 1.4.9.0 diff --git a/package-lock.json b/package-lock.json index ae679dfd89ca..b87f13d8a8cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.8-3", + "version": "1.4.9-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.8-3", + "version": "1.4.9-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 15dfe3f81d69..58d505dbdead 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.8-3", + "version": "1.4.9-0", "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 9d31e4b9e7f85d17988cb1efd7cac6c48e700e46 Mon Sep 17 00:00:00 2001 From: Yuwen Memon Date: Wed, 6 Dec 2023 15:36:09 -0800 Subject: [PATCH 35/41] Revert "translate invite member to room" --- src/languages/en.ts | 6 - src/languages/es.ts | 6 - src/libs/Localize/index.ts | 46 +------ src/libs/MessageElement.ts | 11 -- src/libs/PersonalDetailsUtils.js | 13 -- src/libs/ReportActionsUtils.ts | 113 +----------------- src/libs/ReportUtils.ts | 41 ++++++- src/libs/SidebarUtils.ts | 10 +- .../report/ContextMenu/ContextMenuActions.js | 4 +- .../home/report/ReportActionItemMessage.js | 15 --- src/types/onyx/OriginalMessage.ts | 1 - tests/unit/LocalizeTests.js | 8 +- 12 files changed, 61 insertions(+), 213 deletions(-) delete mode 100644 src/libs/MessageElement.ts diff --git a/src/languages/en.ts b/src/languages/en.ts index a276de4e0f7c..817f06f6b344 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1547,12 +1547,6 @@ export default { invitePeople: 'Invite new members', genericFailureMessage: 'An error occurred inviting the user to the workspace, please try again.', pleaseEnterValidLogin: `Please ensure the email or phone number is valid (e.g. ${CONST.EXAMPLE_PHONE_NUMBER}).`, - user: 'user', - users: 'users', - invited: 'invited', - removed: 'removed', - to: 'to', - from: 'from', }, inviteMessage: { inviteMessageTitle: 'Add message', diff --git a/src/languages/es.ts b/src/languages/es.ts index 290d80a6f65d..b219021daa0f 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1569,12 +1569,6 @@ export default { invitePeople: 'Invitar nuevos miembros', genericFailureMessage: 'Se produjo un error al invitar al usuario al espacio de trabajo. Vuelva a intentarlo..', pleaseEnterValidLogin: `Asegúrese de que el correo electrónico o el número de teléfono sean válidos (p. ej. ${CONST.EXAMPLE_PHONE_NUMBER}).`, - user: 'usuario', - users: 'usuarios', - invited: 'invitó', - removed: 'eliminó', - to: 'a', - from: 'de', }, inviteMessage: { inviteMessageTitle: 'Añadir un mensaje', diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index 77c34ebdc576..488ff0d9b98a 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -1,7 +1,6 @@ import * as RNLocalize from 'react-native-localize'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; -import {MessageElementBase, MessageTextElement} from '@libs/MessageElement'; import Config from '@src/CONFIG'; import CONST from '@src/CONST'; import translations from '@src/languages/translations'; @@ -122,48 +121,15 @@ function translateIfPhraseKey(message: MaybePhraseKey): string { } } -function getPreferredListFormat(): Intl.ListFormat { - if (!CONJUNCTION_LIST_FORMATS_FOR_LOCALES) { - init(); - } - - return CONJUNCTION_LIST_FORMATS_FOR_LOCALES[BaseLocaleListener.getPreferredLocale()]; -} - /** * Format an array into a string with comma and "and" ("a dog, a cat and a chicken") */ -function formatList(components: string[]) { - const listFormat = getPreferredListFormat(); - return listFormat.format(components); -} - -function formatMessageElementList(elements: readonly E[]): ReadonlyArray { - const listFormat = getPreferredListFormat(); - const parts = listFormat.formatToParts(elements.map((e) => e.content)); - const resultElements: Array = []; - - let nextElementIndex = 0; - for (const part of parts) { - if (part.type === 'element') { - /** - * The standard guarantees that all input elements will be present in the constructed parts, each exactly - * once, and without any modifications: https://tc39.es/ecma402/#sec-createpartsfromlist - */ - const element = elements[nextElementIndex++]; - - resultElements.push(element); - } else { - const literalElement: MessageTextElement = { - kind: 'text', - content: part.value, - }; - - resultElements.push(literalElement); - } +function arrayToString(anArray: string[]) { + if (!CONJUNCTION_LIST_FORMATS_FOR_LOCALES) { + init(); } - - return resultElements; + const listFormat = CONJUNCTION_LIST_FORMATS_FOR_LOCALES[BaseLocaleListener.getPreferredLocale()]; + return listFormat.format(anArray); } /** @@ -173,5 +139,5 @@ function getDevicePreferredLocale(): string { return RNLocalize.findBestAvailableLanguage([CONST.LOCALES.EN, CONST.LOCALES.ES])?.languageTag ?? CONST.LOCALES.DEFAULT; } -export {translate, translateLocal, translateIfPhraseKey, formatList, formatMessageElementList, getDevicePreferredLocale}; +export {translate, translateLocal, translateIfPhraseKey, arrayToString, getDevicePreferredLocale}; export type {PhraseParameters, Phrase, MaybePhraseKey}; diff --git a/src/libs/MessageElement.ts b/src/libs/MessageElement.ts deleted file mode 100644 index 584d7e1e289a..000000000000 --- a/src/libs/MessageElement.ts +++ /dev/null @@ -1,11 +0,0 @@ -type MessageElementBase = { - readonly kind: string; - readonly content: string; -}; - -type MessageTextElement = { - readonly kind: 'text'; - readonly content: string; -} & MessageElementBase; - -export type {MessageElementBase, MessageTextElement}; diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 8a4151391453..560480dcec9d 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -197,18 +197,6 @@ function getFormattedAddress(privatePersonalDetails) { return formattedAddress.trim().replace(/,$/, ''); } -/** - * @param {Object} personalDetail - details object - * @returns {String | undefined} - The effective display name - */ -function getEffectiveDisplayName(personalDetail) { - if (personalDetail) { - return LocalePhoneNumber.formatPhoneNumber(personalDetail.login) || personalDetail.displayName; - } - - return undefined; -} - export { getDisplayNameOrDefault, getPersonalDetailsByIDs, @@ -218,5 +206,4 @@ export { getFormattedAddress, getFormattedStreet, getStreetLines, - getEffectiveDisplayName, }; diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 2bc24c1706e6..6dc735ebd8b7 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -5,17 +5,14 @@ import OnyxUtils from 'react-native-onyx/lib/utils'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {ActionName, ChangeLog} from '@src/types/onyx/OriginalMessage'; +import {ActionName} from '@src/types/onyx/OriginalMessage'; import Report from '@src/types/onyx/Report'; -import ReportAction, {Message, ReportActions} from '@src/types/onyx/ReportAction'; +import ReportAction, {ReportActions} from '@src/types/onyx/ReportAction'; import {EmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CollectionUtils from './CollectionUtils'; import * as Environment from './Environment/Environment'; import isReportMessageAttachment from './isReportMessageAttachment'; -import * as Localize from './Localize'; import Log from './Log'; -import {MessageElementBase, MessageTextElement} from './MessageElement'; -import * as PersonalDetailsUtils from './PersonalDetailsUtils'; type LastVisibleMessage = { lastMessageTranslationKey?: string; @@ -23,19 +20,6 @@ type LastVisibleMessage = { lastMessageHtml?: string; }; -type MemberChangeMessageUserMentionElement = { - readonly kind: 'userMention'; - readonly accountID: number; -} & MessageElementBase; - -type MemberChangeMessageRoomReferenceElement = { - readonly kind: 'roomReference'; - readonly roomName: string; - readonly roomID: number; -} & MessageElementBase; - -type MemberChangeMessageElement = MessageTextElement | MemberChangeMessageUserMentionElement | MemberChangeMessageRoomReferenceElement; - const allReports: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -116,7 +100,7 @@ function isReimbursementQueuedAction(reportAction: OnyxEntry) { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED; } -function isMemberChangeAction(reportAction: OnyxEntry) { +function isChannelLogMemberAction(reportAction: OnyxEntry) { return ( reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.REMOVE_FROM_ROOM || @@ -125,10 +109,6 @@ function isMemberChangeAction(reportAction: OnyxEntry) { ); } -function isInviteMemberAction(reportAction: OnyxEntry) { - return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM; -} - function isReimbursementDeQueuedAction(reportAction: OnyxEntry): boolean { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED; } @@ -669,89 +649,6 @@ function isNotifiableReportAction(reportAction: OnyxEntry): boolea return actions.includes(reportAction.actionName); } -function getMemberChangeMessageElements(reportAction: OnyxEntry): readonly MemberChangeMessageElement[] { - const isInviteAction = isInviteMemberAction(reportAction); - - // Currently, we only render messages when members are invited - const verb = isInviteAction ? Localize.translateLocal('workspace.invite.invited') : Localize.translateLocal('workspace.invite.removed'); - - const originalMessage = reportAction?.originalMessage as ChangeLog; - const targetAccountIDs: number[] = originalMessage?.targetAccountIDs ?? []; - const personalDetails = PersonalDetailsUtils.getPersonalDetailsByIDs(targetAccountIDs, 0); - - const mentionElements = targetAccountIDs.map((accountID): MemberChangeMessageUserMentionElement => { - const personalDetail = personalDetails.find((personal) => personal.accountID === accountID); - const handleText = PersonalDetailsUtils.getEffectiveDisplayName(personalDetail) ?? Localize.translateLocal('common.hidden'); - - return { - kind: 'userMention', - content: `@${handleText}`, - accountID, - }; - }); - - const buildRoomElements = (): readonly MemberChangeMessageElement[] => { - const roomName = originalMessage?.roomName; - - if (roomName) { - const preposition = isInviteAction ? ` ${Localize.translateLocal('workspace.invite.to')} ` : ` ${Localize.translateLocal('workspace.invite.from')} `; - - if (originalMessage.reportID) { - return [ - { - kind: 'text', - content: preposition, - }, - { - kind: 'roomReference', - roomName, - roomID: originalMessage.reportID, - content: roomName, - }, - ]; - } - } - - return []; - }; - - return [ - { - kind: 'text', - content: `${verb} `, - }, - ...Localize.formatMessageElementList(mentionElements), - ...buildRoomElements(), - ]; -} - -function getMemberChangeMessageFragment(reportAction: OnyxEntry): Message { - const messageElements: readonly MemberChangeMessageElement[] = getMemberChangeMessageElements(reportAction); - const html = messageElements - .map((messageElement) => { - switch (messageElement.kind) { - case 'userMention': - return ``; - case 'roomReference': - return `${messageElement.roomName}`; - default: - return messageElement.content; - } - }) - .join(''); - - return { - html: `${html}`, - text: reportAction?.message ? reportAction?.message[0].text : '', - type: CONST.REPORT.MESSAGE.TYPE.COMMENT, - }; -} - -function getMemberChangeMessagePlainText(reportAction: OnyxEntry): string { - const messageElements = getMemberChangeMessageElements(reportAction); - return messageElements.map((element) => element.content).join(''); -} - /** * Helper method to determine if the provided accountID has made a request on the specified report. * @@ -814,9 +711,7 @@ export { shouldReportActionBeVisibleAsLastAction, hasRequestFromCurrentAccount, getFirstVisibleReportActionID, - isMemberChangeAction, - getMemberChangeMessageFragment, - getMemberChangeMessagePlainText, + isChannelLogMemberAction, isReimbursementDeQueuedAction, }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 84b2283681c6..1266f145de30 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -18,7 +18,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import {Beta, Login, PersonalDetails, Policy, PolicyTags, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import {IOUMessage, OriginalMessageActionName} from '@src/types/onyx/OriginalMessage'; +import {ChangeLog, IOUMessage, OriginalMessageActionName} from '@src/types/onyx/OriginalMessage'; import {Message, ReportActions} from '@src/types/onyx/ReportAction'; import {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; import DeepValueOf from '@src/types/utils/DeepValueOf'; @@ -4174,6 +4174,44 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry) }); } +/** + * Return room channel log display message + */ +function getChannelLogMemberMessage(reportAction: OnyxEntry): string { + const verb = + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM + ? 'invited' + : 'removed'; + + const mentions = (reportAction?.originalMessage as ChangeLog)?.targetAccountIDs?.map(() => { + const personalDetail = allPersonalDetails?.accountID; + const displayNameOrLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? '') || (personalDetail?.displayName ?? '') || Localize.translateLocal('common.hidden'); + return `@${displayNameOrLogin}`; + }); + + const lastMention = mentions?.pop(); + let message = ''; + + if (mentions?.length === 0) { + message = `${verb} ${lastMention}`; + } else if (mentions?.length === 1) { + message = `${verb} ${mentions?.[0]} and ${lastMention}`; + } else { + message = `${verb} ${mentions?.join(', ')}, and ${lastMention}`; + } + + const roomName = (reportAction?.originalMessage as ChangeLog)?.roomName ?? ''; + if (roomName) { + const preposition = + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM + ? ' to' + : ' from'; + message += `${preposition} ${roomName}`; + } + + return message; +} + /** * Checks if a report is a group chat. * @@ -4408,6 +4446,7 @@ export { getReimbursementQueuedActionMessage, getReimbursementDeQueuedActionMessage, getPersonalDetailsForAccountID, + getChannelLogMemberMessage, getRoom, shouldDisableWelcomeMessage, navigateToPrivateNotes, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 6e382e11b49b..bace29e06d28 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -375,17 +375,17 @@ function getOptionData( const targetAccountIDs = lastAction?.originalMessage?.targetAccountIDs ?? []; const verb = lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM - ? Localize.translate(preferredLocale, 'workspace.invite.invited') - : Localize.translate(preferredLocale, 'workspace.invite.removed'); - const users = Localize.translate(preferredLocale, targetAccountIDs.length > 1 ? 'workspace.invite.users' : 'workspace.invite.user'); + ? 'invited' + : 'removed'; + const users = targetAccountIDs.length > 1 ? 'users' : 'user'; result.alternateText = `${verb} ${targetAccountIDs.length} ${users}`; const roomName = lastAction?.originalMessage?.roomName ?? ''; if (roomName) { const preposition = lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM - ? ` ${Localize.translate(preferredLocale, 'workspace.invite.to')}` - : ` ${Localize.translate(preferredLocale, 'workspace.invite.from')}`; + ? ' to' + : ' from'; result.alternateText += `${preposition} ${roomName}`; } } else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && lastActorDisplayName && lastMessageTextFromReport) { diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 6c645bc87486..4f35926c5957 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -281,8 +281,8 @@ export default [ } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction); Clipboard.setString(displayMessage); - } else if (ReportActionsUtils.isMemberChangeAction(reportAction)) { - const logMessage = ReportActionsUtils.getMemberChangeMessagePlainText(reportAction); + } else if (ReportActionsUtils.isChannelLogMemberAction(reportAction)) { + const logMessage = ReportUtils.getChannelLogMemberMessage(reportAction); Clipboard.setString(logMessage); } else if (content) { const parser = new ExpensiMark(); diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index 46e0438f250a..2265530f29a1 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -8,7 +8,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; -import TextCommentFragment from './comment/TextCommentFragment'; import ReportActionItemFragment from './ReportActionItemFragment'; import reportActionPropTypes from './reportActionPropTypes'; @@ -41,20 +40,6 @@ function ReportActionItemMessage(props) { const styles = useThemeStyles(); const fragments = _.compact(props.action.previousMessage || props.action.message); const isIOUReport = ReportActionsUtils.isMoneyRequestAction(props.action); - if (ReportActionsUtils.isMemberChangeAction(props.action)) { - const fragment = ReportActionsUtils.getMemberChangeMessageFragment(props.action); - - return ( - - ); - } - let iouMessage; if (isIOUReport) { const iouReportID = lodashGet(props.action, 'originalMessage.IOUReportID'); diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 72ea275e3ba3..f76fbd5ffd7d 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -140,7 +140,6 @@ type ChronosOOOTimestamp = { type ChangeLog = { targetAccountIDs?: number[]; roomName?: string; - reportID?: number; }; type ChronosOOOEvent = { diff --git a/tests/unit/LocalizeTests.js b/tests/unit/LocalizeTests.js index 7693a0a4a88d..4c89d587fc06 100644 --- a/tests/unit/LocalizeTests.js +++ b/tests/unit/LocalizeTests.js @@ -15,7 +15,7 @@ describe('localize', () => { afterEach(() => Onyx.clear()); - describe('formatList', () => { + describe('arrayToString', () => { test.each([ [ [], @@ -52,9 +52,9 @@ describe('localize', () => { [CONST.LOCALES.ES]: 'rory, vit e ionatan', }, ], - ])('formatList(%s)', (input, {[CONST.LOCALES.DEFAULT]: expectedOutput, [CONST.LOCALES.ES]: expectedOutputES}) => { - expect(Localize.formatList(input)).toBe(expectedOutput); - return Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(Localize.formatList(input)).toBe(expectedOutputES)); + ])('arrayToSpokenList(%s)', (input, {[CONST.LOCALES.DEFAULT]: expectedOutput, [CONST.LOCALES.ES]: expectedOutputES}) => { + expect(Localize.arrayToString(input)).toBe(expectedOutput); + return Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES).then(() => expect(Localize.arrayToString(input)).toBe(expectedOutputES)); }); }); }); From 3827a9ce47f12f7c004339281c34a76f3a4c286b Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 7 Dec 2023 00:05:00 +0000 Subject: [PATCH 36/41] Update version to 1.4.9-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 25010c93da6f..5bf287cc784b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001040900 - versionName "1.4.9-0" + versionCode 1001040901 + versionName "1.4.9-1" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 9657a6d9b4e6..e6ff025496f2 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.9.0 + 1.4.9.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 77b0f97c9e9b..3bf704107dde 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.9.0 + 1.4.9.1 diff --git a/package-lock.json b/package-lock.json index b87f13d8a8cd..e708bbb08809 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.9-0", + "version": "1.4.9-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.9-0", + "version": "1.4.9-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 58d505dbdead..b014365c3dd5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.9-0", + "version": "1.4.9-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 f4a3bcf40351d2709609e24d27baf477d148290f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 6 Dec 2023 15:11:44 -1000 Subject: [PATCH 37/41] Revert "Only allow alphabetic and latin characters for some fields in `CompanyStep`" --- src/libs/ValidationUtils.ts | 94 +++++++++---------- src/pages/ReimbursementAccount/CompanyStep.js | 8 -- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 0adacac4035a..388020bc0d6d 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -30,6 +30,17 @@ function validateCardNumber(value: string): boolean { return sum % 10 === 0; } +/** + * Validating that this is a valid address (PO boxes are not allowed) + */ +function isValidAddress(value: string): boolean { + if (!CONST.REGEX.ANY_VALUE.test(value)) { + return false; + } + + return !CONST.REGEX.PO_BOX.test(value); +} + /** * Validate date fields */ @@ -193,6 +204,40 @@ function isValidWebsite(url: string): boolean { return new RegExp(`^${URL_REGEX_WITH_REQUIRED_PROTOCOL}$`, 'i').test(url) && isLowerCase; } +function validateIdentity(identity: Record): Record { + const requiredFields = ['firstName', 'lastName', 'street', 'city', 'zipCode', 'state', 'ssnLast4', 'dob']; + const errors: Record = {}; + + // Check that all required fields are filled + requiredFields.forEach((fieldName) => { + if (isRequiredFulfilled(identity[fieldName])) { + return; + } + errors[fieldName] = true; + }); + + if (!isValidAddress(identity.street)) { + errors.street = true; + } + + if (!isValidZipCode(identity.zipCode)) { + errors.zipCode = true; + } + + // dob field has multiple validations/errors, we are handling it temporarily like this. + if (!isValidDate(identity.dob) || !meetsMaximumAgeRequirement(identity.dob)) { + errors.dob = true; + } else if (!meetsMinimumAgeRequirement(identity.dob)) { + errors.dobAge = true; + } + + if (!isValidSSNLastFour(identity.ssnLast4)) { + errors.ssnLast4 = true; + } + + return errors; +} + function isValidUSPhone(phoneNumber = '', isCountryCodeOptional?: boolean): boolean { const phone = phoneNumber || ''; const regionCode = isCountryCodeOptional ? CONST.COUNTRY.US : undefined; @@ -259,51 +304,6 @@ function isValidPersonName(value: string) { return /^[^\d^!#$%*=<>;{}"]+$/.test(value); } -/** - * Validating that this is a valid address (PO boxes are not allowed) - */ -function isValidAddress(value: string): boolean { - if (!isValidLegalName(value)) { - return false; - } - - return !CONST.REGEX.PO_BOX.test(value); -} - -function validateIdentity(identity: Record): Record { - const requiredFields = ['firstName', 'lastName', 'street', 'city', 'zipCode', 'state', 'ssnLast4', 'dob']; - const errors: Record = {}; - - // Check that all required fields are filled - requiredFields.forEach((fieldName) => { - if (isRequiredFulfilled(identity[fieldName])) { - return; - } - errors[fieldName] = true; - }); - - if (!isValidAddress(identity.street)) { - errors.street = true; - } - - if (!isValidZipCode(identity.zipCode)) { - errors.zipCode = true; - } - - // dob field has multiple validations/errors, we are handling it temporarily like this. - if (!isValidDate(identity.dob) || !meetsMaximumAgeRequirement(identity.dob)) { - errors.dob = true; - } else if (!meetsMinimumAgeRequirement(identity.dob)) { - errors.dobAge = true; - } - - if (!isValidSSNLastFour(identity.ssnLast4)) { - errors.ssnLast4 = true; - } - - return errors; -} - /** * Checks if the provided string includes any of the provided reserved words */ @@ -384,6 +384,7 @@ export { meetsMinimumAgeRequirement, meetsMaximumAgeRequirement, getAgeRequirementError, + isValidAddress, isValidDate, isValidPastDate, isValidSecurityCode, @@ -395,6 +396,7 @@ export { getFieldRequiredErrors, isValidUSPhone, isValidWebsite, + validateIdentity, isValidTwoFactorCode, isNumericWithSpecialChars, isValidRoutingNumber, @@ -407,8 +409,6 @@ export { isValidValidateCode, isValidDisplayName, isValidLegalName, - isValidAddress, - validateIdentity, doesContainReservedWord, isNumeric, isValidAccountRoute, diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index d7d622d309d6..f1d62eef89ae 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -88,10 +88,6 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul ]; const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); - if (values.companyName && !ValidationUtils.isValidLegalName(values.companyName)) { - errors.companyName = 'bankAccount.error.companyName'; - } - if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { errors.addressStreet = 'bankAccount.error.addressStreet'; } @@ -100,10 +96,6 @@ function CompanyStep({reimbursementAccount, reimbursementAccountDraft, getDefaul errors.addressZipCode = 'bankAccount.error.zipCode'; } - if (values.addressCity && !ValidationUtils.isValidLegalName(values.addressCity)) { - errors.addressCity = 'bankAccount.error.addressCity'; - } - if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { errors.companyPhone = 'bankAccount.error.phoneNumber'; } From 9a8f59b1d33ec78e1ea93ab6df33bf4085fa1d25 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 6 Dec 2023 15:26:12 -1000 Subject: [PATCH 38/41] Fix deep linking --- .../HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js index 8f1406439be9..0c36bc36c11e 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js @@ -62,7 +62,7 @@ function AnchorRenderer(props) { key={props.key} displayName={displayName} // Only pass the press handler for internal links. For public links or whitelisted internal links fallback to default link handling - onPress={internalNewExpensifyPath || internalExpensifyPath ? Link.openLink : undefined} + onPress={() => internalNewExpensifyPath || internalExpensifyPath ? Link.openLink(attrHref, environmentURL, isAttachment) : undefined} > From 1e8bb05c0b886da4ff2c6baca612eeb846f739f6 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 6 Dec 2023 15:27:18 -1000 Subject: [PATCH 39/41] Pass undefined to onPress --- .../HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js index 0c36bc36c11e..2216f88d1055 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js @@ -61,8 +61,9 @@ function AnchorRenderer(props) { style={{...props.style, ...parentStyle, ...styles.textUnderlinePositionUnder, ...styles.textDecorationSkipInkNone}} key={props.key} displayName={displayName} + // Only pass the press handler for internal links. For public links or whitelisted internal links fallback to default link handling - onPress={() => internalNewExpensifyPath || internalExpensifyPath ? Link.openLink(attrHref, environmentURL, isAttachment) : undefined} + onPress={(internalNewExpensifyPath || internalExpensifyPath) ? () => Link.openLink(attrHref, environmentURL, isAttachment) : undefined} > From c9df3705ed7af717c80b264cc3dbb5bf2aec8ce9 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 6 Dec 2023 15:34:16 -1000 Subject: [PATCH 40/41] Fix prettier --- .../HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js index 2216f88d1055..49642308a357 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js @@ -61,9 +61,8 @@ function AnchorRenderer(props) { style={{...props.style, ...parentStyle, ...styles.textUnderlinePositionUnder, ...styles.textDecorationSkipInkNone}} key={props.key} displayName={displayName} - // Only pass the press handler for internal links. For public links or whitelisted internal links fallback to default link handling - onPress={(internalNewExpensifyPath || internalExpensifyPath) ? () => Link.openLink(attrHref, environmentURL, isAttachment) : undefined} + onPress={internalNewExpensifyPath || internalExpensifyPath ? () => Link.openLink(attrHref, environmentURL, isAttachment) : undefined} > From b0268fab8826bb964ad4c8450c342c27e23d0d50 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 7 Dec 2023 09:29:29 +0000 Subject: [PATCH 41/41] Update version to 1.4.9-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 5bf287cc784b..04d711009c10 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001040901 - versionName "1.4.9-1" + versionCode 1001040902 + versionName "1.4.9-2" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index e6ff025496f2..07afc5a85593 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.9.1 + 1.4.9.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 3bf704107dde..a434ffdc5757 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.9.1 + 1.4.9.2 diff --git a/package-lock.json b/package-lock.json index e708bbb08809..6e5b51fa4526 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.9-1", + "version": "1.4.9-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.9-1", + "version": "1.4.9-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index b014365c3dd5..8191454ef138 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.9-1", + "version": "1.4.9-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.",