From 3be5e6909a86a33a9c7c749c2378b14ad1ab243c Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Fri, 25 Aug 2023 16:36:54 +0200 Subject: [PATCH 001/234] Separate ReportScreenContext to prevent cyclic re-renders --- .../ReportActionItemEmojiReactions.js | 4 +- src/hooks/useReportScrollManager/index.js | 4 +- .../useReportScrollManager/index.native.js | 4 +- src/pages/home/ReportScreen.js | 188 +++++++++--------- src/pages/home/ReportScreenContext.js | 9 +- src/pages/home/report/ReportActionItem.js | 4 +- src/pages/home/report/ReportActionsView.js | 7 +- 7 files changed, 110 insertions(+), 110 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index ec2755f1a5dd..4f590b43e180 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -13,7 +13,7 @@ import EmojiReactionsPropTypes from './EmojiReactionsPropTypes'; import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; import * as EmojiUtils from '../../libs/EmojiUtils'; -import ReportScreenContext from '../../pages/home/ReportScreenContext'; +import {ReactionListContext} from '../../pages/home/ReportScreenContext'; const propTypes = { emojiReactions: EmojiReactionsPropTypes, @@ -41,7 +41,7 @@ const defaultProps = { }; function ReportActionItemEmojiReactions(props) { - const {reactionListRef} = useContext(ReportScreenContext); + const reactionListRef = useContext(ReactionListContext); const popoverReactionListAnchor = useRef(null); let totalReactionCount = 0; diff --git a/src/hooks/useReportScrollManager/index.js b/src/hooks/useReportScrollManager/index.js index 0cf09146553c..9a3303504b92 100644 --- a/src/hooks/useReportScrollManager/index.js +++ b/src/hooks/useReportScrollManager/index.js @@ -1,8 +1,8 @@ import {useContext, useCallback} from 'react'; -import ReportScreenContext from '../../pages/home/ReportScreenContext'; +import {ActionListContext} from '../../pages/home/ReportScreenContext'; function useReportScrollManager() { - const {flatListRef} = useContext(ReportScreenContext); + const flatListRef = useContext(ActionListContext); /** * Scroll to the provided index. On non-native implementations we do not want to scroll when we are scrolling because diff --git a/src/hooks/useReportScrollManager/index.native.js b/src/hooks/useReportScrollManager/index.native.js index 35af064cb062..d44a40222ca5 100644 --- a/src/hooks/useReportScrollManager/index.native.js +++ b/src/hooks/useReportScrollManager/index.native.js @@ -1,8 +1,8 @@ import {useContext, useCallback} from 'react'; -import ReportScreenContext from '../../pages/home/ReportScreenContext'; +import {ActionListContext} from '../../pages/home/ReportScreenContext'; function useReportScrollManager() { - const {flatListRef} = useContext(ReportScreenContext); + const flatListRef = useContext(ActionListContext); /** * Scroll to the provided index. diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 83f0e4a6d506..16284c2bd9c5 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -33,7 +33,7 @@ import getIsReportFullyVisible from '../../libs/getIsReportFullyVisible'; import MoneyRequestHeader from '../../components/MoneyRequestHeader'; import MoneyReportHeader from '../../components/MoneyReportHeader'; import * as ComposerActions from '../../libs/actions/Composer'; -import ReportScreenContext from './ReportScreenContext'; +import {ActionListContext, ReactionListContext} from './ReportScreenContext'; import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; import DragAndDropProvider from '../../components/DragAndDrop/Provider'; @@ -276,111 +276,107 @@ class ReportScreen extends React.Component { } return ( - - - + + - - {headerView} - {ReportUtils.isTaskReport(this.props.report) && this.props.isSmallScreenWidth && ReportUtils.isOpenTaskReport(this.props.report) && ( - - - - + + {headerView} + {ReportUtils.isTaskReport(this.props.report) && this.props.isSmallScreenWidth && ReportUtils.isOpenTaskReport(this.props.report) && ( + + + + + - - )} - - {Boolean(this.props.accountManagerReportID) && ReportUtils.isConciergeChatReport(this.props.report) && this.state.isBannerVisible && ( - - )} - - { - // Rounding this value for comparison because they can look like this: 411.9999694824219 - const skeletonViewContainerHeight = Math.round(event.nativeEvent.layout.height); - - // Only set state when the height changes to avoid unnecessary renders - if (reportActionsListViewHeight === skeletonViewContainerHeight) return; - - // The height can be 0 if the component unmounts - we are not interested in this value and want to know how much space it - // takes up so we can set the skeleton view container height. - if (skeletonViewContainerHeight === 0) { - return; - } - reportActionsListViewHeight = skeletonViewContainerHeight; - this.setState({skeletonViewContainerHeight}); - }} - > - {this.isReportReadyForDisplay() && !isLoadingInitialReportActions && !isLoading && ( - )} - - {/* Note: The report should be allowed to mount even if the initial report actions are not loaded. If we prevent rendering the report while they are loading then - we'll unnecessarily unmount the ReportActionsView which will clear the new marker lines initial state. */} - {(!this.isReportReadyForDisplay() || isLoadingInitialReportActions || isLoading) && ( - - )} - - {this.isReportReadyForDisplay() && ( - <> - + {Boolean(this.props.accountManagerReportID) && ReportUtils.isConciergeChatReport(this.props.report) && this.state.isBannerVisible && ( + + )} + + { + // Rounding this value for comparison because they can look like this: 411.9999694824219 + const skeletonViewContainerHeight = Math.round(event.nativeEvent.layout.height); + + // Only set state when the height changes to avoid unnecessary renders + if (reportActionsListViewHeight === skeletonViewContainerHeight) return; + + // The height can be 0 if the component unmounts - we are not interested in this value and want to know how much space it + // takes up so we can set the skeleton view container height. + if (skeletonViewContainerHeight === 0) { + return; + } + reportActionsListViewHeight = skeletonViewContainerHeight; + this.setState({skeletonViewContainerHeight}); + }} + > + {this.isReportReadyForDisplay() && !isLoadingInitialReportActions && !isLoading && ( + - - )} + )} - {!this.isReportReadyForDisplay() && ( - - )} - - - - - + {/* Note: The report should be allowed to mount even if the initial report actions are not loaded. If we prevent rendering the report while they are loading then + we'll unnecessarily unmount the ReportActionsView which will clear the new marker lines initial state. */} + {(!this.isReportReadyForDisplay() || isLoadingInitialReportActions || isLoading) && ( + + )} + + {this.isReportReadyForDisplay() && ( + <> + + + )} + + {!this.isReportReadyForDisplay() && ( + + )} + + + + + + ); } } diff --git a/src/pages/home/ReportScreenContext.js b/src/pages/home/ReportScreenContext.js index 2f79d6ae9432..0be1882699f4 100644 --- a/src/pages/home/ReportScreenContext.js +++ b/src/pages/home/ReportScreenContext.js @@ -1,4 +1,9 @@ import {createContext} from 'react'; -const ReportScreenContext = createContext(); -export default ReportScreenContext; +const ActionListContext = createContext(); +const ReactionListContext = createContext(); + +export { + ActionListContext, + ReactionListContext +} \ No newline at end of file diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index e5b199d1c994..4a0c0d7bd3ba 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -64,7 +64,7 @@ import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils'; import ReportActionItemBasicMessage from './ReportActionItemBasicMessage'; import * as store from '../../../libs/actions/ReimbursementAccount/store'; import * as BankAccounts from '../../../libs/actions/BankAccounts'; -import ReportScreenContext from '../ReportScreenContext'; +import { ReactionListContext } from '../ReportScreenContext'; import Permissions from '../../../libs/Permissions'; const propTypes = { @@ -127,7 +127,7 @@ function ReportActionItem(props) { const [isContextMenuActive, setIsContextMenuActive] = useState(ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); const [isHidden, setIsHidden] = useState(false); const [moderationDecision, setModerationDecision] = useState(CONST.MODERATION.MODERATOR_DECISION_APPROVED); - const {reactionListRef} = useContext(ReportScreenContext); + const reactionListRef = useContext(ReactionListContext); const textInputRef = useRef(); const popoverAnchorRef = useRef(); const downloadedPreviews = useRef([]); diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index da475e61f749..70eb65ade3ea 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -19,7 +19,7 @@ import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; import reportPropTypes from '../../reportPropTypes'; import PopoverReactionList from './ReactionList/PopoverReactionList'; import getIsReportFullyVisible from '../../../libs/getIsReportFullyVisible'; -import ReportScreenContext from '../ReportScreenContext'; +import { ReactionListContext } from '../ReportScreenContext'; const propTypes = { /** The report currently being looked at */ @@ -54,10 +54,9 @@ const defaultProps = { }; function ReportActionsView(props) { - const context = useContext(ReportScreenContext); useCopySelectionHelper(); - + const reactionListRef = useContext(ReactionListContext) const didLayout = useRef(false); const didSubscribeToReportTypingEvents = useRef(false); const hasCachedActions = useRef(_.size(props.reportActions) > 0); @@ -189,7 +188,7 @@ function ReportActionsView(props) { loadMoreChats={loadMoreChats} policy={props.policy} /> - + ); } From 6125c381cde29277f150f1a4156a6b96af05f150 Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Mon, 28 Aug 2023 13:44:09 +0200 Subject: [PATCH 002/234] Get rid of full list re-render when marking messages as unread --- src/pages/home/report/ReportActionsList.js | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 7f897ee825fb..1c0c88e20ec4 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -114,9 +114,9 @@ function ReportActionsList({ const readActionSkipped = useRef(false); const reportActionSize = useRef(sortedReportActions.length); - // Considering that renderItem is enclosed within a useCallback, marking it as "read" twice will retain the value as "true," preventing the useCallback from re-executing. - // However, if we create and listen to an object, it will lead to a new useCallback execution. - const [messageManuallyMarked, setMessageManuallyMarked] = useState({read: false}); + // This state is used to force a re-render when the user manually marks a message as unread + // by using a timestamp you can force re-renders without having to worry about if another message was marked as unread before + const [messageManuallyMarkedUnread, setMessageManuallyMarkedUnread] = useState(0); const [isFloatingMessageCounterVisible, setIsFloatingMessageCounterVisible] = useState(false); const animatedStyles = useAnimatedStyle(() => ({ opacity: opacity.value, @@ -163,15 +163,14 @@ function ReportActionsList({ useEffect(() => { const didManuallyMarkReportAsUnread = report.lastReadTime < DateUtils.getDBTime() && ReportUtils.isUnread(report); - if (!didManuallyMarkReportAsUnread) { - setMessageManuallyMarked({read: false}); - return; + if (didManuallyMarkReportAsUnread) { + // Clearing the current unread marker so that it can be recalculated + currentUnreadMarker.current = null; + setMessageManuallyMarkedUnread(new Date().getTime()); + } else { + setMessageManuallyMarkedUnread(0); } - // Clearing the current unread marker so that it can be recalculated - currentUnreadMarker.current = null; - setMessageManuallyMarked({read: true}); - // We only care when a new lastReadTime is set in the report // eslint-disable-next-line react-hooks/exhaustive-deps }, [report.lastReadTime]); @@ -230,7 +229,7 @@ function ReportActionsList({ const isCurrentMessageUnread = isMessageUnread(reportAction, report.lastReadTime); shouldDisplayNewMarker = isCurrentMessageUnread && !isMessageUnread(nextMessage, report.lastReadTime); - if (!messageManuallyMarked.read) { + if (!messageManuallyMarkedUnread) { shouldDisplayNewMarker = shouldDisplayNewMarker && reportAction.actorAccountID !== Report.getCurrentUserAccountID(); } const canDisplayMarker = scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD ? reportAction.created < userActiveSince.current : true; @@ -272,7 +271,7 @@ function ReportActionsList({ /> ); }, - [report, hasOutstandingIOU, sortedReportActions, mostRecentIOUReportActionID, messageManuallyMarked], + [report, hasOutstandingIOU, sortedReportActions, mostRecentIOUReportActionID, messageManuallyMarkedUnread], ); // Native mobile does not render updates flatlist the changes even though component did update called. From be7ea564639ef27e0ee8052c948d94f3119211b5 Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Mon, 28 Aug 2023 16:31:16 +0200 Subject: [PATCH 003/234] Linting --- src/pages/home/ReportScreenContext.js | 5 +---- src/pages/home/report/ReportActionItem.js | 2 +- src/pages/home/report/ReportActionsView.js | 5 ++--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/pages/home/ReportScreenContext.js b/src/pages/home/ReportScreenContext.js index 0be1882699f4..1e8d30cf7585 100644 --- a/src/pages/home/ReportScreenContext.js +++ b/src/pages/home/ReportScreenContext.js @@ -3,7 +3,4 @@ import {createContext} from 'react'; const ActionListContext = createContext(); const ReactionListContext = createContext(); -export { - ActionListContext, - ReactionListContext -} \ No newline at end of file +export {ActionListContext, ReactionListContext}; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 4a0c0d7bd3ba..fe2ea3acf691 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -64,7 +64,7 @@ import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils'; import ReportActionItemBasicMessage from './ReportActionItemBasicMessage'; import * as store from '../../../libs/actions/ReimbursementAccount/store'; import * as BankAccounts from '../../../libs/actions/BankAccounts'; -import { ReactionListContext } from '../ReportScreenContext'; +import {ReactionListContext} from '../ReportScreenContext'; import Permissions from '../../../libs/Permissions'; const propTypes = { diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 70eb65ade3ea..25d01a6953d1 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -19,7 +19,7 @@ import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; import reportPropTypes from '../../reportPropTypes'; import PopoverReactionList from './ReactionList/PopoverReactionList'; import getIsReportFullyVisible from '../../../libs/getIsReportFullyVisible'; -import { ReactionListContext } from '../ReportScreenContext'; +import {ReactionListContext} from '../ReportScreenContext'; const propTypes = { /** The report currently being looked at */ @@ -54,9 +54,8 @@ const defaultProps = { }; function ReportActionsView(props) { - useCopySelectionHelper(); - const reactionListRef = useContext(ReactionListContext) + const reactionListRef = useContext(ReactionListContext); const didLayout = useRef(false); const didSubscribeToReportTypingEvents = useRef(false); const hasCachedActions = useRef(_.size(props.reportActions) > 0); From 43a729626bc2a3a252a83dadcc5c28e881e14fb1 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 28 Aug 2023 14:55:28 -0700 Subject: [PATCH 004/234] add distance request consts --- src/CONST.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.js b/src/CONST.js index c78268aacd8b..53a7f14c75b5 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1309,6 +1309,7 @@ const CONST = { DATE: 'date', DESCRIPTION: 'description', MERCHANT: 'merchant', + DISTANCE: 'distance', }, FOOTER: { EXPENSE_MANAGEMENT_URL: `${USE_EXPENSIFY_URL}/expense-management`, From 28c293e462f7d1b58d1bd5f1cdeb4d86955ccad5 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 28 Aug 2023 16:03:53 -0700 Subject: [PATCH 005/234] add DISTANCE_REQUEST ONYXKEY --- src/ONYXKEYS.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d4d2ab1f90a6..78430e0fdf33 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -41,6 +41,9 @@ const ONYXKEYS = { // Contains loading data for the IOU feature (MoneyRequestModal, IOUDetail, & MoneyRequestPreview Components) IOU: 'iou', + // Contains loading data for the DistanceRequest components (MoneyRequestEditWaypointPage) + DISTANCE_REQUEST: 'distanceRequest', + /** Keeps track if there is modal currently visible or not */ MODAL: 'modal', From 322504aafb2a51ac8ffa998db3b12f24935371f9 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 28 Aug 2023 16:04:17 -0700 Subject: [PATCH 006/234] add routes for editdistancerequest and editrequestwaypoint --- src/ROUTES.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ROUTES.js b/src/ROUTES.js index bf1beaecb3c3..df36bf3e5069 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -97,6 +97,7 @@ export default { MONEY_REQUEST_SCAN_TAB: ':iouType/new/:reportID?/scan', MONEY_REQUEST_DISTANCE_TAB: ':iouType/new/:reportID?/distance', MONEY_REQUEST_WAYPOINT: ':iouType/new/waypoint/:waypointIndex', + MONEY_REQUEST_EDIT_WAYPOINT: 'r/:threadReportID/edit/distance/waypoint/:waypointIndex', IOU_SEND_ADD_BANK_ACCOUNT: `${IOU_SEND}/add-bank-account`, IOU_SEND_ADD_DEBIT_CARD: `${IOU_SEND}/add-debit-card`, IOU_SEND_ENABLE_PAYMENTS: `${IOU_SEND}/enable-payments`, @@ -111,6 +112,7 @@ export default { getMoneyRequestMerchantRoute: (iouType, reportID = '') => `${iouType}/new/merchant/${reportID}`, getMoneyRequestDistanceTabRoute: (iouType, reportID = '') => `${iouType}/new/${reportID}/distance`, getMoneyRequestWaypointRoute: (iouType, waypointIndex) => `${iouType}/new/waypoint/${waypointIndex}`, + getMoneyRequestEditWaypointRoute: (threadReportID, waypointIndex) => `r/${threadReportID}/edit/distance/waypoint/${waypointIndex}`, SPLIT_BILL_DETAILS: `r/:reportID/split/:reportActionID`, getSplitBillDetailsRoute: (reportID, reportActionID) => `r/${reportID}/split/${reportActionID}`, getNewTaskRoute: (reportID) => `${NEW_TASK}/${reportID}`, From c5c72a5bba9430de145a99ca1ae2e4eb81dc789c Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 28 Aug 2023 16:04:33 -0700 Subject: [PATCH 007/234] add distance translations --- src/languages/en.js | 1 + src/languages/es.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/languages/en.js b/src/languages/en.js index 5dcb84c4e487..cdc96834289b 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -158,6 +158,7 @@ export default { category: 'Category', receipt: 'Receipt', replace: 'Replace', + distance: 'Distance', }, anonymousReportFooter: { logoTagline: 'Join the discussion.', diff --git a/src/languages/es.js b/src/languages/es.js index 9cb91261cdd5..35ac0c2cc9d8 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -157,6 +157,7 @@ export default { category: 'Categoría', receipt: 'Recibo', replace: 'Sustituir', + distance: 'Distancia', }, anonymousReportFooter: { logoTagline: 'Únete a la discusión.', From 85ac77daa978300b79c7e4386cc0c07f05327e9e Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 28 Aug 2023 16:04:50 -0700 Subject: [PATCH 008/234] add editDistanceRequest actions --- src/libs/actions/IOU.js | 64 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 838214bbd98e..3f64c2c7c756 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -530,6 +530,61 @@ function createDistanceRequest(report, payeeEmail, payeeAccountID, participant, ); } +/** + * Edits an existing distance request + * + * @param {String} transactionID + * @param {Object} transactionChanges + * @param {String} [transactionChanges.created] + * @param {Number} [transactionChanges.amount] + * @param {String} [transactionChanges.comment] + * @param {String} [transactionChanges.waypoints] + * + */ +function editDistanceRequest(transactionID, transactionThreadReportID, transactionChanges) { + const pendingFields = _.mapObject(updatedProperties, () => CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + const clearedPendingFields = _.mapObject(updatedProperties, () => null); + const errorFields = _.mapObject(pendingFields, () => ({ + [DateUtils.getMicroseconds()]: Localize.translateLocal('iou.edit.genericError'), + })); + + const transactionThread = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`]; + const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const iouReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread.parentReportID}`]; + const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); + const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport); + API.write( + 'EditDistanceRequest', + {transactionID, ...ReportUtils.getTransactionDetails(updatedTransaction)}, + { + optimisticData: [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: {...updatedProperties, pendingFields}, + }, + ], + successData: [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: {pendingFields: clearedPendingFields}, + }, + ], + failureData: [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + pendingFields: clearedPendingFields, + errorFields, + }, + }, + ], + }, + ); +} + /** * Request money from another user * @@ -1767,6 +1822,13 @@ function createEmptyTransaction() { Onyx.merge(ONYXKEYS.IOU, {transactionID}); } +/** + * @param {String} transactionID + */ +function setDistanceRequestTransactionID(transactionID) { + Onyx.set(ONYXKEYS.DISTANCE_REQUEST, {transactionID}); +} + /** * Navigates to the next IOU page based on where the IOU request was started * @@ -1825,6 +1887,8 @@ export { setMoneyRequestMerchant, setMoneyRequestParticipants, setMoneyRequestReceipt, + setDistanceRequestTransactionID, createEmptyTransaction, navigateToNextPage, + editDistanceRequest, }; From 4cac0948a58821e3c9587b178eb82af91f657292 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 28 Aug 2023 16:05:12 -0700 Subject: [PATCH 009/234] add new pages for editing distance requests --- src/pages/EditRequestDistancePage.js | 51 ++++++++++++++++++ src/pages/iou/MoneyRequestEditWaypointPage.js | 52 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/pages/EditRequestDistancePage.js create mode 100644 src/pages/iou/MoneyRequestEditWaypointPage.js diff --git a/src/pages/EditRequestDistancePage.js b/src/pages/EditRequestDistancePage.js new file mode 100644 index 000000000000..f2587db43a14 --- /dev/null +++ b/src/pages/EditRequestDistancePage.js @@ -0,0 +1,51 @@ +import React, {useEffect} from 'react'; +import PropTypes from 'prop-types'; +import ScreenWrapper from '../components/ScreenWrapper'; +import HeaderWithBackButton from '../components/HeaderWithBackButton'; +import Navigation from '../libs/Navigation/Navigation'; +import useLocalize from '../hooks/useLocalize'; +import DistanceRequest from '../components/DistanceRequest'; +import reportPropTypes from './reportPropTypes'; +import * as IOU from '../libs/actions/IOU'; + +const propTypes = { + /** The transactionID we're currently editing */ + transactionID: PropTypes.number, + + /** The report to with which the distance request is associated */ + report: reportPropTypes, +}; + +function EditRequestDistancePage({iou, report}) { + + useEffect(() => { + IOU.setDistanceRequestTransactionID(iou.transactionID); + }, []) + + const {translate} = useLocalize(); + return ( + + Navigation.goBack()} + /> + { + IOU.editDistanceRequest(iou.transactionID, report.reportID, {waypoints}); + Navigation.dismissModal(); + }} + /> + + ); +} + +EditRequestDistancePage.propTypes = propTypes; +EditRequestDistancePage.displayName = 'EditRequestDistancePage'; + +export default EditRequestDistancePage; diff --git a/src/pages/iou/MoneyRequestEditWaypointPage.js b/src/pages/iou/MoneyRequestEditWaypointPage.js new file mode 100644 index 000000000000..4a212cadd957 --- /dev/null +++ b/src/pages/iou/MoneyRequestEditWaypointPage.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import WaypointEditor from './WaypointEditor'; +import ONYXKEYS from '../../ONYXKEYS'; + +const propTypes = { + /** The transactionID of this request */ + transactionID: PropTypes.string, + + /** Route params */ + route: PropTypes.shape({ + params: PropTypes.shape({ + /** IOU type */ + iouType: PropTypes.string, + + /** Index of the waypoint being edited */ + waypointIndex: PropTypes.string, + }), + }), +}; + +const defaultProps = { + transactionID: '', + route: { + params: { + iouType: '', + waypointIndex: '', + }, + }, +}; + +// This component is responsible for grabbing the transactionID from the IOU key +// You can't use Onyx props in the withOnyx mapping, so we need to set up and access the transactionID here, and then pass it down so that WaypointEditor can subscribe to the transaction. +function MoneyRequestEditWaypointPage({transactionID, route}) { + console.log(">>>> transactionID", transactionID); + return ( + + ); +} + +MoneyRequestEditWaypointPage.displayName = 'MoneyRequestEditWaypointPage'; +MoneyRequestEditWaypointPage.propTypes = propTypes; +MoneyRequestEditWaypointPage.defaultProps = defaultProps; +export default withOnyx({ + // We must provide a default value for transactionID here, otherwise the component won't mount + // because withOnyx returns null until all the keys are defined + transactionID: {key: ONYXKEYS.DISTANCE_REQUEST, selector: (distanceRequest) => (distanceRequest && distanceRequest.transactionID) || ''}, +})(MoneyRequestEditWaypointPage); From ec140606b87805a5dc28f94e5ca816f3dc0f729c Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 28 Aug 2023 16:05:44 -0700 Subject: [PATCH 010/234] simplify DistanceRequest to just take a transactionID, modify to handle edits --- src/components/DistanceRequest.js | 47 ++++++++++++++----------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/components/DistanceRequest.js b/src/components/DistanceRequest.js index c96342dd8965..55975022866a 100644 --- a/src/components/DistanceRequest.js +++ b/src/components/DistanceRequest.js @@ -22,7 +22,6 @@ import useNetwork from '../hooks/useNetwork'; import useLocalize from '../hooks/useLocalize'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; -import * as IOU from '../libs/actions/IOU'; import participantPropTypes from './participantPropTypes'; import reportPropTypes from '../pages/reportPropTypes'; import transactionPropTypes from './transactionPropTypes'; @@ -38,17 +37,8 @@ const MAP_PADDING = 50; const DEFAULT_ZOOM_LEVEL = 10; const propTypes = { - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: PropTypes.shape({ - id: PropTypes.string, - amount: PropTypes.number, - currency: PropTypes.string, - participants: PropTypes.arrayOf(participantPropTypes), - transactionID: PropTypes.string, - }), - - /** Type of money request (i.e. IOU) */ - iouType: PropTypes.string, + /** The transactionID of the distance request we're currently looking at */ + transactionID: PropTypes.number, /** The report to with which the distance request is associated */ report: reportPropTypes, @@ -64,6 +54,12 @@ const propTypes = { /** Time when the token will expire in ISO 8601 */ expiration: PropTypes.string, }), + + /** Are we editing an existing distance request, or creating a new one? */ + isEditingRequest: PropTypes.bool, + + /** Called on submit of this page */ + onSubmit: PropTypes.func.isRequired, }; const defaultProps = { @@ -71,15 +67,13 @@ const defaultProps = { id: '', amount: 0, currency: CONST.CURRENCY.USD, - participants: [], }, - iouType: '', - report: {}, transaction: {}, mapboxAccessToken: {}, + isEditingRequest: false, }; -function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken}) { +function DistanceRequest({transactionID, report, transaction, mapboxAccessToken, isEditingRequest, onSubmit}) { const [shouldShowGradient, setShouldShowGradient] = useState(false); const [scrollContainerHeight, setScrollContainerHeight] = useState(0); const [scrollContentHeight, setScrollContentHeight] = useState(0); @@ -136,12 +130,13 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken}) }, []); useEffect(() => { - if (!iou.transactionID || !_.isEmpty(waypoints)) { + console.log(">>>>", transactionID, waypoints); + if (!transactionID || !_.isEmpty(waypoints)) { return; } // Create the initial start and stop waypoints - Transaction.createInitialWaypoints(iou.transactionID); - }, [iou.transactionID, waypoints]); + Transaction.createInitialWaypoints(transactionID); + }, [transactionID, waypoints]); const updateGradientVisibility = (event = {}) => { // If a waypoint extends past the bottom of the visible area show the gradient, else hide it. @@ -155,8 +150,8 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken}) return; } - Transaction.getRoute(iou.transactionID, waypoints); - }, [shouldFetchRoute, iou.transactionID, waypoints]); + Transaction.getRoute(transactionID, waypoints); + }, [shouldFetchRoute, transactionID, waypoints]); useEffect(updateGradientVisibility, [scrollContainerHeight, scrollContentHeight]); @@ -196,7 +191,7 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken}) secondaryIcon={waypointIcon} secondaryIconFill={theme.icon} shouldShowRightIcon - onPress={() => Navigation.navigate(ROUTES.getMoneyRequestWaypointRoute('request', index))} + onPress={() => Navigation.navigate(isEditingRequest ? ROUTES.getMoneyRequestEditWaypointRoute(report.reportID, index) : ROUTES.getMoneyRequestWaypointRoute('request', index))} key={key} /> ); @@ -220,7 +215,7 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken})