From 887d7001d6405dcbf2e2f8cbffa0f963ae197827 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 22 Sep 2023 16:43:48 +0700 Subject: [PATCH 0001/1250] fix: 22383 Whole thread disappears if the thread header is flagged as an Assault or Harassment --- src/libs/ReportActionsUtils.js | 3 ++- src/libs/ReportUtils.js | 15 +++++---------- src/libs/SidebarUtils.js | 2 +- src/pages/home/report/ReportActionItem.js | 14 ++++++++++---- src/pages/home/report/ReportActionItemFragment.js | 8 ++++++++ src/pages/home/report/ReportActionItemMessage.js | 1 + src/pages/home/report/ReportActionItemSingle.js | 1 + 7 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index cda1f39d3087..822b75944c87 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -341,7 +341,8 @@ function shouldReportActionBeVisible(reportAction, key) { return false; } - if (isPendingRemove(reportAction)) { + const isThreadParent = reportAction && reportAction.childReportID && reportAction.childReportID !== 0; + if (isPendingRemove(reportAction) && !isThreadParent) { return false; } diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index eee9d6549f6c..ee4fbe767380 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1694,7 +1694,8 @@ function getReportName(report, policy = undefined) { } if ( lodashGet(parentReportAction, 'message[0].moderationDecision.decision') === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE || - lodashGet(parentReportAction, 'message[0].moderationDecision.decision') === CONST.MODERATION.MODERATOR_DECISION_HIDDEN + lodashGet(parentReportAction, 'message[0].moderationDecision.decision') === CONST.MODERATION.MODERATOR_DECISION_HIDDEN || + lodashGet(parentReportAction, 'message[0].moderationDecision.decision') === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE ) { return Localize.translateLocal('parentReportAction.hiddenMessage'); } @@ -2843,14 +2844,9 @@ function canSeeDefaultRoom(report, policies, betas) { * @param {Object} report * @param {Array} policies * @param {Array} betas - * @param {Object} allReportActions * @returns {Boolean} */ -function canAccessReport(report, policies, betas, allReportActions) { - if (isThread(report) && ReportActionsUtils.isPendingRemove(ReportActionsUtils.getParentReportAction(report, allReportActions))) { - return false; - } - +function canAccessReport(report, policies, betas) { // We hide default rooms (it's basically just domain rooms now) from people who aren't on the defaultRooms beta. if (isDefaultRoom(report) && !canSeeDefaultRoom(report, policies, betas)) { return false; @@ -2883,11 +2879,10 @@ function shouldHideReport(report, currentReportId) { * @param {Boolean} isInGSDMode * @param {String[]} betas * @param {Object} policies - * @param {Object} allReportActions * @param {Boolean} excludeEmptyChats * @returns {boolean} */ -function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, policies, allReportActions, excludeEmptyChats = false) { +function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, policies, excludeEmptyChats = false) { const isInDefaultMode = !isInGSDMode; // Exclude reports that have no data because there wouldn't be anything to show in the option item. @@ -2908,7 +2903,7 @@ function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, return false; } - if (!canAccessReport(report, policies, betas, allReportActions)) { + if (!canAccessReport(report, policies, betas)) { return false; } diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index f645697690e6..06d0f2935168 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -129,7 +129,7 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p const isInDefaultMode = !isInGSDMode; const allReportsDictValues = Object.values(allReportsDict); // Filter out all the reports that shouldn't be displayed - const reportsToDisplay = allReportsDictValues.filter((report) => ReportUtils.shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, policies, allReportActions, true)); + const reportsToDisplay = allReportsDictValues.filter((report) => ReportUtils.shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, policies, true)); if (reportsToDisplay.length === 0) { // Display Concierge chat report when there is no report to be displayed diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 8cf8fd78371d..896bcb6ae758 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -213,12 +213,13 @@ function ReportActionItem(props) { } setModerationDecision(latestDecision); - if (!_.contains([CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING], latestDecision)) { + if (!_.contains([CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING], latestDecision) && !ReportActionsUtils.isPendingRemove(props.action)) { setIsHidden(true); return; } setIsHidden(false); - }, [latestDecision, props.action.actionName]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [latestDecision, props.action.actionName, latestDecision]); const toggleContextMenuFromActiveReportAction = useCallback(() => { setIsContextMenuActive(ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); @@ -353,7 +354,9 @@ function ReportActionItem(props) { } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE) { children = ; } else { - const hasBeenFlagged = !_.contains([CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING], moderationDecision); + const hasBeenFlagged = + !_.contains([CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING], moderationDecision) && + !ReportActionsUtils.isPendingRemove(props.action); children = ( {content} diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 1f61b44841bc..3c41f91d2bcf 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -69,6 +69,9 @@ const propTypes = { /** Whether the comment is a thread parent message/the first message in a thread */ isThreadParentMessage: PropTypes.bool, + /** Moderation decision of the report action */ + moderationDecision: PropTypes.string, + ...windowDimensionsPropTypes, /** localization props */ @@ -91,6 +94,7 @@ const defaultProps = { delegateAccountID: 0, actorIcon: {}, isThreadParentMessage: false, + moderationDecision: '', }; function ReportActionItemFragment(props) { @@ -121,6 +125,10 @@ function ReportActionItemFragment(props) { return ${props.translate('parentReportAction.deletedMessage')}`} />; } + if (props.isThreadParentMessage && props.moderationDecision === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE) { + return ${props.translate('parentReportAction.hiddenMessage')}`} />; + } + // If the only difference between fragment.text and fragment.html is
tags // we render it as text, not as html. // This is done to render emojis with line breaks between them as text. diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index bc92889158d0..b18599621c34 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -63,6 +63,7 @@ function ReportActionItemMessage(props) { accountID={props.action.actorAccountID} loading={props.action.isLoading} style={props.style} + moderationDecision={lodashGet(props.action, 'message[0].moderationDecision.decision')} /> )) ) : ( diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index bfbce8aed336..c0614ddcde53 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -243,6 +243,7 @@ function ReportActionItemSingle(props) { delegateAccountID={props.action.delegateAccountID} isSingleLine actorIcon={icon} + moderationDecision={lodashGet(props.action, 'message[0].moderationDecision.decision')} /> ))} From fe3ed22888aef75ffa04b65d6c86b0458ae86392 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 26 Sep 2023 13:19:13 +0700 Subject: [PATCH 0002/1250] fix: LHN message --- src/CONST.ts | 1 + src/libs/OptionsListUtils.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index c6ae9cc5928d..883f4325ff6a 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2586,6 +2586,7 @@ const CONST = { }, TRANSLATION_KEYS: { ATTACHMENT: 'common.attachment', + HIDDEN_MESSSAGE: 'parentReportAction.hiddenMessage', }, TEACHERS_UNITE: { PUBLIC_ROOM_ID: '207591744844000', diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7c36fa095029..2bd70705103f 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -403,6 +403,9 @@ function getLastMessageTextForReport(report) { // then set the last message text to the text of the latest visible action that is not a whisper or the report creation message. const lastNonWhisper = _.find(allSortedReportActions[report.reportID], (action) => !ReportActionUtils.isWhisperAction(action)) || {}; if (ReportActionUtils.isPendingRemove(lastNonWhisper)) { + if (ReportActionUtils.isThreadParentMessage(lastNonWhisper)) { + return Localize.translateLocal(CONST.TRANSLATION_KEYS.HIDDEN_MESSSAGE); + } const latestVisibleAction = _.find( allSortedReportActions[report.reportID], From 8910bb99ea6f517f4cf1808876974aba05da5b5c Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 26 Sep 2023 23:28:55 +0700 Subject: [PATCH 0003/1250] fix deleted message --- src/libs/OptionsListUtils.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 2bd70705103f..5cef616bd4f4 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -381,10 +381,11 @@ function getAllReportErrors(report, reportActions) { * @returns {String} */ function getLastMessageTextForReport(report) { - const lastReportAction = _.find( + const visibleActions = _.filter( allSortedReportActions[report.reportID], (reportAction, key) => ReportActionUtils.shouldReportActionBeVisible(reportAction, key) && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); + const lastReportAction = _.last(visibleActions); let lastMessageTextFromReport = ''; if (ReportUtils.isReportMessageAttachment({text: report.lastMessageText, html: report.lastMessageHtml, translationKey: report.lastMessageTranslationKey})) { @@ -401,7 +402,7 @@ function getLastMessageTextForReport(report) { // Yeah this is a bit ugly. If the latest report action that is not a whisper has been moderated as pending remove // then set the last message text to the text of the latest visible action that is not a whisper or the report creation message. - const lastNonWhisper = _.find(allSortedReportActions[report.reportID], (action) => !ReportActionUtils.isWhisperAction(action)) || {}; + const lastNonWhisper = _.find(visibleActions, (action) => !ReportActionUtils.isWhisperAction(action)) || {}; if (ReportActionUtils.isPendingRemove(lastNonWhisper)) { if (ReportActionUtils.isThreadParentMessage(lastNonWhisper)) { return Localize.translateLocal(CONST.TRANSLATION_KEYS.HIDDEN_MESSSAGE); From 787d2e30e7566615d709b697a3f4be403c3ead09 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 26 Sep 2023 23:46:32 +0700 Subject: [PATCH 0004/1250] fix change to first --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 5cef616bd4f4..5aadf33028b9 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -385,7 +385,7 @@ function getLastMessageTextForReport(report) { allSortedReportActions[report.reportID], (reportAction, key) => ReportActionUtils.shouldReportActionBeVisible(reportAction, key) && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); - const lastReportAction = _.last(visibleActions); + const lastReportAction = _.first(visibleActions); let lastMessageTextFromReport = ''; if (ReportUtils.isReportMessageAttachment({text: report.lastMessageText, html: report.lastMessageHtml, translationKey: report.lastMessageTranslationKey})) { From aa741899a75e9d81bccd39787e85fcdcdbc8a6d2 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 13:28:10 +0700 Subject: [PATCH 0005/1250] fix: isThreadParent --- src/libs/ReportActionsUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 5ac542f9e926..858b71ec0fe3 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -356,7 +356,7 @@ function shouldReportActionBeVisible(reportAction, key) { return false; } - const isThreadParent = reportAction && reportAction.childReportID && reportAction.childReportID !== 0; + const isThreadParent = isThreadParentMessage(reportAction, ''); if (isPendingRemove(reportAction) && !isThreadParent) { return false; } From 0487fc89584e948fc44ff977d1f0eb8337ced9eb Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 14:06:53 +0700 Subject: [PATCH 0006/1250] fix lastNonWhisper --- src/libs/OptionsListUtils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 5aadf33028b9..a84f1b3e5286 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -381,11 +381,10 @@ function getAllReportErrors(report, reportActions) { * @returns {String} */ function getLastMessageTextForReport(report) { - const visibleActions = _.filter( + const lastReportAction = _.find( allSortedReportActions[report.reportID], (reportAction, key) => ReportActionUtils.shouldReportActionBeVisible(reportAction, key) && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); - const lastReportAction = _.first(visibleActions); let lastMessageTextFromReport = ''; if (ReportUtils.isReportMessageAttachment({text: report.lastMessageText, html: report.lastMessageHtml, translationKey: report.lastMessageTranslationKey})) { @@ -402,7 +401,8 @@ function getLastMessageTextForReport(report) { // Yeah this is a bit ugly. If the latest report action that is not a whisper has been moderated as pending remove // then set the last message text to the text of the latest visible action that is not a whisper or the report creation message. - const lastNonWhisper = _.find(visibleActions, (action) => !ReportActionUtils.isWhisperAction(action)) || {}; + const lastNonWhisper = + _.find(allSortedReportActions[report.reportID], (action) => !ReportActionUtils.isWhisperAction(action) && !ReportActionUtils.isDeletedParentAction(action)) || {}; if (ReportActionUtils.isPendingRemove(lastNonWhisper)) { if (ReportActionUtils.isThreadParentMessage(lastNonWhisper)) { return Localize.translateLocal(CONST.TRANSLATION_KEYS.HIDDEN_MESSSAGE); From de6bb44632f5e13d39701baaee5fa358018b91e4 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 20 Oct 2023 15:31:04 +0700 Subject: [PATCH 0007/1250] merge main --- src/libs/OptionsListUtils.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 1653339d81b1..24b49f80c32d 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -418,14 +418,16 @@ function getLastMessageTextForReport(report) { const lastNonWhisper = _.find(allSortedReportActions[report.reportID], (action) => !ReportActionUtils.isWhisperAction(action) && !ReportActionUtils.isDeletedParentAction(action)) || {}; if (ReportActionUtils.isPendingRemove(lastNonWhisper)) { - if (ReportActionUtils.isThreadParentMessage(lastNonWhisper)) { - return Localize.translateLocal(CONST.TRANSLATION_KEYS.HIDDEN_MESSSAGE); - } const latestVisibleAction = _.find( allSortedReportActions[report.reportID], (action) => ReportActionUtils.shouldReportActionBeVisibleAsLastAction(action) && !ReportActionUtils.isCreatedAction(action), ) || {}; + + if (ReportActionUtils.isThreadParentMessage(latestVisibleAction) && ReportActionUtils.isPendingRemove(latestVisibleAction)) { + return Localize.translateLocal(CONST.TRANSLATION_KEYS.HIDDEN_MESSSAGE); + } + lastMessageTextFromReport = lodashGet(latestVisibleAction, 'message[0].text', ''); } } From 53bc74cf8a2cb666731b348b8e75c675104fb816 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 20 Oct 2023 15:39:18 +0700 Subject: [PATCH 0008/1250] fix remove js file --- src/libs/ReportActionsUtils.js | 690 --------------------------------- 1 file changed, 690 deletions(-) delete mode 100644 src/libs/ReportActionsUtils.js diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js deleted file mode 100644 index 6d6fe0fb44c8..000000000000 --- a/src/libs/ReportActionsUtils.js +++ /dev/null @@ -1,690 +0,0 @@ -/* eslint-disable rulesdir/prefer-underscore-method */ -import lodashGet from 'lodash/get'; -import _ from 'underscore'; -import {max, parseISO, isEqual} from 'date-fns'; -import lodashFindLast from 'lodash/findLast'; -import Onyx from 'react-native-onyx'; -import * as CollectionUtils from './CollectionUtils'; -import CONST from '../CONST'; -import ONYXKEYS from '../ONYXKEYS'; -import Log from './Log'; -import isReportMessageAttachment from './isReportMessageAttachment'; - -const allReports = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - callback: (report, key) => { - if (!key || !report) { - return; - } - - const reportID = CollectionUtils.extractCollectionItemID(key); - allReports[reportID] = report; - }, -}); - -const allReportActions = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - callback: (actions, key) => { - if (!key || !actions) { - return; - } - - const reportID = CollectionUtils.extractCollectionItemID(key); - allReportActions[reportID] = actions; - }, -}); - -let isNetworkOffline = false; -Onyx.connect({ - key: ONYXKEYS.NETWORK, - callback: (val) => (isNetworkOffline = lodashGet(val, 'isOffline', false)), -}); - -/** - * @param {Object} reportAction - * @returns {Boolean} - */ -function isCreatedAction(reportAction) { - return lodashGet(reportAction, 'actionName') === CONST.REPORT.ACTIONS.TYPE.CREATED; -} - -/** - * @param {Object} reportAction - * @returns {Boolean} - */ -function isDeletedAction(reportAction) { - // A deleted comment has either an empty array or an object with html field with empty string as value - const message = lodashGet(reportAction, 'message', []); - return message.length === 0 || lodashGet(message, [0, 'html']) === ''; -} - -/** - * @param {Object} reportAction - * @returns {Boolean} - */ -function isDeletedParentAction(reportAction) { - return lodashGet(reportAction, ['message', 0, 'isDeletedParentAction'], false) && lodashGet(reportAction, 'childVisibleActionCount', 0) > 0; -} - -/** - * @param {Object} reportAction - * @returns {Boolean} - */ -function isPendingRemove(reportAction) { - return lodashGet(reportAction, 'message[0].moderationDecision.decision') === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE; -} - -/** - * @param {Object} reportAction - * @returns {Boolean} - */ -function isMoneyRequestAction(reportAction) { - return lodashGet(reportAction, 'actionName', '') === CONST.REPORT.ACTIONS.TYPE.IOU; -} - -/** - * @param {Object} reportAction - * @returns {Boolean} - */ -function isReportPreviewAction(reportAction) { - return lodashGet(reportAction, 'actionName', '') === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW; -} - -/** - * @param {Object} reportAction - * @returns {Boolean} - */ -function isModifiedExpenseAction(reportAction) { - return lodashGet(reportAction, 'actionName', '') === CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE; -} - -function isWhisperAction(action) { - return (action.whisperedToAccountIDs || []).length > 0; -} - -/** - * Returns whether the comment is a thread parent message/the first message in a thread - * - * @param {Object} reportAction - * @param {String} reportID - * @returns {Boolean} - */ -function isThreadParentMessage(reportAction = {}, reportID) { - const {childType, childVisibleActionCount = 0, childReportID} = reportAction; - return childType === CONST.REPORT.TYPE.CHAT && (childVisibleActionCount > 0 || String(childReportID) === reportID); -} - -/** - * Returns the parentReportAction if the given report is a thread/task. - * - * @param {Object} report - * @param {Object} [allReportActionsParam] - * @returns {Object} - * @deprecated Use Onyx.connect() or withOnyx() instead - */ -function getParentReportAction(report, allReportActionsParam = undefined) { - if (!report || !report.parentReportID || !report.parentReportActionID) { - return {}; - } - return lodashGet(allReportActionsParam || allReportActions, [report.parentReportID, report.parentReportActionID], {}); -} - -/** - * Determines if the given report action is sent money report action by checking for 'pay' type and presence of IOUDetails object. - * - * @param {Object} reportAction - * @returns {Boolean} - */ -function isSentMoneyReportAction(reportAction) { - return ( - reportAction && - reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && - lodashGet(reportAction, 'originalMessage.type') === CONST.IOU.REPORT_ACTION_TYPE.PAY && - _.has(reportAction.originalMessage, 'IOUDetails') - ); -} - -/** - * Returns whether the thread is a transaction thread, which is any thread with IOU parent - * report action from requesting money (type - create) or from sending money (type - pay with IOUDetails field) - * - * @param {Object} parentReportAction - * @returns {Boolean} - */ -function isTransactionThread(parentReportAction) { - const originalMessage = lodashGet(parentReportAction, 'originalMessage', {}); - return ( - parentReportAction && - parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && - (originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.CREATE || (originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.PAY && _.has(originalMessage, 'IOUDetails'))) - ); -} - -/** - * Sort an array of reportActions by their created timestamp first, and reportActionID second - * This gives us a stable order even in the case of multiple reportActions created on the same millisecond - * - * @param {Array} reportActions - * @param {Boolean} shouldSortInDescendingOrder - * @returns {Array} - */ -function getSortedReportActions(reportActions, shouldSortInDescendingOrder = false) { - if (!_.isArray(reportActions)) { - throw new Error(`ReportActionsUtils.getSortedReportActions requires an array, received ${typeof reportActions}`); - } - - const invertedMultiplier = shouldSortInDescendingOrder ? -1 : 1; - return _.chain(reportActions) - .compact() - .sort((first, second) => { - // First sort by timestamp - if (first.created !== second.created) { - return (first.created < second.created ? -1 : 1) * invertedMultiplier; - } - - // Then by action type, ensuring that `CREATED` actions always come first if they have the same timestamp as another action type - if ((first.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED || second.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) && first.actionName !== second.actionName) { - return (first.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED ? -1 : 1) * invertedMultiplier; - } - // Ensure that `REPORTPREVIEW` actions always come after if they have the same timestamp as another action type - if ((first.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW || second.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW) && first.actionName !== second.actionName) { - return (first.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW ? 1 : -1) * invertedMultiplier; - } - - // Then fallback on reportActionID as the final sorting criteria. It is a random number, - // but using this will ensure that the order of reportActions with the same created time and action type - // will be consistent across all users and devices - return (first.reportActionID < second.reportActionID ? -1 : 1) * invertedMultiplier; - }) - .value(); -} - -/** - * Finds most recent IOU request action ID. - * - * @param {Array} reportActions - * @returns {String} - */ -function getMostRecentIOURequestActionID(reportActions) { - const iouRequestTypes = [CONST.IOU.REPORT_ACTION_TYPE.CREATE, CONST.IOU.REPORT_ACTION_TYPE.SPLIT]; - const iouRequestActions = _.filter(reportActions, (action) => iouRequestTypes.includes(lodashGet(action, 'originalMessage.type'))); - - if (_.isEmpty(iouRequestActions)) { - return null; - } - - const sortedReportActions = getSortedReportActions(iouRequestActions); - return _.last(sortedReportActions).reportActionID; -} - -/** - * Returns array of links inside a given report action - * - * @param {Object} reportAction - * @returns {Array} - */ -function extractLinksFromMessageHtml(reportAction) { - const htmlContent = lodashGet(reportAction, ['message', 0, 'html']); - - // Regex to get link in href prop inside of component - const regex = /]*?\s+)?href="([^"]*)"/gi; - - if (!htmlContent) { - return []; - } - - return _.map([...htmlContent.matchAll(regex)], (match) => match[1]); -} - -/** - * Returns the report action immediately before the specified index. - * @param {Array} reportActions - all actions - * @param {Number} actionIndex - index of the action - * @returns {Object|null} - */ -function findPreviousAction(reportActions, actionIndex) { - for (let i = actionIndex + 1; i < reportActions.length; i++) { - // Find the next non-pending deletion report action, as the pending delete action means that it is not displayed in the UI, but still is in the report actions list. - // If we are offline, all actions are pending but shown in the UI, so we take the previous action, even if it is a delete. - if (isNetworkOffline || reportActions[i].pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - return reportActions[i]; - } - } - return null; -} - -/** - * Returns true when the report action immediately before the specified index is a comment made by the same actor who who is leaving a comment in the action at the specified index. - * Also checks to ensure that the comment is not too old to be shown as a grouped comment. - * - * @param {Array} reportActions - * @param {Number} actionIndex - index of the comment item in state to check - * @returns {Boolean} - */ -function isConsecutiveActionMadeByPreviousActor(reportActions, actionIndex) { - const previousAction = findPreviousAction(reportActions, actionIndex); - const currentAction = reportActions[actionIndex]; - - // It's OK for there to be no previous action, and in that case, false will be returned - // so that the comment isn't grouped - if (!currentAction || !previousAction) { - return false; - } - - // Comments are only grouped if they happen within 5 minutes of each other - if (new Date(currentAction.created).getTime() - new Date(previousAction.created).getTime() > 300000) { - return false; - } - - // Do not group if previous action was a created action - if (previousAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) { - return false; - } - - // Do not group if previous or current action was a renamed action - if (previousAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED || currentAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED) { - return false; - } - - // Do not group if the delegate account ID is different - if (previousAction.delegateAccountID !== currentAction.delegateAccountID) { - return false; - } - - return currentAction.actorAccountID === previousAction.actorAccountID; -} - -/** - * Checks if a reportAction is deprecated. - * - * @param {Object} reportAction - * @param {String} key - * @returns {Boolean} - */ -function isReportActionDeprecated(reportAction, key) { - if (!reportAction) { - return true; - } - - // HACK ALERT: We're temporarily filtering out any reportActions keyed by sequenceNumber - // to prevent bugs during the migration from sequenceNumber -> reportActionID - if (String(reportAction.sequenceNumber) === key) { - Log.info('Front-end filtered out reportAction keyed by sequenceNumber!', false, reportAction); - return true; - } - - return false; -} - -/** - * Checks if a reportAction is fit for display, meaning that it's not deprecated, is of a valid - * and supported type, it's not deleted and also not closed. - * - * @param {Object} reportAction - * @param {String} key - * @returns {Boolean} - */ -function shouldReportActionBeVisible(reportAction, key) { - if (isReportActionDeprecated(reportAction, key)) { - return false; - } - - if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.TASKEDITED) { - return false; - } - - // Filter out any unsupported reportAction types - if (!Object.values(CONST.REPORT.ACTIONS.TYPE).includes(reportAction.actionName) && !Object.values(CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG).includes(reportAction.actionName)) { - return false; - } - - // Ignore closed action here since we're already displaying a footer that explains why the report was closed - if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED) { - return false; - } - - const isThreadParent = isThreadParentMessage(reportAction, ''); - if (isPendingRemove(reportAction) && !isThreadParent) { - return false; - } - - // All other actions are displayed except thread parents, deleted, or non-pending actions - const isDeleted = isDeletedAction(reportAction); - const isPending = !!reportAction.pendingAction; - return !isDeleted || isPending || isDeletedParentAction(reportAction); -} - -/** - * Checks if a reportAction is fit for display as report last action, meaning that - * it satisfies shouldReportActionBeVisible, it's not whisper action and not deleted. - * - * @param {Object} reportAction - * @returns {Boolean} - */ -function shouldReportActionBeVisibleAsLastAction(reportAction) { - if (!reportAction) { - return false; - } - - if (!_.isEmpty(reportAction.errors)) { - return false; - } - - // If a whisper action is the REPORTPREVIEW action, we are displaying it. - return ( - shouldReportActionBeVisible(reportAction, reportAction.reportActionID) && - !(isWhisperAction(reportAction) && !isReportPreviewAction(reportAction) && !isMoneyRequestAction(reportAction)) && - !isDeletedAction(reportAction) - ); -} - -/** - * @param {String} reportID - * @param {Object} [actionsToMerge] - * @return {Object} - */ -function getLastVisibleAction(reportID, actionsToMerge = {}) { - const updatedActionsToMerge = {}; - if (actionsToMerge && Object.keys(actionsToMerge).length !== 0) { - Object.keys(actionsToMerge).forEach( - (actionToMergeID) => (updatedActionsToMerge[actionToMergeID] = {...allReportActions[reportID][actionToMergeID], ...actionsToMerge[actionToMergeID]}), - ); - } - const actions = Object.values({ - ...allReportActions[reportID], - ...updatedActionsToMerge, - }); - const visibleActions = actions.filter((action) => shouldReportActionBeVisibleAsLastAction(action)); - - if (visibleActions.length === 0) { - return {}; - } - const maxDate = max(visibleActions.map((action) => parseISO(action.created))); - const maxAction = visibleActions.find((action) => isEqual(parseISO(action.created), maxDate)); - return maxAction; -} - -/** - * @param {String} reportID - * @param {Object} [actionsToMerge] - * @return {Object} - */ -function getLastVisibleMessage(reportID, actionsToMerge = {}) { - const lastVisibleAction = getLastVisibleAction(reportID, actionsToMerge); - const message = lodashGet(lastVisibleAction, ['message', 0], {}); - - if (isReportMessageAttachment(message)) { - return { - lastMessageTranslationKey: CONST.TRANSLATION_KEYS.ATTACHMENT, - lastMessageText: CONST.ATTACHMENT_MESSAGE_TEXT, - lastMessageHtml: CONST.TRANSLATION_KEYS.ATTACHMENT, - }; - } - - if (isCreatedAction(lastVisibleAction)) { - return { - lastMessageText: '', - }; - } - - const messageText = lodashGet(message, 'text', ''); - return { - lastMessageText: String(messageText).replace(CONST.REGEX.AFTER_FIRST_LINE_BREAK, '').substring(0, CONST.REPORT.LAST_MESSAGE_TEXT_MAX_LENGTH).trim(), - }; -} - -/** - * A helper method to filter out report actions keyed by sequenceNumbers. - * - * @param {Object} reportActions - * @returns {Array} - */ -function filterOutDeprecatedReportActions(reportActions) { - return _.filter(reportActions, (reportAction, key) => !isReportActionDeprecated(reportAction, key)); -} - -/** - * This method returns the report actions that are ready for display in the ReportActionsView. - * The report actions need to be sorted by created timestamp first, and reportActionID second - * 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. - * - * @param {Object} reportActions - * @returns {Array} - */ -function getSortedReportActionsForDisplay(reportActions) { - const filteredReportActions = _.filter(reportActions, (reportAction, key) => shouldReportActionBeVisible(reportAction, key)); - return getSortedReportActions(filteredReportActions, true); -} - -/** - * In some cases, there can be multiple closed report actions in a chat report. - * This method returns the last closed report action so we can always show the correct archived report reason. - * Additionally, archived #admins and #announce do not have the closed report action so we will return null if none is found. - * - * @param {Object} reportActions - * @returns {Object|null} - */ -function getLastClosedReportAction(reportActions) { - // If closed report action is not present, return early - if (!_.some(reportActions, (action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED)) { - return null; - } - const filteredReportActions = filterOutDeprecatedReportActions(reportActions); - const sortedReportActions = getSortedReportActions(filteredReportActions); - return lodashFindLast(sortedReportActions, (action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED); -} - -/** - * @param {Array} onyxData - * @returns {Object} The latest report action in the `onyxData` or `null` if one couldn't be found - */ -function getLatestReportActionFromOnyxData(onyxData) { - const reportActionUpdate = _.find(onyxData, (onyxUpdate) => onyxUpdate.key.startsWith(ONYXKEYS.COLLECTION.REPORT_ACTIONS)); - - if (!reportActionUpdate) { - return null; - } - - const reportActions = _.values(reportActionUpdate.value); - const sortedReportActions = getSortedReportActions(reportActions); - return _.last(sortedReportActions); -} - -/** - * Find the transaction associated with this reportAction, if one exists. - * - * @param {String} reportID - * @param {String} reportActionID - * @returns {String|null} - */ -function getLinkedTransactionID(reportID, reportActionID) { - const reportAction = lodashGet(allReportActions, [reportID, reportActionID]); - if (!reportAction || reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.IOU) { - return null; - } - return reportAction.originalMessage.IOUTransactionID; -} - -/** - * - * @param {String} reportID - * @param {String} reportActionID - * @returns {Object} - */ -function getReportAction(reportID, reportActionID) { - return lodashGet(allReportActions, [reportID, reportActionID], {}); -} - -/** - * @returns {string} - */ -function getMostRecentReportActionLastModified() { - // Start with the oldest date possible - let mostRecentReportActionLastModified = new Date(0).toISOString(); - - // Flatten all the actions - // Loop over them all to find the one that is the most recent - const flatReportActions = _.flatten(_.map(allReportActions, (actions) => _.values(actions))); - _.each(flatReportActions, (action) => { - // Pending actions should not be counted here as a user could create a comment or some other action while offline and the server might know about - // messages they have not seen yet. - if (!_.isEmpty(action.pendingAction)) { - return; - } - - const lastModified = action.lastModified || action.created; - if (lastModified < mostRecentReportActionLastModified) { - return; - } - - mostRecentReportActionLastModified = lastModified; - }); - - // We might not have actions so we also look at the report objects to see if any have a lastVisibleActionLastModified that is more recent. We don't need to get - // any reports that have been updated before either a recently updated report or reportAction as we should be up to date on these - _.each(allReports, (report) => { - const reportLastVisibleActionLastModified = report.lastVisibleActionLastModified || report.lastVisibleActionCreated; - if (!reportLastVisibleActionLastModified || reportLastVisibleActionLastModified < mostRecentReportActionLastModified) { - return; - } - - mostRecentReportActionLastModified = reportLastVisibleActionLastModified; - }); - - return mostRecentReportActionLastModified; -} - -/** - * @param {*} chatReportID - * @param {*} iouReportID - * @returns {Object} The report preview action or `null` if one couldn't be found - */ -function getReportPreviewAction(chatReportID, iouReportID) { - return _.find( - allReportActions[chatReportID], - (reportAction) => reportAction && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && lodashGet(reportAction, 'originalMessage.linkedReportID') === iouReportID, - ); -} - -/** - * Get the iouReportID for a given report action. - * - * @param {Object} reportAction - * @returns {String} - */ -function getIOUReportIDFromReportActionPreview(reportAction) { - return lodashGet(reportAction, 'originalMessage.linkedReportID', ''); -} - -function isCreatedTaskReportAction(reportAction) { - return reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && _.has(reportAction.originalMessage, 'taskReportID'); -} - -/** - * A helper method to identify if the message is deleted or not. - * - * @param {Object} reportAction - * @returns {Boolean} - */ -function isMessageDeleted(reportAction) { - return lodashGet(reportAction, ['message', 0, 'isDeletedParentAction'], false); -} - -/** - * Returns the number of money requests associated with a report preview - * - * @param {Object|null} reportPreviewAction - * @returns {Number} - */ -function getNumberOfMoneyRequests(reportPreviewAction) { - return lodashGet(reportPreviewAction, 'childMoneyRequestCount', 0); -} - -/** - * @param {*} reportAction - * @returns {Boolean} - */ -function isSplitBillAction(reportAction) { - return lodashGet(reportAction, 'originalMessage.type', '') === CONST.IOU.REPORT_ACTION_TYPE.SPLIT; -} - -/** - * - * @param {*} reportAction - * @returns {Boolean} - */ -function isTaskAction(reportAction) { - const reportActionName = lodashGet(reportAction, 'actionName', ''); - return ( - reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED || - reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED || - reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKREOPENED - ); -} - -/** - * @param {*} reportID - * @returns {[Object]} - */ -function getAllReportActions(reportID) { - return lodashGet(allReportActions, reportID, []); -} - -/** - * Check whether a report action is an attachment (a file, such as an image or a zip). - * - * @param {Object} reportAction report action - * @returns {Boolean} - */ -function isReportActionAttachment(reportAction) { - const message = _.first(lodashGet(reportAction, 'message', [{}])); - return _.has(reportAction, 'isAttachment') ? reportAction.isAttachment : isReportMessageAttachment(message); -} - -// eslint-disable-next-line rulesdir/no-negated-variables -function isNotifiableReportAction(reportAction) { - return reportAction && _.contains([CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT, CONST.REPORT.ACTIONS.TYPE.IOU, CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE], reportAction.actionName); -} - -export { - getSortedReportActions, - getLastVisibleAction, - getLastVisibleMessage, - getMostRecentIOURequestActionID, - extractLinksFromMessageHtml, - isCreatedAction, - isDeletedAction, - shouldReportActionBeVisible, - shouldReportActionBeVisibleAsLastAction, - isReportActionDeprecated, - isConsecutiveActionMadeByPreviousActor, - getSortedReportActionsForDisplay, - getLastClosedReportAction, - getLatestReportActionFromOnyxData, - isMoneyRequestAction, - isThreadParentMessage, - getLinkedTransactionID, - getMostRecentReportActionLastModified, - getReportPreviewAction, - isCreatedTaskReportAction, - getParentReportAction, - isTransactionThread, - isSentMoneyReportAction, - isDeletedParentAction, - isReportPreviewAction, - isModifiedExpenseAction, - getIOUReportIDFromReportActionPreview, - isMessageDeleted, - isWhisperAction, - isPendingRemove, - getReportAction, - getNumberOfMoneyRequests, - isSplitBillAction, - isTaskAction, - getAllReportActions, - isReportActionAttachment, - isNotifiableReportAction, -}; From 00a7c235b3c37b96f022ada724f3aa6a0e6f2087 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 23 Oct 2023 15:41:54 +0500 Subject: [PATCH 0009/1250] perf: memoize components --- src/components/AttachmentModal.js | 4 ++-- src/components/EmojiPicker/EmojiPickerButton.js | 4 ++-- src/components/ExceededCommentLength.js | 4 ++-- src/pages/home/report/ReportActionCompose/SendButton.js | 4 ++-- src/pages/home/report/ReportTypingIndicator.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 61b138747950..9cceaab5e63e 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -1,4 +1,4 @@ -import React, {useState, useCallback, useRef, useMemo} from 'react'; +import React, {useState, useCallback, useRef, useMemo, memo} from 'react'; import PropTypes from 'prop-types'; import {View, Animated, Keyboard} from 'react-native'; import Str from 'expensify-common/lib/str'; @@ -534,4 +534,4 @@ export default compose( key: ONYXKEYS.SESSION, }, }), -)(AttachmentModal); +)(memo(AttachmentModal)); diff --git a/src/components/EmojiPicker/EmojiPickerButton.js b/src/components/EmojiPicker/EmojiPickerButton.js index cbfc3517117c..549e7d75005e 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.js +++ b/src/components/EmojiPicker/EmojiPickerButton.js @@ -1,4 +1,4 @@ -import React, {useEffect, useRef} from 'react'; +import React, {memo, useEffect, useRef} from 'react'; import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; @@ -64,4 +64,4 @@ function EmojiPickerButton(props) { EmojiPickerButton.propTypes = propTypes; EmojiPickerButton.defaultProps = defaultProps; EmojiPickerButton.displayName = 'EmojiPickerButton'; -export default withLocalize(EmojiPickerButton); +export default withLocalize(memo(EmojiPickerButton)); diff --git a/src/components/ExceededCommentLength.js b/src/components/ExceededCommentLength.js index 9c785cec0395..587ab11cac37 100644 --- a/src/components/ExceededCommentLength.js +++ b/src/components/ExceededCommentLength.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState, useMemo} from 'react'; +import React, {useEffect, useState, useMemo, memo} from 'react'; import PropTypes from 'prop-types'; import {debounce} from 'lodash'; import {withOnyx} from 'react-native-onyx'; @@ -65,4 +65,4 @@ export default withOnyx({ key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, initialValue: '', }, -})(ExceededCommentLength); +})(memo(ExceededCommentLength)); diff --git a/src/pages/home/report/ReportActionCompose/SendButton.js b/src/pages/home/report/ReportActionCompose/SendButton.js index a97dd420e181..0e1d10d44092 100644 --- a/src/pages/home/report/ReportActionCompose/SendButton.js +++ b/src/pages/home/report/ReportActionCompose/SendButton.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {memo} from 'react'; import {View} from 'react-native'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import PropTypes from 'prop-types'; @@ -61,4 +61,4 @@ function SendButton({isDisabled: isDisabledProp, handleSendMessage}) { SendButton.propTypes = propTypes; SendButton.displayName = 'SendButton'; -export default SendButton; +export default memo(SendButton); diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js index db97f712d65f..b4e5bbe8fe9d 100755 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react'; +import React, {memo, useMemo} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; @@ -77,4 +77,4 @@ export default compose( initialValue: {}, }, }), -)(ReportTypingIndicator); +)(memo(ReportTypingIndicator)); From 9c4f2995a3a86c75ea17f039939199396049d379 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 23 Oct 2023 15:43:15 +0500 Subject: [PATCH 0010/1250] refactor: simplify props --- src/pages/home/ReportScreen.js | 50 ++++++++---- .../SilentCommentUpdater.js | 16 ++-- src/pages/home/report/ReportActionsList.js | 2 +- .../report/ReportActionsListItemRenderer.js | 9 +-- src/pages/home/report/ReportActionsView.js | 10 ++- src/pages/home/report/ReportFooter.js | 81 ++++++++++++------- 6 files changed, 107 insertions(+), 61 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 81000c2dab92..682edb701c14 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -1,10 +1,10 @@ -import React, {useRef, useState, useEffect, useMemo, useCallback} from 'react'; +import React, {useRef, useState, useEffect, useMemo, useCallback, memo} from 'react'; import {withOnyx} from 'react-native-onyx'; import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import lodashGet from 'lodash/get'; -import _ from 'underscore'; +import _, {isEqual} from 'underscore'; import styles from '../../styles/styles'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderView from './HeaderView'; @@ -163,8 +163,9 @@ function ReportScreen({ const {addWorkspaceRoomOrChatPendingAction, addWorkspaceRoomOrChatErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report); const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; + const isEmptyChat = useMemo(() => _.isEmpty(reportActions), [reportActions]); // There are no reportActions at all to display and we are still in the process of loading the next set of actions. - const isLoadingInitialReportActions = _.isEmpty(reportActions) && reportMetadata.isLoadingReportActions; + const isLoadingInitialReportActions = isEmptyChat && reportMetadata.isLoadingReportActions; const isOptimisticDelete = lodashGet(report, 'statusNum') === CONST.REPORT.STATUS.CLOSED; @@ -173,6 +174,10 @@ function ReportScreen({ const isLoading = !reportID || !isSidebarLoaded || _.isEmpty(personalDetails); const parentReportAction = ReportActionsUtils.getParentReportAction(report); + const lastReportAction = useMemo( + () => _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)), + [reportActions, parentReportAction], + ); const isSingleTransactionView = ReportUtils.isMoneyRequest(report); const policy = policies[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`] || {}; @@ -253,16 +258,6 @@ function ReportScreen({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(accountManagerReportID)); }, [accountManagerReportID]); - /** - * @param {String} text - */ - const onSubmitComment = useCallback( - (text) => { - Report.addComment(getReportID(route), text); - }, - [route], - ); - useFocusEffect( useCallback(() => { const unsubscribeVisibilityListener = Visibility.onVisibilityChange(() => { @@ -441,14 +436,15 @@ function ReportScreen({ {isReportReadyForDisplay ? ( ) : ( @@ -515,4 +511,24 @@ export default compose( }, true, ), -)(ReportScreen); +)( + memo( + ReportScreen, + (prevProps, nextProps) => + prevProps.isSidebarLoaded === nextProps.isSidebarLoaded && + isEqual(prevProps.reportActions, nextProps.reportActions) && + isEqual(prevProps.reportMetadata, nextProps.reportMetadata) && + prevProps.isComposerFullSize === nextProps.isComposerFullSize && + isEqual(prevProps.betas, nextProps.betas) && + isEqual(prevProps.policies, nextProps.policies) && + prevProps.accountManagerReportID === nextProps.accountManagerReportID && + isEqual(prevProps.personalDetails, nextProps.personalDetails) && + prevProps.userLeavingStatus === nextProps.userLeavingStatus && + prevProps.report.reportID === nextProps.report.reportID && + prevProps.report.policyID === nextProps.report.policyID && + prevProps.report.isOptimisticReport === nextProps.report.isOptimisticReport && + prevProps.report.statusNum === nextProps.report.statusNum && + isEqual(prevProps.report.pendingFields, nextProps.report.pendingFields) && + prevProps.currentReportID === nextProps.currentReportID, + ), +); diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js index 09f9d368bdcc..2d7a65ca4cc7 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js @@ -9,12 +9,6 @@ const propTypes = { /** The comment of the report */ comment: PropTypes.string, - /** The report associated with the comment */ - report: PropTypes.shape({ - /** The ID of the report */ - reportID: PropTypes.string, - }).isRequired, - /** The value of the comment */ value: PropTypes.string.isRequired, @@ -26,6 +20,8 @@ const propTypes = { /** Updates the comment */ updateComment: PropTypes.func.isRequired, + + reportID: PropTypes.string.isRequired, }; const defaultProps = { @@ -38,9 +34,9 @@ const defaultProps = { * re-rendering a UI component for that. That's why the side effect was moved down to a separate component. * @returns {null} */ -function SilentCommentUpdater({comment, commentRef, report, value, updateComment}) { +function SilentCommentUpdater({comment, commentRef, reportID, value, updateComment}) { const prevCommentProp = usePrevious(comment); - const prevReportId = usePrevious(report.reportID); + const prevReportId = usePrevious(reportID); const {preferredLocale} = useLocalize(); const prevPreferredLocale = usePrevious(preferredLocale); @@ -51,12 +47,12 @@ function SilentCommentUpdater({comment, commentRef, report, value, updateComment // As the report IDs change, make sure to update the composer comment as we need to make sure // we do not show incorrect data in there (ie. draft of message from other report). - if (preferredLocale === prevPreferredLocale && report.reportID === prevReportId && !shouldSyncComment) { + if (preferredLocale === prevPreferredLocale && reportID === prevReportId && !shouldSyncComment) { return; } updateComment(comment); - }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, report.reportID, updateComment, value, commentRef]); + }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, reportID, updateComment, value, commentRef]); return null; } diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index febcf3cd3507..a0cdee57f600 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -327,7 +327,7 @@ function ReportActionsList({ report={report} linkedReportActionID={linkedReportActionID} hasOutstandingIOU={hasOutstandingIOU} - sortedReportActions={sortedReportActions} + displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedReportActions, index)} mostRecentIOUReportActionID={mostRecentIOUReportActionID} shouldHideThreadDividerLine={shouldHideThreadDividerLine} shouldDisplayNewMarker={shouldDisplayNewMarker(reportAction, index)} diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index 40b9ee9142b7..e8e1f630ceb6 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -22,9 +22,6 @@ const propTypes = { /** Whether the option has an outstanding IOU */ hasOutstandingIOU: PropTypes.bool, - /** Sorted actions prepared for display */ - sortedReportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)).isRequired, - /** The ID of the most recent IOU report action connected with the shown report */ mostRecentIOUReportActionID: PropTypes.string, @@ -36,6 +33,8 @@ const propTypes = { /** Linked report action ID */ linkedReportActionID: PropTypes.string, + + displayAsGroup: PropTypes.bool.isRequired, }; const defaultProps = { @@ -49,7 +48,7 @@ function ReportActionsListItemRenderer({ index, report, hasOutstandingIOU, - sortedReportActions, + displayAsGroup, mostRecentIOUReportActionID, shouldHideThreadDividerLine, shouldDisplayNewMarker, @@ -73,7 +72,7 @@ function ReportActionsListItemRenderer({ report={report} action={reportAction} linkedReportActionID={linkedReportActionID} - displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedReportActions, index)} + displayAsGroup={displayAsGroup} shouldDisplayNewMarker={shouldDisplayNewMarker} shouldShowSubscriptAvatar={ (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isExpenseReport(report)) && diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index a3671faf194c..db9b6e1c3ecf 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -179,6 +179,14 @@ function ReportActionsView(props) { } }; + const report = useMemo( + () => ({ + lastReadTime: props.report.lastReadTime, + reportID: props.report.reportID, + }), + [props.report.lastReadTime, props.report.reportID], + ); + // Comments have not loaded at all yet do nothing if (!_.size(props.reportActions)) { return null; @@ -187,7 +195,7 @@ function ReportActionsView(props) { return ( <> {}, pendingAction: null, personalDetails: {}, - shouldShowComposeInput: true, shouldDisableCompose: false, listHeight: 0, isReportReadyForDisplay: true, + lastReportAction: null, + isEmptyChat: true, }; function ReportFooter(props) { @@ -72,6 +67,33 @@ function ReportFooter(props) { const isSmallSizeLayout = props.windowWidth - (props.isSmallScreenWidth ? 0 : variables.sideBarWidth) < variables.anonymousReportFooterBreakpoint; const hideComposer = ReportUtils.shouldDisableWriteActions(props.report); + const [shouldShowComposeInput, setShouldShowComposeInput] = useState(false); + + useEffect(() => { + // eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs + const connID = Onyx.connect({ + key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, + callback: (val) => { + if (val === shouldShowComposeInput) { + return; + } + setShouldShowComposeInput(val); + }, + }); + + return () => { + Onyx.disconnect(connID); + }; + }, [shouldShowComposeInput]); + + const onSubmitComment = useCallback( + (text) => { + Report.addComment(props.reportID, text); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + return ( <> {hideComposer && ( @@ -89,14 +111,14 @@ function ReportFooter(props) { )} )} - {!hideComposer && (props.shouldShowComposeInput || !props.isSmallScreenWidth) && ( + {!hideComposer && (shouldShowComposeInput || !props.isSmallScreenWidth) && ( + isEqual(prevProps.report, nextProps.report) && + isEqual(prevProps.reportActions, nextProps.reportActions) && + isEqual(prevProps.personalDetails, nextProps.personalDetails) && + prevProps.pendingAction === nextProps.pendingAction && + prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && + prevProps.listHeight === nextProps.listHeight && + prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay, + ), +); From 75ad6135c5391135cdd39d1f6e2b23a77825e0eb Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 23 Oct 2023 15:43:42 +0500 Subject: [PATCH 0011/1250] perf: reduce re-renders --- .../ComposerWithSuggestions.js | 100 +++++++++++------- .../ReportActionCompose.js | 68 ++++-------- 2 files changed, 83 insertions(+), 85 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index e194d0870885..bb5109073cfd 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -1,9 +1,10 @@ -import React, {useEffect, useCallback, useState, useRef, useMemo, useImperativeHandle} from 'react'; +import React, {useEffect, useCallback, useState, useRef, useMemo, useImperativeHandle, memo} from 'react'; import {View, NativeModules, findNodeHandle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import {useIsFocused, useNavigation} from '@react-navigation/native'; +import {runOnJS, useAnimatedRef} from 'react-native-reanimated'; import styles from '../../../../styles/styles'; import themeColors from '../../../../styles/themes/default'; import Composer from '../../../../components/Composer'; @@ -23,7 +24,6 @@ import * as EmojiUtils from '../../../../libs/EmojiUtils'; import * as User from '../../../../libs/actions/User'; import * as ReportUtils from '../../../../libs/ReportUtils'; import * as SuggestionUtils from '../../../../libs/SuggestionUtils'; -import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; import canFocusInputOnScreenFocus from '../../../../libs/canFocusInputOnScreenFocus'; import SilentCommentUpdater from './SilentCommentUpdater'; import Suggestions from './Suggestions'; @@ -36,6 +36,10 @@ import focusWithDelay from '../../../../libs/focusWithDelay'; import useDebounce from '../../../../hooks/useDebounce'; import updateMultilineInputRange from '../../../../libs/UpdateMultilineInputRange'; import * as InputFocus from '../../../../libs/actions/InputFocus'; +import SendButton from './SendButton'; +import updatePropsPaperWorklet from '../../../../libs/updatePropsPaperWorklet'; +import EmojiPickerButton from '../../../../components/EmojiPicker/EmojiPickerButton'; +import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; const {RNTextInputReset} = NativeModules; @@ -70,14 +74,14 @@ function ComposerWithSuggestions({ // Onyx modal, preferredSkinTone, - parentReportActions, numberOfLines, // HOCs isKeyboardShown, // Props: Report reportID, - report, - reportActions, + includeChronos, + isEmptyChat, + lastReportAction, // Focus onFocus, onBlur, @@ -92,14 +96,13 @@ function ComposerWithSuggestions({ disabled, isFullComposerAvailable, setIsFullComposerAvailable, - setIsCommentEmpty, + isSendDisabled, handleSendMessage, shouldShowComposeInput, measureParentContainer, listHeight, // Refs suggestionsRef, - animatedRef, forwardedRef, isNextModalWillOpenRef, editFocused, @@ -108,19 +111,21 @@ function ComposerWithSuggestions({ const isFocused = useIsFocused(); const navigation = useNavigation(); const emojisPresentBefore = useRef([]); + + const draftComment = getDraftComment(reportID) || ''; + const [isCommentEmpty, setIsCommentEmpty] = useState(() => !draftComment || !!draftComment.match(/^(\s)*$/)); + const animatedRef = useAnimatedRef(); const [value, setValue] = useState(() => { - const draft = getDraftComment(reportID) || ''; - if (draft) { - emojisPresentBefore.current = EmojiUtils.extractEmojis(draft); + if (draftComment) { + emojisPresentBefore.current = EmojiUtils.extractEmojis(draftComment); } - return draft; + return draftComment; }); const commentRef = useRef(value); - const {isSmallScreenWidth} = useWindowDimensions(); + const {isSmallScreenWidth, isMediumScreenWidth} = useWindowDimensions(); const maxComposerLines = isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES; - const isEmptyChat = useMemo(() => _.size(reportActions) === 1, [reportActions]); const shouldAutoFocus = !modal.isVisible && (shouldFocusInputOnScreenFocus || isEmptyChat) && shouldShowComposeInput; const valueRef = useRef(value); @@ -202,6 +207,20 @@ function ComposerWithSuggestions({ [], ); + const sendMessage = useCallback(() => { + 'worklet'; + + if (isCommentEmpty) { + return; + } + + runOnJS(handleSendMessage)(); + const viewTag = animatedRef(); + const viewName = 'RCTMultilineTextInputView'; + const updates = {text: ''}; + updatePropsPaperWorklet(viewTag, viewName, updates); // clears native text input on the UI thread + }, [animatedRef, handleSendMessage, isCommentEmpty]); + /** * Update the value of the comment in Onyx * @@ -225,7 +244,13 @@ function ComposerWithSuggestions({ } } emojisPresentBefore.current = emojis; - setIsCommentEmpty(!!newComment.match(/^(\s)*$/)); + const isNewCommentEmpty = !!newComment.match(/^(\s)*$/); + const isPrevCommentEmpty = !!commentRef.current.match(/^(\s)*$/); + + /** Only update isCommentEmpty state if it's different from previous one */ + if (isNewCommentEmpty !== isPrevCommentEmpty) { + setIsCommentEmpty(isNewCommentEmpty); + } setValue(newComment); if (commentValue !== newComment) { const remainder = ComposerUtils.getCommonSuffixLength(commentValue, newComment); @@ -338,26 +363,20 @@ function ComposerWithSuggestions({ // Submit the form when Enter is pressed if (e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey && !e.shiftKey) { e.preventDefault(); - handleSendMessage(); + sendMessage(); } // Trigger the edit box for last sent message if ArrowUp is pressed and the comment is empty and Chronos is not in the participants const valueLength = valueRef.current.length; - if (e.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && textInputRef.current.selectionStart === 0 && valueLength === 0 && !ReportUtils.chatIncludesChronos(report)) { + if (e.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && textInputRef.current.selectionStart === 0 && valueLength === 0 && !includeChronos) { e.preventDefault(); - const parentReportActionID = lodashGet(report, 'parentReportActionID', ''); - const parentReportAction = lodashGet(parentReportActions, [parentReportActionID], {}); - const lastReportAction = _.find( - [...reportActions, parentReportAction], - (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action), - ); if (lastReportAction) { Report.saveReportActionDraft(reportID, lastReportAction, _.last(lastReportAction.message).html); } } }, - [isKeyboardShown, isSmallScreenWidth, parentReportActions, report, reportActions, reportID, handleSendMessage, suggestionsRef, valueRef], + [isSmallScreenWidth, isKeyboardShown, suggestionsRef, includeChronos, sendMessage, lastReportAction, reportID], ); const onSelectionChange = useCallback( @@ -561,6 +580,19 @@ function ComposerWithSuggestions({ }} onScroll={hideSuggestionMenu} /> + + {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( + replaceSelectionWithText(...args)} + emojiPickerID={reportID} + /> + )} + `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, - canEvict: false, - initWithStoredValues: false, - }, }), )( - React.forwardRef((props, ref) => ( - - )), + memo( + React.forwardRef((props, ref) => ( + + )), + ), ); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index dd4d51653546..8796fd1c1b85 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -1,10 +1,10 @@ -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; -import {runOnJS, useAnimatedRef} from 'react-native-reanimated'; +import {runOnJS} from 'react-native-reanimated'; import {PortalHost} from '@gorhom/portal'; import styles from '../../../../styles/styles'; import ONYXKEYS from '../../../../ONYXKEYS'; @@ -21,23 +21,17 @@ import ParticipantLocalTime from '../ParticipantLocalTime'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../../components/withCurrentUserPersonalDetails'; import {withNetwork} from '../../../../components/OnyxProvider'; import * as User from '../../../../libs/actions/User'; -import EmojiPickerButton from '../../../../components/EmojiPicker/EmojiPickerButton'; -import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; import OfflineIndicator from '../../../../components/OfflineIndicator'; import ExceededCommentLength from '../../../../components/ExceededCommentLength'; import ReportDropUI from '../ReportDropUI'; import reportPropTypes from '../../../reportPropTypes'; import OfflineWithFeedback from '../../../../components/OfflineWithFeedback'; -import SendButton from './SendButton'; import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import ComposerWithSuggestions from './ComposerWithSuggestions'; -import reportActionPropTypes from '../reportActionPropTypes'; import useLocalize from '../../../../hooks/useLocalize'; import getModalState from '../../../../libs/getModalState'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as EmojiPickerActions from '../../../../libs/actions/EmojiPickerAction'; -import getDraftComment from '../../../../libs/ComposerUtils/getDraftComment'; -import updatePropsPaperWorklet from '../../../../libs/updatePropsPaperWorklet'; const propTypes = { /** A method to call when the form is submitted */ @@ -46,9 +40,6 @@ const propTypes = { /** The ID of the report actions will be created for */ reportID: PropTypes.string.isRequired, - /** Array of report actions for this report */ - reportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)), - /** Personal details of all the users */ personalDetails: PropTypes.objectOf(participantPropTypes), @@ -111,14 +102,14 @@ function ReportActionCompose({ personalDetails, report, reportID, - reportActions, + isEmptyChat, + lastReportAction, listHeight, shouldShowComposeInput, isReportReadyForDisplay, }) { const {translate} = useLocalize(); - const {isMediumScreenWidth, isSmallScreenWidth} = useWindowDimensions(); - const animatedRef = useAnimatedRef(); + const {isSmallScreenWidth} = useWindowDimensions(); const actionButtonRef = useRef(null); /** @@ -134,10 +125,6 @@ function ReportActionCompose({ * Updates the should clear state of the composer */ const [textInputShouldClear, setTextInputShouldClear] = useState(false); - const [isCommentEmpty, setIsCommentEmpty] = useState(() => { - const draftComment = getDraftComment(reportID); - return !draftComment || !!draftComment.match(/^(\s)*$/); - }); /** * Updates the visibility state of the menu @@ -164,7 +151,9 @@ function ReportActionCompose({ [personalDetails, report, currentUserPersonalDetails.accountID, isComposerFullSize], ); - const isBlockedFromConcierge = useMemo(() => ReportUtils.chatIncludesConcierge(report) && User.isBlockedFromConcierge(blockedFromConcierge), [report, blockedFromConcierge]); + const includesConcierge = useMemo(() => ReportUtils.chatIncludesConcierge({participantAccountIDs: report.participantAccountIDs}), [report.participantAccountIDs]); + const userBlockedFromConcierge = useMemo(() => User.isBlockedFromConcierge(blockedFromConcierge), [blockedFromConcierge]); + const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); // If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions const conciergePlaceholderRandomIndex = useMemo( @@ -175,8 +164,8 @@ function ReportActionCompose({ // Placeholder to display in the chat input. const inputPlaceholder = useMemo(() => { - if (ReportUtils.chatIncludesConcierge(report)) { - if (User.isBlockedFromConcierge(blockedFromConcierge)) { + if (includesConcierge) { + if (userBlockedFromConcierge) { return translate('reportActionCompose.blockedFromConcierge'); } @@ -184,7 +173,7 @@ function ReportActionCompose({ } return translate('reportActionCompose.writeSomething'); - }, [report, blockedFromConcierge, translate, conciergePlaceholderRandomIndex]); + }, [includesConcierge, translate, userBlockedFromConcierge, conciergePlaceholderRandomIndex]); const focus = () => { if (composerRef === null || composerRef.current === null) { @@ -322,24 +311,16 @@ function ReportActionCompose({ const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); - const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; + const isSendDisabled = isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; const handleSendMessage = useCallback(() => { - 'worklet'; - if (isSendDisabled || !isReportReadyForDisplay) { return; } - const viewTag = animatedRef(); - const viewName = 'RCTMultilineTextInputView'; - const updates = {text: ''}; - // We are setting the isCommentEmpty flag to true so the status of it will be in sync of the native text input state - runOnJS(setIsCommentEmpty)(true); runOnJS(resetFullComposerSize)(); - updatePropsPaperWorklet(viewTag, viewName, updates); // clears native text input on the UI thread runOnJS(submitForm)(); - }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); + }, [isSendDisabled, resetFullComposerSize, submitForm, isReportReadyForDisplay]); return ( { @@ -427,18 +409,6 @@ function ReportActionCompose({ )} - {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( - composerRef.current.replaceSelectionWithText(...args)} - emojiPickerID={report.reportID} - /> - )} - Date: Tue, 24 Oct 2023 17:04:11 +0500 Subject: [PATCH 0012/1250] refactor: make action prop lightweight --- .../report/ReportActionsListItemRenderer.js | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index e8e1f630ceb6..a7ec10a30eea 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, {memo} from 'react'; +import React, {memo, useMemo} from 'react'; import _ from 'underscore'; import CONST from '../../../CONST'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; @@ -59,6 +59,41 @@ function ReportActionsListItemRenderer({ ReportUtils.isChatThread(report) && !ReportActionsUtils.isTransactionThread(ReportActionsUtils.getParentReportAction(report)); + const action = useMemo( + () => ({ + reportActionID: reportAction.reportActionID, + message: reportAction.message, + pendingAction: reportAction.pendingAction, + actionName: reportAction.actionName, + errors: reportAction.errors, + originalMessage: reportAction.originalMessage, + childCommenterCount: reportAction.childCommenterCount, + linkMetadata: reportAction.linkMetadata, + childReportID: reportAction.childReportID, + childLastVisibleActionCreated: reportAction.childLastVisibleActionCreated, + whisperedToAccountIDs: reportAction.whisperedToAccountIDs, + error: reportAction.error, + created: reportAction.created, + actorAccountID: reportAction.actorAccountID, + }), + [ + reportAction.actionName, + reportAction.childCommenterCount, + reportAction.childLastVisibleActionCreated, + reportAction.childReportID, + reportAction.created, + reportAction.error, + reportAction.errors, + reportAction.linkMetadata, + reportAction.message, + reportAction.originalMessage, + reportAction.pendingAction, + reportAction.reportActionID, + reportAction.whisperedToAccountIDs, + reportAction.actorAccountID, + ], + ); + return shouldDisplayParentAction ? ( Date: Wed, 25 Oct 2023 18:25:16 +0500 Subject: [PATCH 0013/1250] perf: add Interaction Manager --- src/libs/actions/OnyxUpdates.ts | 29 +++++++++++++++------------ src/libs/actions/PersistedRequests.ts | 19 ++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 39a20ae9362a..5f13d8133f16 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -1,5 +1,6 @@ import Onyx, {OnyxEntry} from 'react-native-onyx'; import {Merge} from 'type-fest'; +import {InteractionManager} from 'react-native'; import PusherUtils from '../PusherUtils'; import ONYXKEYS from '../../ONYXKEYS'; import * as QueuedOnyxUpdates from './QueuedOnyxUpdates'; @@ -61,19 +62,21 @@ function apply({lastUpdateID, type, request, response, updates}: Merge | undefined { console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); - if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { - console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); - return Promise.resolve(); - } - if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { - Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); - } - if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { - return applyHTTPSOnyxUpdates(request, response); - } - if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { - return applyPusherOnyxUpdates(updates); - } + InteractionManager.runAfterInteractions(() => { + if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { + console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); + return Promise.resolve(); + } + if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { + Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); + } + if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { + return applyHTTPSOnyxUpdates(request, response); + } + if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { + return applyPusherOnyxUpdates(updates); + } + }); } /** diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index d9f4ed020109..1e1a147cecd3 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -1,5 +1,6 @@ import Onyx from 'react-native-onyx'; import isEqual from 'lodash/isEqual'; +import {InteractionManager} from 'react-native'; import ONYXKEYS from '../../ONYXKEYS'; import {Request} from '../../types/onyx'; @@ -33,14 +34,16 @@ function remove(requestToRemove: Request) { * We only remove the first matching request because the order of requests matters. * If we were to remove all matching requests, we can end up with a final state that is different than what the user intended. */ - const requests = [...persistedRequests]; - const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); - if (index === -1) { - return; - } - requests.splice(index, 1); - persistedRequests = requests; - Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); + InteractionManager.runAfterInteractions(() => { + const requests = [...persistedRequests]; + const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); + if (index === -1) { + return; + } + requests.splice(index, 1); + persistedRequests = requests; + Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); + }); } function update(oldRequestIndex: number, newRequest: Request) { From 60d3034b9c91aaeb31626282f89e61e587416cbd Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 25 Oct 2023 18:30:54 +0500 Subject: [PATCH 0014/1250] perf: reference bindings of functions --- .../ComposerWithSuggestions.js | 33 ++++++++++++++----- .../composerWithSuggestionsProps.js | 18 ---------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 4c3136ff6328..97435e510389 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -40,6 +40,7 @@ import SendButton from './SendButton'; import updatePropsPaperWorklet from '../../../../libs/updatePropsPaperWorklet'; import EmojiPickerButton from '../../../../components/EmojiPicker/EmojiPickerButton'; import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; +import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; const {RNTextInputReset} = NativeModules; @@ -540,6 +541,26 @@ function ComposerWithSuggestions({ [blur, focus, prepareCommentAndResetComposer, replaceSelectionWithText], ); + const onLayout = useCallback( + (e) => { + const composerLayoutHeight = e.nativeEvent.layout.height; + if (composerHeight === composerLayoutHeight) { + return; + } + setComposerHeight(composerLayoutHeight); + }, + [composerHeight], + ); + + const onClear = useCallback(() => { + setTextInputShouldClear(false); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onChangeText = useCallback((text) => { + updateComment(text, true); + }, []); + return ( <> @@ -551,7 +572,7 @@ function ComposerWithSuggestions({ textAlignVertical="top" placeholder={inputPlaceholder} placeholderTextColor={themeColors.placeholderText} - onChangeText={(commentValue) => updateComment(commentValue, true)} + onChangeText={onChangeText} onKeyPress={triggerHotkeyActions} style={[styles.textInputCompose, isComposerFullSize ? styles.textInputFullCompose : styles.flex4]} maxLines={maxComposerLines} @@ -560,7 +581,7 @@ function ComposerWithSuggestions({ onClick={setShouldBlockSuggestionCalcToFalse} onPasteFile={displayFileInModal} shouldClear={textInputShouldClear} - onClear={() => setTextInputShouldClear(false)} + onClear={onClear} isDisabled={isBlockedFromConcierge || disabled} isReportActionCompose selection={selection} @@ -572,13 +593,7 @@ function ComposerWithSuggestions({ numberOfLines={numberOfLines} onNumberOfLinesChange={updateNumberOfLines} shouldCalculateCaretPosition - onLayout={(e) => { - const composerLayoutHeight = e.nativeEvent.layout.height; - if (composerHeight === composerLayoutHeight) { - return; - } - setComposerHeight(composerLayoutHeight); - }} + onLayout={onLayout} onScroll={hideSuggestionMenu} /> diff --git a/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js b/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js index 017b5aecb4ae..246399d334d6 100644 --- a/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js +++ b/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js @@ -1,5 +1,4 @@ import PropTypes from 'prop-types'; -import reportActionPropTypes from '../reportActionPropTypes'; import CONST from '../../../../CONST'; const propTypes = { @@ -18,20 +17,9 @@ const propTypes = { /** Whether the keyboard is open or not */ isKeyboardShown: PropTypes.bool.isRequired, - /** The actions from the parent report */ - parentReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), - - /** Array of report actions for this report */ - reportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)), - /** The ID of the report */ reportID: PropTypes.string.isRequired, - /** The report currently being looked at */ - report: PropTypes.shape({ - parentReportID: PropTypes.string, - }).isRequired, - /** Callback when the input is focused */ onFocus: PropTypes.func.isRequired, @@ -68,9 +56,6 @@ const propTypes = { /** Function to set whether the full composer is available or not */ setIsFullComposerAvailable: PropTypes.func.isRequired, - /** Function to set whether the comment is empty or not */ - setIsCommentEmpty: PropTypes.func.isRequired, - /** A method to call when the form is submitted */ handleSendMessage: PropTypes.func.isRequired, @@ -97,9 +82,6 @@ const propTypes = { }), }).isRequired, - /** Ref for the animated view (text input) */ - animatedRef: PropTypes.func.isRequired, - /** Ref for the composer */ forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), From 851c61f1edc0291d022b3480d22778e97a492ce9 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 26 Oct 2023 15:04:22 +0500 Subject: [PATCH 0015/1250] perf: optimise rendering --- .../ComposerWithSuggestions.js | 5 ---- .../ReportActionCompose/SuggestionMention.js | 27 +++++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 97435e510389..c41589fe4b68 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -283,14 +283,9 @@ function ComposerWithSuggestions({ } }, [ - debouncedUpdateFrequentlyUsedEmojis, - preferredLocale, preferredSkinTone, reportID, - setIsCommentEmpty, suggestionsRef, - raiseIsScrollLikelyLayoutTriggered, - debouncedSaveReportComment, ], ); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 67d6ac0632eb..237b1e956966 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -1,7 +1,7 @@ import React, {useState, useCallback, useRef, useImperativeHandle, useEffect} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import {withOnyx} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import CONST from '../../../../CONST'; import useArrowKeyFocusManager from '../../../../hooks/useArrowKeyFocusManager'; import MentionSuggestions from '../../../../components/MentionSuggestions'; @@ -29,9 +29,6 @@ const defaultSuggestionsValues = { }; const propTypes = { - /** Personal details of all users */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - /** A ref to this component */ forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), @@ -39,17 +36,28 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, forwardedRef: null, }; +/** + * We only need the personalDetails once because as long as the + * user is in the ReportScreen, these details won't be changing, + * hence we don't have to use it with `withOnyx`. + */ +let allPersonalDetails = {}; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => { + allPersonalDetails = val; + }, +}); + function SuggestionMention({ value, setValue, selection, setSelection, isComposerFullSize, - personalDetails, updateComment, composerHeight, forwardedRef, @@ -57,6 +65,7 @@ function SuggestionMention({ measureParentContainer, isComposerFocused, }) { + const personalDetails = allPersonalDetails; const {translate} = useLocalize(); const previousValue = usePrevious(value); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); @@ -316,8 +325,4 @@ const SuggestionMentionWithRef = React.forwardRef((props, ref) => ( SuggestionMentionWithRef.displayName = 'SuggestionMentionWithRef'; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, -})(SuggestionMentionWithRef); +export default SuggestionMentionWithRef; From e613f4278dc5cdfb85cf79670f9f6f575c826918 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 26 Oct 2023 16:28:03 +0500 Subject: [PATCH 0016/1250] perf: optimise usage of personal detail list --- src/components/AnonymousReportFooter.js | 5 --- src/components/ArchivedReportFooter.js | 13 ++----- src/components/AvatarWithDisplayName.js | 8 +--- src/components/HeaderWithBackButton/index.js | 2 - .../LHNOptionsList/OptionRowLHNData.js | 39 +------------------ src/components/MoneyReportHeader.js | 6 +-- src/components/MoneyRequestHeader.js | 6 +-- .../withCurrentUserPersonalDetails.tsx | 9 +---- src/libs/OptionsListUtils.js | 5 ++- src/libs/PersonalDetailsUtils.js | 27 +++++++++++-- src/libs/ReportUtils.js | 19 +++++---- src/libs/SidebarUtils.js | 10 ++++- src/pages/home/HeaderView.js | 8 +--- src/pages/home/ReportScreen.js | 15 +------ .../ReportActionCompose.js | 17 ++------ src/pages/home/report/ReportActionItem.js | 8 ++-- .../home/report/ReportActionItemCreated.js | 9 +---- .../home/report/ReportActionItemSingle.js | 5 ++- src/pages/home/report/ReportActionsList.js | 11 +----- src/pages/home/report/ReportFooter.js | 6 --- 20 files changed, 72 insertions(+), 156 deletions(-) diff --git a/src/components/AnonymousReportFooter.js b/src/components/AnonymousReportFooter.js index 43933210dc0b..902a31f12ee3 100644 --- a/src/components/AnonymousReportFooter.js +++ b/src/components/AnonymousReportFooter.js @@ -16,16 +16,12 @@ const propTypes = { isSmallSizeLayout: PropTypes.bool, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - ...withLocalizePropTypes, }; const defaultProps = { report: {}, isSmallSizeLayout: false, - personalDetails: {}, }; function AnonymousReportFooter(props) { @@ -34,7 +30,6 @@ function AnonymousReportFooter(props) { diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index 71d331b68db0..bca5f0b391c8 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -34,9 +34,6 @@ const propTypes = { /** The archived report */ report: reportPropTypes.isRequired, - /** Personal details of all users */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - ...withLocalizePropTypes, }; @@ -46,19 +43,18 @@ const defaultProps = { reason: CONST.REPORT.ARCHIVE_REASON.DEFAULT, }, }, - personalDetails: {}, }; function ArchivedReportFooter(props) { const archiveReason = lodashGet(props.reportClosedAction, 'originalMessage.reason', CONST.REPORT.ARCHIVE_REASON.DEFAULT); - let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [props.report.ownerAccountID, 'displayName']); + let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName']); let oldDisplayName; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) { const newAccountID = props.reportClosedAction.originalMessage.newAccountID; const oldAccountID = props.reportClosedAction.originalMessage.oldAccountID; - displayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [newAccountID, 'displayName']); - oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [oldAccountID, 'displayName']); + displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [newAccountID, 'displayName']); + oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [oldAccountID, 'displayName']); } const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT; @@ -92,9 +88,6 @@ ArchivedReportFooter.displayName = 'ArchivedReportFooter'; export default compose( withLocalize, withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, reportClosedAction: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, canEvict: false, diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 03ae8f51bfb6..3a4ebee0aa42 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -37,9 +37,6 @@ const propTypes = { /** The size of the avatar */ size: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)), - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** Whether if it's an unauthenticated user */ isAnonymous: PropTypes.bool, @@ -50,7 +47,6 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, policy: {}, report: {}, isAnonymous: false, @@ -93,8 +89,8 @@ function AvatarWithDisplayName(props) { const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(props.report); const isMoneyRequestOrReport = ReportUtils.isMoneyRequestReport(props.report) || ReportUtils.isMoneyRequest(props.report); - const icons = ReportUtils.getIcons(props.report, props.personalDetails, props.policy); - const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID], props.personalDetails); + const icons = ReportUtils.getIcons(props.report, null, props.policy); + const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID]); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(_.values(ownerPersonalDetails), false); const shouldShowSubscriptAvatar = ReportUtils.shouldReportShowSubscript(props.report); const isExpenseRequest = ReportUtils.isExpenseRequest(props.report); diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js index 6a02ce02237d..8e5fed15ff7e 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.js @@ -28,7 +28,6 @@ function HeaderWithBackButton({ onThreeDotsButtonPress = () => {}, report = null, policy = {}, - personalDetails = {}, shouldShowAvatarWithDisplay = false, shouldShowBackButton = true, shouldShowBorderBottom = false, @@ -85,7 +84,6 @@ function HeaderWithBackButton({ ) : ( diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index e93e3690138e..2ddf8a687cfd 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -9,11 +9,9 @@ import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; import OptionRowLHN, {propTypes as basePropTypes, defaultProps as baseDefaultProps} from './OptionRowLHN'; import * as Report from '../../libs/actions/Report'; -import * as UserUtils from '../../libs/UserUtils'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; import * as TransactionUtils from '../../libs/TransactionUtils'; -import participantPropTypes from '../participantPropTypes'; import CONST from '../../CONST'; import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes'; @@ -21,9 +19,6 @@ const propTypes = { /** Whether row should be focused */ isFocused: PropTypes.bool, - /** List of users' personal details */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** The preferred language for the app */ preferredLocale: PropTypes.string, @@ -54,7 +49,6 @@ const propTypes = { const defaultProps = { isFocused: false, - personalDetails: {}, fullReport: {}, policy: {}, parentReportActions: {}, @@ -73,7 +67,6 @@ function OptionRowLHNData({ isFocused, fullReport, reportActions, - personalDetails, preferredLocale, comment, policy, @@ -97,7 +90,7 @@ function OptionRowLHNData({ const optionItem = useMemo(() => { // Note: ideally we'd have this as a dependent selector in onyx! - const item = SidebarUtils.getOptionData(fullReport, reportActions, personalDetails, preferredLocale, policy, parentReportAction); + const item = SidebarUtils.getOptionData(fullReport, reportActions, preferredLocale, policy, parentReportAction); if (deepEqual(item, optionItemRef.current)) { return optionItemRef.current; } @@ -106,7 +99,7 @@ function OptionRowLHNData({ // Listen parentReportAction to update title of thread report when parentReportAction changed // Listen to transaction to update title of transaction report when transaction changed // eslint-disable-next-line react-hooks/exhaustive-deps - }, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction]); + }, [fullReport, linkedTransaction, reportActions, preferredLocale, policy, parentReportAction, transaction]); useEffect(() => { if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) { @@ -130,30 +123,6 @@ OptionRowLHNData.propTypes = propTypes; OptionRowLHNData.defaultProps = defaultProps; OptionRowLHNData.displayName = 'OptionRowLHNData'; -/** - * @param {Object} [personalDetails] - * @returns {Object|undefined} - */ -const personalDetailsSelector = (personalDetails) => - _.reduce( - personalDetails, - (finalPersonalDetails, personalData, accountID) => { - // It's OK to do param-reassignment in _.reduce() because we absolutely know the starting state of finalPersonalDetails - // eslint-disable-next-line no-param-reassign - finalPersonalDetails[accountID] = { - accountID: Number(accountID), - login: personalData.login, - displayName: personalData.displayName, - firstName: personalData.firstName, - status: personalData.status, - avatar: UserUtils.getAvatar(personalData.avatar, personalData.accountID), - fallbackIcon: personalData.fallbackIcon, - }; - return finalPersonalDetails; - }, - {}, - ); - /** * This component is rendered in a list. * On scroll we want to avoid that a item re-renders @@ -174,10 +143,6 @@ export default React.memo( key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, canEvict: false, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - selector: personalDetailsSelector, - }, preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, }, diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index ab0b77c21653..211acaf033ce 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -46,9 +46,6 @@ const propTypes = { /** The next step for the report */ nextStep: nextStepPropTypes, - /** Personal details so we can get the ones for the report participants */ - personalDetails: PropTypes.objectOf(participantPropTypes).isRequired, - /** Session info for the currently logged in user. */ session: PropTypes.shape({ /** Currently logged in user email */ @@ -67,7 +64,7 @@ const defaultProps = { policy: {}, }; -function MoneyReportHeader({session, personalDetails, policy, chatReport, nextStep, report: moneyRequestReport, isSmallScreenWidth}) { +function MoneyReportHeader({session, policy, chatReport, nextStep, report: moneyRequestReport, isSmallScreenWidth}) { const {translate} = useLocalize(); const reimbursableTotal = ReportUtils.getMoneyRequestReimbursableTotal(moneyRequestReport); const isApproved = ReportUtils.isReportApproved(moneyRequestReport); @@ -102,7 +99,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldShowPinButton={false} report={moneyRequestReport} policy={policy} - personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)} // Shows border if no buttons or next steps are showing below the header diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 086e1429baef..d9be47ad312f 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -35,9 +35,6 @@ const propTypes = { name: PropTypes.string, }), - /** Personal details so we can get the ones for the report participants */ - personalDetails: PropTypes.objectOf(participantPropTypes).isRequired, - /* Onyx Props */ /** Session info for the currently logged in user. */ session: PropTypes.shape({ @@ -65,7 +62,7 @@ const defaultProps = { policy: {}, }; -function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, policy, personalDetails}) { +function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, policy}) { const {translate} = useLocalize(); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const moneyRequestReport = parentReport; @@ -125,7 +122,6 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, ownerEmail: lodashGet(parentReport, 'ownerEmail', null), }} policy={policy} - personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)} /> diff --git a/src/components/withCurrentUserPersonalDetails.tsx b/src/components/withCurrentUserPersonalDetails.tsx index e1472f280f17..fa81c658bc78 100644 --- a/src/components/withCurrentUserPersonalDetails.tsx +++ b/src/components/withCurrentUserPersonalDetails.tsx @@ -4,13 +4,11 @@ import getComponentDisplayName from '../libs/getComponentDisplayName'; import ONYXKEYS from '../ONYXKEYS'; import personalDetailsPropType from '../pages/personalDetailsPropType'; import type {PersonalDetails, Session} from '../types/onyx'; +import { getPersonalDetailsByAccountID } from '../libs/PersonalDetailsUtils'; type CurrentUserPersonalDetails = PersonalDetails | Record; type OnyxProps = { - /** Personal details of all the users, including current user */ - personalDetails: OnyxEntry>; - /** Session of the current user */ session: OnyxEntry; }; @@ -35,7 +33,7 @@ export default function ( ): ComponentType & RefAttributes, keyof OnyxProps>> { function WithCurrentUserPersonalDetails(props: Omit, ref: ForwardedRef) { const accountID = props.session?.accountID ?? 0; - const accountPersonalDetails = props.personalDetails?.[accountID]; + const accountPersonalDetails: PersonalDetails = getPersonalDetailsByAccountID(accountID); const currentUserPersonalDetails: CurrentUserPersonalDetails = useMemo( () => (accountPersonalDetails ? {...accountPersonalDetails, accountID} : {}), [accountPersonalDetails, accountID], @@ -55,9 +53,6 @@ export default function ( const withCurrentUserPersonalDetails = React.forwardRef(WithCurrentUserPersonalDetails); return withOnyx & RefAttributes, OnyxProps>({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 79c480711c4d..4948c4e8ef90 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -152,11 +152,12 @@ function getAvatarsForAccountIDs(accountIDs, personalDetails, defaultValues = {} * Returns the personal details for an array of accountIDs * * @param {Array} accountIDs - * @param {Object} personalDetails + * @param {Object} passedPersonalDetails * @returns {Object} – keys of the object are emails, values are PersonalDetails objects. */ -function getPersonalDetailsForAccountIDs(accountIDs, personalDetails) { +function getPersonalDetailsForAccountIDs(accountIDs, passedPersonalDetails) { const personalDetailsForAccountIDs = {}; + const personalDetails = passedPersonalDetails || allPersonalDetails; if (!personalDetails) { return personalDetailsForAccountIDs; } diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 29c49427bc81..78066c6b1d9d 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -23,8 +23,7 @@ Onyx.connect({ * @returns {String} */ function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defaultValue) { - const displayName = lodashGet(passedPersonalDetails, pathToDisplayName); - + const displayName = lodashGet(passedPersonalDetails || allPersonalDetails, pathToDisplayName); return displayName || defaultValue || Localize.translateLocal('common.hidden'); } @@ -177,4 +176,26 @@ function getFormattedAddress(privatePersonalDetails) { return formattedAddress.trim().replace(/,$/, ''); } -export {getDisplayNameOrDefault, getPersonalDetailsByIDs, getAccountIDsByLogins, getLoginsByAccountIDs, getNewPersonalDetailsOnyxData, getFormattedAddress}; +function getPersonalDetailsByAccountID(accountID) { + return allPersonalDetails[accountID]; +} + +function getWhisperedToPersonalDetails(whisperedToAccountIDs) { + return _.filter(allPersonalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)); +} + +function isPersonalDetailsEmpty() { + return !personalDetails.length; +} + +export { + getDisplayNameOrDefault, + getPersonalDetailsByIDs, + getAccountIDsByLogins, + getLoginsByAccountIDs, + getNewPersonalDetailsOnyxData, + getFormattedAddress, + getPersonalDetailsByAccountID, + getWhisperedToPersonalDetails, + isPersonalDetailsEmpty, +}; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 79c1c500b837..74d744efb837 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -913,15 +913,13 @@ function getReportRecipientAccountIDs(report, currentLoginAccountID) { /** * Whether the time row should be shown for a report. - * @param {Array} personalDetails * @param {Object} report - * @param {Number} accountID * @return {Boolean} */ -function canShowReportRecipientLocalTime(personalDetails, report, accountID) { - const reportRecipientAccountIDs = getReportRecipientAccountIDs(report, accountID); +function canShowReportRecipientLocalTime(report) { + const reportRecipientAccountIDs = getReportRecipientAccountIDs(report, currentUserAccountID); const hasMultipleParticipants = reportRecipientAccountIDs.length > 1; - const reportRecipient = personalDetails[reportRecipientAccountIDs[0]]; + const reportRecipient = allPersonalDetails[reportRecipientAccountIDs[0]]; const reportRecipientTimezone = lodashGet(reportRecipient, 'timezone', CONST.DEFAULT_TIME_ZONE); const isReportParticipantValidated = lodashGet(reportRecipient, 'validated', false); return Boolean( @@ -977,13 +975,13 @@ function getWorkspaceAvatar(report) { * The Avatar sources can be URLs or Icon components according to the chat type. * * @param {Array} participants - * @param {Object} personalDetails + * @param {Object} passedPersonalDetails * @returns {Array<*>} */ -function getIconsForParticipants(participants, personalDetails) { +function getIconsForParticipants(participants, passedPersonalDetails) { const participantDetails = []; const participantsList = participants || []; - + const personalDetails = passedPersonalDetails || allPersonalDetails; for (let i = 0; i < participantsList.length; i++) { const accountID = participantsList[i]; const avatarSource = UserUtils.getAvatar(lodashGet(personalDetails, [accountID, 'avatar'], ''), accountID); @@ -1046,14 +1044,15 @@ function getWorkspaceIcon(report, policy = undefined) { * The Avatar sources can be URLs or Icon components according to the chat type. * * @param {Object} report - * @param {Object} personalDetails + * @param {Object} passedPersonalDetails * @param {*} [defaultIcon] * @param {String} [defaultName] * @param {Number} [defaultAccountID] * @param {Object} [policy] * @returns {Array<*>} */ -function getIcons(report, personalDetails, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined) { +function getIcons(report, passedPersonalDetails, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined) { + const personalDetails = passedPersonalDetails || allPersonalDetails; if (_.isEmpty(report)) { const fallbackIcon = { source: defaultIcon || Expensicons.FallbackAvatar, diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index 6b9e6d10fd16..5d7dd5bc1ed7 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -55,6 +55,12 @@ Onyx.connect({ }, }); +let allPersonalDetails; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => (allPersonalDetails = val), +}); + let resolveSidebarIsReadyPromise; let sidebarIsReadyPromise = new Promise((resolve) => { @@ -218,16 +224,16 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p * * @param {Object} report * @param {Object} reportActions - * @param {Object} personalDetails * @param {String} preferredLocale * @param {Object} [policy] * @param {Object} parentReportAction * @returns {Object} */ -function getOptionData(report, reportActions, personalDetails, preferredLocale, policy, parentReportAction) { +function getOptionData(report, reportActions, preferredLocale, policy, parentReportAction) { // When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for // this method to be called after the Onyx data has been cleared out. In that case, it's fine to do // a null check here and return early. + const personalDetails = allPersonalDetails; if (!report || !personalDetails) { return; } diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index e88f6cd0b756..d36937b0f420 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -43,9 +43,6 @@ const propTypes = { /** The report currently being looked at */ report: reportPropTypes, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** Onyx Props */ parentReport: reportPropTypes, @@ -62,7 +59,6 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, report: null, guideCalendarLink: null, parentReport: {}, @@ -73,7 +69,7 @@ const defaultProps = { function HeaderView(props) { const participants = lodashGet(props.report, 'participantAccountIDs', []); - const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails); + const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants); const isMultipleParticipant = participants.length > 1; const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant); const isChatThread = ReportUtils.isChatThread(props.report); @@ -167,7 +163,7 @@ function HeaderView(props) { const shouldShowSubscript = ReportUtils.shouldReportShowSubscript(props.report); const defaultSubscriptSize = ReportUtils.isExpenseRequest(props.report) ? CONST.AVATAR_SIZE.SMALL_NORMAL : CONST.AVATAR_SIZE.DEFAULT; - const icons = ReportUtils.getIcons(reportHeaderData, props.personalDetails); + const icons = ReportUtils.getIcons(reportHeaderData); const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; const shouldShowBorderBottom = !isTaskReport || !props.isSmallScreenWidth; const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(props.report); diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 64641ab473fb..d6caff573d25 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -40,6 +40,7 @@ import usePrevious from '../../hooks/usePrevious'; import CONST from '../../CONST'; import withCurrentReportID, {withCurrentReportIDPropTypes, withCurrentReportIDDefaultProps} from '../../components/withCurrentReportID'; import reportWithoutHasDraftSelector from '../../libs/OnyxSelectors/reportWithoutHasDraftSelector'; +import { isPersonalDetailsEmpty } from '../../libs/PersonalDetailsUtils'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -86,9 +87,6 @@ const propTypes = { /** The account manager report ID */ accountManagerReportID: PropTypes.string, - /** All of the personal details for everyone */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - /** Onyx function that marks the component ready for hydration */ markReadyForHydration: PropTypes.func, @@ -115,7 +113,6 @@ const defaultProps = { policies: {}, accountManagerReportID: null, userLeavingStatus: false, - personalDetails: {}, markReadyForHydration: null, ...withCurrentReportIDDefaultProps, }; @@ -140,7 +137,6 @@ function ReportScreen({ reportMetadata, reportActions, accountManagerReportID, - personalDetails, markReadyForHydration, policies, isSidebarLoaded, @@ -173,7 +169,7 @@ function ReportScreen({ const shouldHideReport = !ReportUtils.canAccessReport(report, policies, betas); - const isLoading = !reportID || !isSidebarLoaded || _.isEmpty(personalDetails); + const isLoading = !reportID || !isSidebarLoaded || isPersonalDetailsEmpty(); const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( @@ -191,7 +187,6 @@ function ReportScreen({ Navigation.goBack(ROUTES.HOME, false, true)} - personalDetails={personalDetails} report={report} /> ); @@ -201,7 +196,6 @@ function ReportScreen({ @@ -213,7 +207,6 @@ function ReportScreen({ @@ -445,7 +438,6 @@ function ReportScreen({ isComposerFullSize={isComposerFullSize} policies={policies} listHeight={listHeight} - personalDetails={personalDetails} isEmptyChat={isEmptyChat} lastReportAction={lastReportAction} /> @@ -506,9 +498,6 @@ export default compose( key: ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, initialValue: null, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, userLeavingStatus: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, initialValue: false, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 2721329919c7..dc515c2495ff 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -16,7 +16,6 @@ import willBlurTextInputOnTapOutsideFunc from '../../../../libs/willBlurTextInpu import canFocusInputOnScreenFocus from '../../../../libs/canFocusInputOnScreenFocus'; import CONST from '../../../../CONST'; import * as ReportUtils from '../../../../libs/ReportUtils'; -import participantPropTypes from '../../../../components/participantPropTypes'; import ParticipantLocalTime from '../ParticipantLocalTime'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../../components/withCurrentUserPersonalDetails'; import {withNetwork} from '../../../../components/OnyxProvider'; @@ -33,6 +32,7 @@ import getModalState from '../../../../libs/getModalState'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as EmojiPickerActions from '../../../../libs/actions/EmojiPickerAction'; import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; +import {getPersonalDetailsByAccountID} from '../../../../libs/PersonalDetailsUtils'; const propTypes = { /** A method to call when the form is submitted */ @@ -41,9 +41,6 @@ const propTypes = { /** The ID of the report actions will be created for */ reportID: PropTypes.string.isRequired, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** The report currently being looked at */ report: reportPropTypes, @@ -76,7 +73,6 @@ const propTypes = { const defaultProps = { report: {}, blockedFromConcierge: {}, - personalDetails: {}, preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, isComposerFullSize: false, pendingAction: null, @@ -100,7 +96,6 @@ function ReportActionCompose({ network, onSubmit, pendingAction, - personalDetails, report, reportID, isEmptyChat, @@ -147,10 +142,7 @@ function ReportActionCompose({ [currentUserPersonalDetails.accountID, report], ); - const shouldShowReportRecipientLocalTime = useMemo( - () => ReportUtils.canShowReportRecipientLocalTime(personalDetails, report, currentUserPersonalDetails.accountID) && !isComposerFullSize, - [personalDetails, report, currentUserPersonalDetails.accountID, isComposerFullSize], - ); + const shouldShowReportRecipientLocalTime = useMemo(() => ReportUtils.canShowReportRecipientLocalTime(report) && !isComposerFullSize, [report, isComposerFullSize]); const includesConcierge = useMemo(() => ReportUtils.chatIncludesConcierge({participantAccountIDs: report.participantAccountIDs}), [report.participantAccountIDs]); const userBlockedFromConcierge = useMemo(() => User.isBlockedFromConcierge(blockedFromConcierge), [blockedFromConcierge]); @@ -307,7 +299,7 @@ function ReportActionCompose({ ); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); - const reportRecipient = personalDetails[reportRecipientAcountIDs[0]]; + const reportRecipient = getPersonalDetailsByAccountID(reportRecipientAcountIDs[0]); const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); @@ -443,9 +435,6 @@ export default compose( blockedFromConcierge: { key: ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, shouldShowComposeInput: { key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, }, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 3afdb437a49a..2697d4b5551d 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -133,7 +133,6 @@ const defaultProps = { }; function ReportActionItem(props) { - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const [isContextMenuActive, setIsContextMenuActive] = useState(() => ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); const [isHidden, setIsHidden] = useState(false); const [moderationDecision, setModerationDecision] = useState(CONST.MODERATION.MODERATOR_DECISION_APPROVED); @@ -303,6 +302,7 @@ function ReportActionItem(props) { */ const renderItemContent = (hovered = false, isWhisper = false, hasErrors = false) => { let children; + const _submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); // Show the MoneyRequestPreview for when request was created, bill was split or money was sent if ( @@ -360,7 +360,7 @@ function ReportActionItem(props) { /> ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); @@ -506,7 +506,7 @@ function ReportActionItem(props) { numberOfReplies={numberOfThreadReplies} mostRecentReply={`${props.action.childLastVisibleActionCreated}`} isHovered={hovered} - icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, personalDetails)} + icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs)} onSecondaryInteraction={showPopover} /> @@ -638,7 +638,7 @@ function ReportActionItem(props) { const isWhisper = whisperedToAccountIDs.length > 0; const isMultipleParticipant = whisperedToAccountIDs.length > 1; const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedToAccountIDs); - const whisperedToPersonalDetails = isWhisper ? _.filter(personalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)) : []; + const whisperedToPersonalDetails = isWhisper ? PersonalDetailsUtils.getWhisperedToPersonalDetails(whisperedToAccountIDs) : []; const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; return ( `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, selector: reportWithoutHasDraftSelector, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index fc189a3aef36..faa9b4c6fbbc 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -31,6 +31,7 @@ import ONYXKEYS from '../../../ONYXKEYS'; import Text from '../../../components/Text'; import Tooltip from '../../../components/Tooltip'; import DateUtils from '../../../libs/DateUtils'; +import { getPersonalDetailsByAccountID } from '../../../libs/PersonalDetailsUtils'; const propTypes = { /** All the data of the action */ @@ -83,8 +84,8 @@ const showWorkspaceDetails = (reportID) => { }; function ReportActionItemSingle(props) { - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const actorAccountID = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport ? props.iouReport.managerID : props.action.actorAccountID; + const personalDetails = getPersonalDetailsByAccountID(actorAccountID); let {displayName} = personalDetails[actorAccountID] || {}; const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID] || {}; let actorHint = (login || displayName || '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); @@ -122,7 +123,7 @@ function ReportActionItemSingle(props) { id: secondaryAccountId, }; } else if (!isWorkspaceActor) { - secondaryAvatar = ReportUtils.getIcons(props.report, {})[props.report.isOwnPolicyExpenseChat ? 0 : 1]; + secondaryAvatar = ReportUtils.getIcons(props.report)[props.report.isOwnPolicyExpenseChat ? 0 : 1]; } const icon = {source: avatarSource, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: primaryDisplayName, id: isWorkspaceActor ? '' : actorAccountID}; diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 531a11ce7252..6515dc652bdc 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -6,8 +6,6 @@ import {useRoute} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import CONST from '../../../CONST'; import InvertedFlatList from '../../../components/InvertedFlatList'; -import {withPersonalDetails} from '../../../components/OnyxProvider'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../../../components/withCurrentUserPersonalDetails'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import useNetwork from '../../../hooks/useNetwork'; import useLocalize from '../../../hooks/useLocalize'; @@ -66,17 +64,14 @@ const propTypes = { }), ...windowDimensionsPropTypes, - ...withCurrentUserPersonalDetailsPropTypes, }; const defaultProps = { - personalDetails: {}, onScroll: () => {}, mostRecentIOUReportActionID: '', isLoadingInitialReportActions: false, isLoadingOlderReportActions: false, isLoadingNewerReportActions: false, - ...withCurrentUserPersonalDetailsDefaultProps, }; const VERTICAL_OFFSET_THRESHOLD = 200; @@ -122,8 +117,6 @@ function ReportActionsList({ onScroll, mostRecentIOUReportActionID, isSmallScreenWidth, - personalDetailsList, - currentUserPersonalDetails, hasOutstandingIOU, loadNewerChats, loadOlderChats, @@ -350,7 +343,7 @@ function ReportActionsList({ // To notify there something changes we can use extraData prop to flatlist const extraData = [isSmallScreenWidth ? currentUnreadMarker : undefined, ReportUtils.isArchivedRoom(report)]; const hideComposer = ReportUtils.shouldDisableWriteActions(report); - const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(personalDetailsList, report, currentUserPersonalDetails.accountID) && !isComposerFullSize; + const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(report) && !isComposerFullSize; const contentContainerStyle = useMemo( () => [styles.chatContentScrollView, isLoadingNewerReportActions ? styles.chatContentScrollViewWithHeaderLoader : {}], @@ -435,4 +428,4 @@ ReportActionsList.propTypes = propTypes; ReportActionsList.defaultProps = defaultProps; ReportActionsList.displayName = 'ReportActionsList'; -export default compose(withWindowDimensions, withPersonalDetails(), withCurrentUserPersonalDetails)(ReportActionsList); +export default compose(withWindowDimensions)(ReportActionsList); diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 5af46b14142d..710eed2b86df 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -33,9 +33,6 @@ const propTypes = { /** The pending action when we are adding a chat */ pendingAction: PropTypes.string, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** Whether user interactions should be disabled */ shouldDisableCompose: PropTypes.bool, @@ -51,7 +48,6 @@ const propTypes = { const defaultProps = { report: {reportID: '0'}, pendingAction: null, - personalDetails: {}, shouldDisableCompose: false, listHeight: 0, isReportReadyForDisplay: true, @@ -111,7 +107,6 @@ function ReportFooter(props) { )} {isArchivedRoom && } @@ -151,7 +146,6 @@ export default withWindowDimensions( (prevProps, nextProps) => isEqual(prevProps.report, nextProps.report) && isEqual(prevProps.reportActions, nextProps.reportActions) && - isEqual(prevProps.personalDetails, nextProps.personalDetails) && prevProps.pendingAction === nextProps.pendingAction && prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && prevProps.listHeight === nextProps.listHeight && From 47c4717c3ee8553a96b1bb8540df9d5779097f5f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 26 Oct 2023 16:54:08 +0500 Subject: [PATCH 0017/1250] fix: linting --- src/components/AnonymousReportFooter.js | 1 - src/components/ArchivedReportFooter.js | 1 - src/components/AvatarWithDisplayName.js | 1 - src/components/MoneyReportHeader.js | 1 - src/components/MoneyRequestHeader.js | 1 - src/pages/home/HeaderView.js | 1 - src/pages/home/ReportScreen.js | 5 ++--- .../ReportActionCompose/ReportActionCompose.js | 4 ++-- .../ReportActionCompose/SuggestionMention.js | 2 +- src/pages/home/report/ReportActionItem.js | 3 +-- src/pages/home/report/ReportActionItemCreated.js | 1 - src/pages/home/report/ReportActionItemSingle.js | 15 +++++++-------- src/pages/home/report/ReportActionsList.js | 4 ++-- src/pages/home/report/ReportFooter.js | 1 - 14 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/components/AnonymousReportFooter.js b/src/components/AnonymousReportFooter.js index 902a31f12ee3..51b9752294e9 100644 --- a/src/components/AnonymousReportFooter.js +++ b/src/components/AnonymousReportFooter.js @@ -8,7 +8,6 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize'; import reportPropTypes from '../pages/reportPropTypes'; import styles from '../styles/styles'; import * as Session from '../libs/actions/Session'; -import participantPropTypes from './participantPropTypes'; const propTypes = { /** The report currently being looked at */ diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index bca5f0b391c8..41cf0f65487e 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -7,7 +7,6 @@ import CONST from '../CONST'; import Banner from './Banner'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import compose from '../libs/compose'; -import personalDetailsPropType from '../pages/personalDetailsPropType'; import ONYXKEYS from '../ONYXKEYS'; import * as ReportUtils from '../libs/ReportUtils'; import reportPropTypes from '../pages/reportPropTypes'; diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 3a4ebee0aa42..8623f6bba4bc 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -5,7 +5,6 @@ import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; import CONST from '../CONST'; import reportPropTypes from '../pages/reportPropTypes'; -import participantPropTypes from './participantPropTypes'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import styles from '../styles/styles'; diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index 211acaf033ce..55a31c0a9bdc 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -8,7 +8,6 @@ import useLocalize from '../hooks/useLocalize'; import HeaderWithBackButton from './HeaderWithBackButton'; import iouReportPropTypes from '../pages/iouReportPropTypes'; import * as ReportUtils from '../libs/ReportUtils'; -import participantPropTypes from './participantPropTypes'; import styles from '../styles/styles'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import compose from '../libs/compose'; diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index d9be47ad312f..c106e1aa9bfd 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -8,7 +8,6 @@ import iouReportPropTypes from '../pages/iouReportPropTypes'; import * as ReportUtils from '../libs/ReportUtils'; import compose from '../libs/compose'; import * as Expensicons from './Icon/Expensicons'; -import participantPropTypes from './participantPropTypes'; import styles from '../styles/styles'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index d36937b0f420..3f9776ca4946 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -19,7 +19,6 @@ import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; import Text from '../../components/Text'; import ThreeDotsMenu from '../../components/ThreeDotsMenu'; import Tooltip from '../../components/Tooltip'; -import participantPropTypes from '../../components/participantPropTypes'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; import * as OptionsListUtils from '../../libs/OptionsListUtils'; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index d6caff573d25..3340c4ca5ee3 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -28,7 +28,6 @@ import reportMetadataPropTypes from '../reportMetadataPropTypes'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import withViewportOffsetTop from '../../components/withViewportOffsetTop'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; -import personalDetailsPropType from '../personalDetailsPropType'; import getIsReportFullyVisible from '../../libs/getIsReportFullyVisible'; import MoneyRequestHeader from '../../components/MoneyRequestHeader'; import MoneyReportHeader from '../../components/MoneyReportHeader'; @@ -40,7 +39,7 @@ import usePrevious from '../../hooks/usePrevious'; import CONST from '../../CONST'; import withCurrentReportID, {withCurrentReportIDPropTypes, withCurrentReportIDDefaultProps} from '../../components/withCurrentReportID'; import reportWithoutHasDraftSelector from '../../libs/OnyxSelectors/reportWithoutHasDraftSelector'; -import { isPersonalDetailsEmpty } from '../../libs/PersonalDetailsUtils'; +import * as PersonalDetailsUtils from '../../libs/PersonalDetailsUtils'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -169,7 +168,7 @@ function ReportScreen({ const shouldHideReport = !ReportUtils.canAccessReport(report, policies, betas); - const isLoading = !reportID || !isSidebarLoaded || isPersonalDetailsEmpty(); + const isLoading = !reportID || !isSidebarLoaded || PersonalDetailsUtils.isPersonalDetailsEmpty(); const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index dc515c2495ff..fa7dfc8418fa 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -32,7 +32,7 @@ import getModalState from '../../../../libs/getModalState'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as EmojiPickerActions from '../../../../libs/actions/EmojiPickerAction'; import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; -import {getPersonalDetailsByAccountID} from '../../../../libs/PersonalDetailsUtils'; +import * as PersonalDetailsUtils from '../../../../libs/PersonalDetailsUtils'; const propTypes = { /** A method to call when the form is submitted */ @@ -299,7 +299,7 @@ function ReportActionCompose({ ); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); - const reportRecipient = getPersonalDetailsByAccountID(reportRecipientAcountIDs[0]); + const reportRecipient = PersonalDetailsUtils.getPersonalDetailsByAccountID(reportRecipientAcountIDs[0]); const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 237b1e956966..0172bfe20038 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -11,7 +11,6 @@ import * as SuggestionsUtils from '../../../../libs/SuggestionUtils'; import useLocalize from '../../../../hooks/useLocalize'; import usePrevious from '../../../../hooks/usePrevious'; import ONYXKEYS from '../../../../ONYXKEYS'; -import personalDetailsPropType from '../../../personalDetailsPropType'; import * as SuggestionProps from './suggestionProps'; /** @@ -45,6 +44,7 @@ const defaultProps = { * hence we don't have to use it with `withOnyx`. */ let allPersonalDetails = {}; +// eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 2697d4b5551d..7c955b0a88e3 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -28,7 +28,7 @@ import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMe import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu'; import * as ContextMenuActions from './ContextMenu/ContextMenuActions'; import * as EmojiPickerAction from '../../../libs/actions/EmojiPickerAction'; -import {usePersonalDetails, withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '../../../components/OnyxProvider'; +import {withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '../../../components/OnyxProvider'; import RenameAction from '../../../components/ReportActionItem/RenameAction'; import InlineSystemMessage from '../../../components/InlineSystemMessage'; import styles from '../../../styles/styles'; @@ -302,7 +302,6 @@ function ReportActionItem(props) { */ const renderItemContent = (hovered = false, isWhisper = false, hasErrors = false) => { let children; - const _submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); // Show the MoneyRequestPreview for when request was created, bill was split or money was sent if ( diff --git a/src/pages/home/report/ReportActionItemCreated.js b/src/pages/home/report/ReportActionItemCreated.js index 9b428bf90ea6..2e7bce92f053 100644 --- a/src/pages/home/report/ReportActionItemCreated.js +++ b/src/pages/home/report/ReportActionItemCreated.js @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import ONYXKEYS from '../../../ONYXKEYS'; import ReportWelcomeText from '../../../components/ReportWelcomeText'; -import participantPropTypes from '../../../components/participantPropTypes'; import * as ReportUtils from '../../../libs/ReportUtils'; import styles from '../../../styles/styles'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index faa9b4c6fbbc..5450b457e53f 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -13,7 +13,6 @@ import compose from '../../../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; -import {usePersonalDetails} from '../../../components/OnyxProvider'; import ControlSelection from '../../../libs/ControlSelection'; import * as ReportUtils from '../../../libs/ReportUtils'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; @@ -31,7 +30,7 @@ import ONYXKEYS from '../../../ONYXKEYS'; import Text from '../../../components/Text'; import Tooltip from '../../../components/Tooltip'; import DateUtils from '../../../libs/DateUtils'; -import { getPersonalDetailsByAccountID } from '../../../libs/PersonalDetailsUtils'; +import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils'; const propTypes = { /** All the data of the action */ @@ -85,22 +84,22 @@ const showWorkspaceDetails = (reportID) => { function ReportActionItemSingle(props) { const actorAccountID = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport ? props.iouReport.managerID : props.action.actorAccountID; - const personalDetails = getPersonalDetailsByAccountID(actorAccountID); - let {displayName} = personalDetails[actorAccountID] || {}; - const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID] || {}; + const personalDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(actorAccountID); + let {displayName} = personalDetails || {}; + const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails || {}; let actorHint = (login || displayName || '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport, [props.action.actionName, props.iouReport]); const isWorkspaceActor = ReportUtils.isPolicyExpenseChat(props.report) && (!actorAccountID || displayAllActors); let avatarSource = UserUtils.getAvatar(avatar, actorAccountID); + const delegateDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(props.action.delegateAccountID); if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(props.report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(props.report); - } else if (props.action.delegateAccountID && personalDetails[props.action.delegateAccountID]) { + } else if (props.action.delegateAccountID && delegateDetails) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. - const delegateDetails = personalDetails[props.action.delegateAccountID]; const delegateDisplayName = delegateDetails.displayName; actorHint = `${delegateDisplayName} (${props.translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; @@ -113,7 +112,7 @@ function ReportActionItemSingle(props) { if (displayAllActors) { // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = props.iouReport.ownerAccountID === actorAccountID ? props.iouReport.managerID : props.iouReport.ownerAccountID; - const secondaryUserDetails = personalDetails[secondaryAccountId] || {}; + const secondaryUserDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(secondaryAccountId); const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 6515dc652bdc..8127e40a3aa2 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -13,7 +13,6 @@ import useReportScrollManager from '../../../hooks/useReportScrollManager'; import DateUtils from '../../../libs/DateUtils'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as Report from '../../../libs/actions/Report'; -import compose from '../../../libs/compose'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; import reportPropTypes from '../../reportPropTypes'; @@ -72,6 +71,7 @@ const defaultProps = { isLoadingInitialReportActions: false, isLoadingOlderReportActions: false, isLoadingNewerReportActions: false, + policy: {} }; const VERTICAL_OFFSET_THRESHOLD = 200; @@ -428,4 +428,4 @@ ReportActionsList.propTypes = propTypes; ReportActionsList.defaultProps = defaultProps; ReportActionsList.displayName = 'ReportActionsList'; -export default compose(withWindowDimensions)(ReportActionsList); +export default withWindowDimensions(ReportActionsList); diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 710eed2b86df..760f67dbc310 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -18,7 +18,6 @@ import reportActionPropTypes from './reportActionPropTypes'; import reportPropTypes from '../../reportPropTypes'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as Session from '../../../libs/actions/Session'; -import participantPropTypes from '../../../components/participantPropTypes'; import * as Report from '../../../libs/actions/Report'; import useReportScrollManager from '../../../hooks/useReportScrollManager'; From 9562e8ca37be9b0dd961d9e6f7ea84df753f277a Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 14:44:54 +0500 Subject: [PATCH 0018/1250] refactor: remove not needed code --- src/libs/PersonalDetailsUtils.js | 16 +++++++++- src/libs/actions/OnyxUpdates.ts | 29 +++++++++---------- src/libs/actions/PersistedRequests.ts | 19 +++++------- src/pages/home/ReportScreen.js | 3 +- .../report/ReportActionsListItemRenderer.js | 4 +++ src/pages/home/report/ReportActionsView.js | 7 ++++- src/pages/home/report/ReportFooter.js | 5 ++-- 7 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index c2841b139422..acbdeca80a9d 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -176,14 +176,28 @@ function getFormattedAddress(privatePersonalDetails) { return formattedAddress.trim().replace(/,$/, ''); } +/** + * Get personal detail for an accountID + * @param {String} accountID + * @returns {PersonalDetail} personal detail object + */ function getPersonalDetailsByAccountID(accountID) { - return allPersonalDetails[accountID]; + return allPersonalDetails ? allPersonalDetails[accountID] : {}; } +/** + * Get whispered personal details for array of accountIDs + * @param {Array} whisperedToAccountIDs + * @returns {PersonalDetails} personal details + */ function getWhisperedToPersonalDetails(whisperedToAccountIDs) { return _.filter(allPersonalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)); } +/** + * Whether personal details is empty + * @returns {Boolean} true if personal details is empty + */ function isPersonalDetailsEmpty() { return !personalDetails.length; } diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 5f13d8133f16..39a20ae9362a 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -1,6 +1,5 @@ import Onyx, {OnyxEntry} from 'react-native-onyx'; import {Merge} from 'type-fest'; -import {InteractionManager} from 'react-native'; import PusherUtils from '../PusherUtils'; import ONYXKEYS from '../../ONYXKEYS'; import * as QueuedOnyxUpdates from './QueuedOnyxUpdates'; @@ -62,21 +61,19 @@ function apply({lastUpdateID, type, request, response, updates}: Merge | undefined { console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); - InteractionManager.runAfterInteractions(() => { - if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { - console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); - return Promise.resolve(); - } - if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { - Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); - } - if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { - return applyHTTPSOnyxUpdates(request, response); - } - if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { - return applyPusherOnyxUpdates(updates); - } - }); + if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { + console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); + return Promise.resolve(); + } + if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { + Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); + } + if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { + return applyHTTPSOnyxUpdates(request, response); + } + if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { + return applyPusherOnyxUpdates(updates); + } } /** diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index 1e1a147cecd3..d9f4ed020109 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -1,6 +1,5 @@ import Onyx from 'react-native-onyx'; import isEqual from 'lodash/isEqual'; -import {InteractionManager} from 'react-native'; import ONYXKEYS from '../../ONYXKEYS'; import {Request} from '../../types/onyx'; @@ -34,16 +33,14 @@ function remove(requestToRemove: Request) { * We only remove the first matching request because the order of requests matters. * If we were to remove all matching requests, we can end up with a final state that is different than what the user intended. */ - InteractionManager.runAfterInteractions(() => { - const requests = [...persistedRequests]; - const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); - if (index === -1) { - return; - } - requests.splice(index, 1); - persistedRequests = requests; - Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); - }); + const requests = [...persistedRequests]; + const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); + if (index === -1) { + return; + } + requests.splice(index, 1); + persistedRequests = requests; + Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); } function update(oldRequestIndex: number, newRequest: Request) { diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 76be144150c3..ed1fd91b9834 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -172,7 +172,7 @@ function ReportScreen({ const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( - () => _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)), + () => reportActions.length ? _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)) : {}, [reportActions, parentReportAction], ); const isSingleTransactionView = ReportUtils.isMoneyRequest(report); @@ -437,7 +437,6 @@ function ReportScreen({ ({ reportActionID: reportAction.reportActionID, diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 415b8b6fbd1b..f1bd30f1a8b9 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -216,12 +216,17 @@ function ReportActionsView(props) { } }; + /** + * Create a lightweight Report so as to keep the re-rendering as light as possible by + * passing in only the required props. + */ const report = useMemo( () => ({ lastReadTime: props.report.lastReadTime, reportID: props.report.reportID, + policyID: props.report.policyID, }), - [props.report.lastReadTime, props.report.reportID], + [props.report.lastReadTime, props.report.reportID, props.report.policyID], ); // Comments have not loaded at all yet do nothing diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 760f67dbc310..6f061cb3d015 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -85,7 +85,7 @@ function ReportFooter(props) { const onSubmitComment = useCallback( (text) => { - Report.addComment(props.reportID, text); + Report.addComment(props.report.reportID, text); // We need to scroll to the bottom of the list after the comment is added const refID = setTimeout(() => { @@ -119,7 +119,7 @@ function ReportFooter(props) { isEqual(prevProps.report, nextProps.report) && - isEqual(prevProps.reportActions, nextProps.reportActions) && prevProps.pendingAction === nextProps.pendingAction && prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && prevProps.listHeight === nextProps.listHeight && From ad1b337ebc433635371547a8c8c8bbeeff800618 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 16:15:37 +0500 Subject: [PATCH 0019/1250] fix: linting and test --- src/libs/PersonalDetailsUtils.js | 2 +- .../report/ReportActionCompose/ComposerWithSuggestions.js | 2 ++ tests/unit/ReportActionItemSingleTest.js | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index acbdeca80a9d..965f1b927095 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -178,7 +178,7 @@ function getFormattedAddress(privatePersonalDetails) { /** * Get personal detail for an accountID - * @param {String} accountID + * @param {Number} accountID * @returns {PersonalDetail} personal detail object */ function getPersonalDetailsByAccountID(accountID) { diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 79c53650f27a..115cf404e589 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -283,6 +283,7 @@ function ComposerWithSuggestions({ debouncedBroadcastUserIsTyping(reportID); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [ preferredSkinTone, reportID, @@ -555,6 +556,7 @@ function ComposerWithSuggestions({ const onChangeText = useCallback((text) => { updateComment(text, true); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/tests/unit/ReportActionItemSingleTest.js b/tests/unit/ReportActionItemSingleTest.js index d6b46eb55414..799f6363d778 100644 --- a/tests/unit/ReportActionItemSingleTest.js +++ b/tests/unit/ReportActionItemSingleTest.js @@ -1,5 +1,5 @@ import Onyx from 'react-native-onyx'; -import {cleanup, screen} from '@testing-library/react-native'; +import {cleanup, screen, waitFor} from '@testing-library/react-native'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; @@ -70,7 +70,9 @@ describe('ReportActionItemSingle', () => { const expectedSecondaryIconTestId = 'SvgDefaultAvatar_w Icon'; return setup().then(() => { - expect(screen.getByTestId(expectedSecondaryIconTestId)).toBeDefined(); + waitFor(() => { + expect(screen.getByTestId(expectedSecondaryIconTestId)).toBeDefined(); + }); }); }); From d3921d9523b759c4275ea47757bc522a40c38b50 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 27 Oct 2023 18:39:52 +0700 Subject: [PATCH 0020/1250] fix ts --- src/pages/home/report/withReportOrNotFound.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx index 28d6707b085f..fe20a8a25d12 100644 --- a/src/pages/home/report/withReportOrNotFound.tsx +++ b/src/pages/home/report/withReportOrNotFound.tsx @@ -39,7 +39,7 @@ export default function ( const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData && (!Object.entries(props.report ?? {}).length || !props.report?.reportID); const shouldShowNotFoundPage = - !Object.entries(props.report ?? {}).length || !props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {}); + !Object.entries(props.report ?? {}).length || !props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas); // If the content was shown but it's not anymore that means the report was deleted and we are probably navigating out of this screen. // Return null for this case to avoid rendering FullScreenLoadingIndicator or NotFoundPage when animating transition. From 9a27bede1cd7a13f7f61bc32afdc23e91113026b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 17:08:19 +0500 Subject: [PATCH 0021/1250] fix: failing unread indicator test --- tests/ui/UnreadIndicatorsTest.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index a9ffe258ac7f..34e2c0322cde 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -524,13 +524,12 @@ describe('Unread Indicators', () => { .then(() => { // Simulate the response from the server so that the comment can be deleted in this test lastReportAction = {...CollectionUtils.lastItem(reportActions)}; - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { + return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { lastMessageText: lastReportAction.message[0].text, lastVisibleActionCreated: DateUtils.getDBTime(lastReportAction.timestamp), lastActorAccountID: lastReportAction.actorAccountID, reportID: REPORT_ID, }); - return waitForBatchedUpdates(); }) .then(() => { // Verify the chat preview text matches the last comment from the current user From 9f294367e0293535250ba3857e9b121dd0a215b4 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 17:15:08 +0500 Subject: [PATCH 0022/1250] fix: prettier issues --- src/components/LHNOptionsList/OptionRowLHNData.js | 13 +------------ src/components/withCurrentUserPersonalDetails.tsx | 2 +- src/libs/PersonalDetailsUtils.js | 4 ++-- src/libs/SidebarUtils.ts | 2 +- src/pages/home/ReportScreen.js | 5 ++++- .../ReportActionCompose/ComposerWithSuggestions.js | 8 ++------ src/pages/home/report/ReportActionsList.js | 2 +- 7 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index 2ddf8a687cfd..89142febf679 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -63,18 +63,7 @@ const defaultProps = { * The OptionRowLHN component is memoized, so it will only * re-render if the data really changed. */ -function OptionRowLHNData({ - isFocused, - fullReport, - reportActions, - preferredLocale, - comment, - policy, - receiptTransactions, - parentReportActions, - transaction, - ...propsToForward -}) { +function OptionRowLHNData({isFocused, fullReport, reportActions, preferredLocale, comment, policy, receiptTransactions, parentReportActions, transaction, ...propsToForward}) { const reportID = propsToForward.reportID; const parentReportAction = parentReportActions[fullReport.parentReportActionID]; diff --git a/src/components/withCurrentUserPersonalDetails.tsx b/src/components/withCurrentUserPersonalDetails.tsx index fa81c658bc78..a54f3f08c8b8 100644 --- a/src/components/withCurrentUserPersonalDetails.tsx +++ b/src/components/withCurrentUserPersonalDetails.tsx @@ -4,7 +4,7 @@ import getComponentDisplayName from '../libs/getComponentDisplayName'; import ONYXKEYS from '../ONYXKEYS'; import personalDetailsPropType from '../pages/personalDetailsPropType'; import type {PersonalDetails, Session} from '../types/onyx'; -import { getPersonalDetailsByAccountID } from '../libs/PersonalDetailsUtils'; +import {getPersonalDetailsByAccountID} from '../libs/PersonalDetailsUtils'; type CurrentUserPersonalDetails = PersonalDetails | Record; diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 965f1b927095..930ba19ad77b 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -178,7 +178,7 @@ function getFormattedAddress(privatePersonalDetails) { /** * Get personal detail for an accountID - * @param {Number} accountID + * @param {Number} accountID * @returns {PersonalDetail} personal detail object */ function getPersonalDetailsByAccountID(accountID) { @@ -187,7 +187,7 @@ function getPersonalDetailsByAccountID(accountID) { /** * Get whispered personal details for array of accountIDs - * @param {Array} whisperedToAccountIDs + * @param {Array} whisperedToAccountIDs * @returns {PersonalDetails} personal details */ function getWhisperedToPersonalDetails(whisperedToAccountIDs) { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 6db4c5aa8241..c71548a483db 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1,5 +1,5 @@ /* eslint-disable rulesdir/prefer-underscore-method */ -import Onyx, { OnyxEntry } from 'react-native-onyx'; +import Onyx, {OnyxEntry} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import {ValueOf} from 'type-fest'; import ONYXKEYS from '../ONYXKEYS'; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index ed1fd91b9834..31763e064c1d 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -172,7 +172,10 @@ function ReportScreen({ const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( - () => reportActions.length ? _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)) : {}, + () => + reportActions.length + ? _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)) + : {}, [reportActions, parentReportAction], ); const isSingleTransactionView = ReportUtils.isMoneyRequest(report); diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 115cf404e589..26e33a08417e 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -284,11 +284,7 @@ function ComposerWithSuggestions({ } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [ - preferredSkinTone, - reportID, - suggestionsRef, - ], + [preferredSkinTone, reportID, suggestionsRef], ); /** @@ -556,7 +552,7 @@ function ComposerWithSuggestions({ const onChangeText = useCallback((text) => { updateComment(text, true); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 8127e40a3aa2..6945cf82d158 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -71,7 +71,7 @@ const defaultProps = { isLoadingInitialReportActions: false, isLoadingOlderReportActions: false, isLoadingNewerReportActions: false, - policy: {} + policy: {}, }; const VERTICAL_OFFSET_THRESHOLD = 200; From 352227f178903ec5efe723578c47a12b3ff2ed95 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 19:08:09 +0500 Subject: [PATCH 0023/1250] fix: failing test --- src/pages/home/report/ReportActionsView.js | 70 ++++------------------ tests/ui/UnreadIndicatorsTest.js | 20 ++++--- 2 files changed, 25 insertions(+), 65 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index f1bd30f1a8b9..266ed6e2e346 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -116,7 +116,7 @@ function ReportActionsView(props) { // update ref with current network state prevNetworkRef.current = props.network; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.network, props.report, isReportFullyVisible]); + }, [props.network, isReportFullyVisible]); useEffect(() => { const prevIsSmallScreenWidth = prevIsSmallScreenWidthRef.current; @@ -130,7 +130,7 @@ function ReportActionsView(props) { // update ref with current state prevIsSmallScreenWidthRef.current = props.isSmallScreenWidth; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.isSmallScreenWidth, props.report, props.reportActions, isReportFullyVisible]); + }, [props.isSmallScreenWidth, props.reportActions, isReportFullyVisible]); useEffect(() => { // Ensures subscription event succeeds when the report/workspace room is created optimistically. @@ -142,7 +142,7 @@ function ReportActionsView(props) { Report.subscribeToReportTypingEvents(reportID); didSubscribeToReportTypingEvents.current = true; } - }, [props.report, didSubscribeToReportTypingEvents, reportID]); + }, [props.report.pendingFields, didSubscribeToReportTypingEvents, reportID]); /** * Retrieves the next set of report actions for the chat once we are nearing the end of what we are currently @@ -225,8 +225,9 @@ function ReportActionsView(props) { lastReadTime: props.report.lastReadTime, reportID: props.report.reportID, policyID: props.report.policyID, + lastVisibleActionCreated: props.report.lastVisibleActionCreated, }), - [props.report.lastReadTime, props.report.reportID, props.report.policyID], + [props.report.lastReadTime, props.report.reportID, props.report.policyID, props.report.lastVisibleActionCreated], ); // Comments have not loaded at all yet do nothing @@ -262,14 +263,6 @@ function arePropsEqual(oldProps, newProps) { return false; } - if (!_.isEqual(oldProps.report.pendingFields, newProps.report.pendingFields)) { - return false; - } - - if (!_.isEqual(oldProps.report.errorFields, newProps.report.errorFields)) { - return false; - } - if (lodashGet(oldProps.network, 'isOffline') !== lodashGet(newProps.network, 'isOffline')) { return false; } @@ -286,26 +279,14 @@ function arePropsEqual(oldProps, newProps) { return false; } - if (oldProps.report.lastReadTime !== newProps.report.lastReadTime) { - return false; - } - if (newProps.isSmallScreenWidth !== oldProps.isSmallScreenWidth) { return false; } - if (lodashGet(newProps.report, 'hasOutstandingIOU') !== lodashGet(oldProps.report, 'hasOutstandingIOU')) { - return false; - } - if (newProps.isComposerFullSize !== oldProps.isComposerFullSize) { return false; } - if (lodashGet(newProps.report, 'statusNum') !== lodashGet(oldProps.report, 'statusNum') || lodashGet(newProps.report, 'stateNum') !== lodashGet(oldProps.report, 'stateNum')) { - return false; - } - if (lodashGet(newProps, 'policy.avatar') !== lodashGet(oldProps, 'policy.avatar')) { return false; } @@ -314,39 +295,14 @@ function arePropsEqual(oldProps, newProps) { return false; } - if (lodashGet(newProps, 'report.reportName') !== lodashGet(oldProps, 'report.reportName')) { - return false; - } - - if (lodashGet(newProps, 'report.description') !== lodashGet(oldProps, 'report.description')) { - return false; - } - - if (lodashGet(newProps, 'report.managerID') !== lodashGet(oldProps, 'report.managerID')) { - return false; - } - - if (lodashGet(newProps, 'report.managerEmail') !== lodashGet(oldProps, 'report.managerEmail')) { - return false; - } - - if (lodashGet(newProps, 'report.total') !== lodashGet(oldProps, 'report.total')) { - return false; - } - - if (lodashGet(newProps, 'report.nonReimbursableTotal') !== lodashGet(oldProps, 'report.nonReimbursableTotal')) { - return false; - } - - if (lodashGet(newProps, 'report.writeCapability') !== lodashGet(oldProps, 'report.writeCapability')) { - return false; - } - - if (lodashGet(newProps, 'report.participantAccountIDs', 0) !== lodashGet(oldProps, 'report.participantAccountIDs', 0)) { - return false; - } - - return _.isEqual(lodashGet(newProps.report, 'icons', []), lodashGet(oldProps.report, 'icons', [])); + return ( + oldProps.report.lastReadTime === newProps.report.lastReadTime && + oldProps.report.reportID === newProps.report.reportID && + oldProps.report.policyID === newProps.report.policyID && + oldProps.report.lastVisibleActionCreated === newProps.report.lastVisibleActionCreated && + oldProps.report.isOptimisticReport === newProps.report.isOptimisticReport && + _.isEqual(oldProps.report.pendingFields, newProps.report.pendingFields) + ); } const MemoizedReportActionsView = React.memo(ReportActionsView, arePropsEqual); diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 34e2c0322cde..0fdee8aa63f7 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -130,13 +130,15 @@ function signInAndGetAppWithUnreadChat() { // Render the App and sign in as a test user. render(); return waitForBatchedUpdatesWithAct() - .then(() => { - const hintText = Localize.translateLocal('loginForm.loginForm'); - const loginForm = screen.queryAllByLabelText(hintText); - expect(loginForm).toHaveLength(1); - - return TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); - }) + .then(() => + waitFor(() => { + const hintText = Localize.translateLocal('loginForm.loginForm'); + const loginForm = screen.queryAllByLabelText(hintText); + expect(loginForm).toHaveLength(1); + + return TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); + }), + ) .then(() => { User.subscribeToUserEvents(); return waitForBatchedUpdates(); @@ -524,12 +526,14 @@ describe('Unread Indicators', () => { .then(() => { // Simulate the response from the server so that the comment can be deleted in this test lastReportAction = {...CollectionUtils.lastItem(reportActions)}; - return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { lastMessageText: lastReportAction.message[0].text, lastVisibleActionCreated: DateUtils.getDBTime(lastReportAction.timestamp), lastActorAccountID: lastReportAction.actorAccountID, reportID: REPORT_ID, }); + + return waitForBatchedUpdates(); }) .then(() => { // Verify the chat preview text matches the last comment from the current user From 34d4a7e6ba5e46b6afdaf8cf501882a3c4aced6e Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 3 Nov 2023 11:03:08 +0700 Subject: [PATCH 0024/1250] merge main --- src/libs/OptionsListUtils.js | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 08fddb0a8416..2e54b434293d 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -378,10 +378,7 @@ function getAllReportErrors(report, reportActions) { * @returns {String} */ function getLastMessageTextForReport(report) { - const lastReportAction = _.find( - allSortedReportActions[report.reportID], - (reportAction, key) => ReportActionUtils.shouldReportActionBeVisible(reportAction, key) && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - ); + const lastReportAction = _.find(allSortedReportActions[report.reportID], (reportAction) => ReportActionUtils.shouldReportActionBeVisibleAsLastAction(reportAction)); let lastMessageTextFromReport = ''; const lastActionName = lodashGet(lastReportAction, 'actionName', ''); @@ -404,6 +401,8 @@ function getLastMessageTextForReport(report) { lastMessageTextFromReport = ReportUtils.getReimbursementQueuedActionMessage(lastReportAction, report); } else if (ReportActionUtils.isDeletedParentAction(lastReportAction) && ReportUtils.isChatReport(report)) { lastMessageTextFromReport = ReportUtils.getDeletedParentActionMessageForChatReport(lastReportAction); + } else if (ReportActionUtils.isPendingRemove(lastReportAction) && ReportActionUtils.isThreadParentMessage(lastReportAction, report.reportID)) { + return Localize.translateLocal('parentReportAction.hiddenMessage'); } else if (ReportActionUtils.isModifiedExpenseAction(lastReportAction)) { const properSchemaForModifiedExpenseMessage = ReportUtils.getModifiedExpenseMessage(lastReportAction); lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForModifiedExpenseMessage, true); @@ -415,24 +414,6 @@ function getLastMessageTextForReport(report) { lastMessageTextFromReport = lodashGet(lastReportAction, 'message[0].text', ''); } else { lastMessageTextFromReport = report ? report.lastMessageText || '' : ''; - - // Yeah this is a bit ugly. If the latest report action that is not a whisper has been moderated as pending remove - // then set the last message text to the text of the latest visible action that is not a whisper or the report creation message. - const lastNonWhisper = - _.find(allSortedReportActions[report.reportID], (action) => !ReportActionUtils.isWhisperAction(action) && !ReportActionUtils.isDeletedParentAction(action)) || {}; - if (ReportActionUtils.isPendingRemove(lastNonWhisper)) { - const latestVisibleAction = - _.find( - allSortedReportActions[report.reportID], - (action) => ReportActionUtils.shouldReportActionBeVisibleAsLastAction(action) && !ReportActionUtils.isCreatedAction(action), - ) || {}; - - if (ReportActionUtils.isThreadParentMessage(latestVisibleAction) && ReportActionUtils.isPendingRemove(latestVisibleAction)) { - return Localize.translateLocal(CONST.TRANSLATION_KEYS.HIDDEN_MESSSAGE); - } - - lastMessageTextFromReport = lodashGet(latestVisibleAction, 'message[0].text', ''); - } } return lastMessageTextFromReport; } From 3820a4561ad4464d85adfbbda493ff776c37f01f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 6 Nov 2023 17:13:07 +0500 Subject: [PATCH 0025/1250] fix: reassure failures reported --- src/pages/home/ReportScreen.js | 22 ------------------- .../home/report/ReportActionItemSingle.js | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index ceeae027f522..a2d76192820c 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -1,4 +1,3 @@ -import {useFocusEffect} from '@react-navigation/core'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; @@ -20,13 +19,11 @@ import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; -import getIsReportFullyVisible from '@libs/getIsReportFullyVisible'; import Navigation from '@libs/Navigation/Navigation'; import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import Visibility from '@libs/Visibility'; import reportMetadataPropTypes from '@pages/reportMetadataPropTypes'; import reportPropTypes from '@pages/reportPropTypes'; import styles from '@styles/styles'; @@ -259,25 +256,6 @@ function ReportScreen({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(accountManagerReportID)); }, [accountManagerReportID]); - useFocusEffect( - useCallback(() => { - const unsubscribeVisibilityListener = Visibility.onVisibilityChange(() => { - const isTopMostReportID = Navigation.getTopmostReportId() === getReportID(route); - // If the report is not fully visible (AKA on small screen devices and LHR is open) or the report is optimistic (AKA not yet created) - // we don't need to call openReport - if (!getIsReportFullyVisible(isTopMostReportID) || report.isOptimisticReport) { - return; - } - - Report.openReport(report.reportID); - }); - - return () => unsubscribeVisibilityListener(); - // The effect should run only on the first focus to attach listener - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []), - ); - useEffect(() => { fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 634db2793710..cab7011e88fe 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -112,7 +112,7 @@ function ReportActionItemSingle(props) { if (displayAllActors) { // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = props.iouReport.ownerAccountID === actorAccountID ? props.iouReport.managerID : props.iouReport.ownerAccountID; - const secondaryUserDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(secondaryAccountId); + const secondaryUserDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(secondaryAccountId) || {}; const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { From ddf53d3c22e832d8d1ebecff9d08d0626909d067 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 7 Nov 2023 13:49:08 +0500 Subject: [PATCH 0026/1250] fix: reported reassure issues --- src/components/AttachmentModal.js | 23 ++++++- src/pages/home/ReportScreen.js | 1 - .../ReportActionCompose.js | 62 ++++++++++--------- src/pages/home/report/ReportFooter.js | 50 ++++++--------- 4 files changed, 75 insertions(+), 61 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 7fe342127272..8de07139e022 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -2,7 +2,7 @@ import Str from 'expensify-common/lib/str'; import lodashExtend from 'lodash/extend'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {Animated, Keyboard, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -393,6 +393,14 @@ function AttachmentModal(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy, props.transaction]); + useImperativeHandle( + props.forwardedRef, + () => ({ + displayFileInModal: validateAndDisplayFileToUpload, + }), + [validateAndDisplayFileToUpload], + ); + return ( <> ( + +)); + +AttachmentModalWithRef.displayName = 'AttachmentModalWithRef'; + export default compose( withWindowDimensions, withLocalize, @@ -539,4 +558,4 @@ export default compose( key: ONYXKEYS.SESSION, }, }), -)(memo(AttachmentModal)); +)(memo(AttachmentModalWithRef)); diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index a2d76192820c..506e635b2336 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -419,7 +419,6 @@ function ReportScreen({ report={report} pendingAction={addWorkspaceRoomOrChatPendingAction} isComposerFullSize={isComposerFullSize} - policies={policies} listHeight={listHeight} isEmptyChat={isEmptyChat} lastReportAction={lastReportAction} diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 420422534ba7..8b61aa0fa199 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -136,6 +136,7 @@ function ReportActionCompose({ const suggestionsRef = useRef(null); const composerRef = useRef(null); + const attachementModalRef = useRef(null); const reportParticipantIDs = useMemo( () => _.without(lodashGet(report, 'participantAccountIDs', []), currentUserPersonalDetails.accountID), @@ -316,6 +317,10 @@ function ReportActionCompose({ runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, isReportReadyForDisplay]); + const onDisplayFileInModal = () => { + attachementModalRef.current.displayFileInModal(); + }; + return ( @@ -339,6 +344,7 @@ function ReportActionCompose({ ]} > setIsAttachmentPreviewActive(true)} @@ -365,34 +371,6 @@ function ReportActionCompose({ onItemSelected={onItemSelected} actionButtonRef={actionButtonRef} /> - { if (isAttachmentPreviewActive) { @@ -405,6 +383,34 @@ function ReportActionCompose({ )} + { - // eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs - const connID = Onyx.connect({ - key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, - callback: (val) => { - if (val === shouldShowComposeInput) { - return; - } - setShouldShowComposeInput(val); - }, - }); - - return () => { - Onyx.disconnect(connID); - }; - }, [shouldShowComposeInput]); - const onSubmitComment = useCallback( (text) => { Report.addComment(props.report.reportID, text); @@ -114,7 +95,7 @@ function ReportFooter(props) { )} )} - {!hideComposer && (shouldShowComposeInput || !props.isSmallScreenWidth) && ( + {!hideComposer && (props.shouldShowComposeInput || !props.isSmallScreenWidth) && ( @@ -139,14 +119,24 @@ ReportFooter.displayName = 'ReportFooter'; ReportFooter.propTypes = propTypes; ReportFooter.defaultProps = defaultProps; -export default withWindowDimensions( +export default compose( + withWindowDimensions, + withOnyx({ + shouldShowComposeInput: { + key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, + }, + }), +)( memo( ReportFooter, (prevProps, nextProps) => isEqual(prevProps.report, nextProps.report) && prevProps.pendingAction === nextProps.pendingAction && - prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && prevProps.listHeight === nextProps.listHeight && + prevProps.isComposerFullSize === nextProps.isComposerFullSize && + prevProps.isEmptyChat === nextProps.isEmptyChat && + prevProps.lastReportAction === nextProps.lastReportAction && + prevProps.shouldShowComposeInput === nextProps.shouldShowComposeInput && prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay, ), ); From 5e2f21f8b62472cb67715f5bcd5f26e9e2b70e0e Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 7 Nov 2023 14:26:07 +0500 Subject: [PATCH 0027/1250] fix: reported reassure issues --- src/components/LHNOptionsList/LHNOptionsList.js | 14 +------------- .../ComposerWithSuggestions.js | 2 +- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.js b/src/components/LHNOptionsList/LHNOptionsList.js index 3986773aca87..2603c0128af3 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.js +++ b/src/components/LHNOptionsList/LHNOptionsList.js @@ -4,10 +4,8 @@ import React, {useCallback} from 'react'; import {FlatList, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import participantPropTypes from '@components/participantPropTypes'; import withCurrentReportID, {withCurrentReportIDDefaultProps, withCurrentReportIDPropTypes} from '@components/withCurrentReportID'; import compose from '@libs/compose'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; import reportActionPropTypes from '@pages/home/report/reportActionPropTypes'; import reportPropTypes from '@pages/reportPropTypes'; import styles from '@styles/styles'; @@ -56,9 +54,6 @@ const propTypes = { /** Indicates which locale the user currently has selected */ preferredLocale: PropTypes.string, - /** List of users' personal details */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** The transaction from the parent report action */ transactions: PropTypes.objectOf( PropTypes.shape({ @@ -78,7 +73,6 @@ const defaultProps = { reports: {}, policy: {}, preferredLocale: CONST.LOCALES.DEFAULT, - personalDetails: {}, transactions: {}, draftComments: {}, ...withCurrentReportIDDefaultProps, @@ -97,7 +91,6 @@ function LHNOptionsList({ reportActions, policy, preferredLocale, - personalDetails, transactions, draftComments, currentReportID, @@ -144,7 +137,6 @@ function LHNOptionsList({ '', )}`; const itemComment = draftComments[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] || ''; - const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForAccountIDs(itemFullReport.participantAccountIDs, personalDetails)); return ( ); }, - [currentReportID, draftComments, onSelectRow, optionMode, personalDetails, policy, preferredLocale, reportActions, reports, shouldDisableFocusOptions, transactions], + [currentReportID, draftComments, onSelectRow, optionMode, policy, preferredLocale, reportActions, reports, shouldDisableFocusOptions, transactions], ); return ( @@ -206,9 +197,6 @@ export default compose( preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, transactions: { key: ONYXKEYS.COLLECTION.TRANSACTION, }, diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index cf772230d2bf..7c5d0cfe2f2a 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -29,6 +29,7 @@ import * as SuggestionUtils from '@libs/SuggestionUtils'; import updateMultilineInputRange from '@libs/UpdateMultilineInputRange'; import updatePropsPaperWorklet from '@libs/updatePropsPaperWorklet'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; +import SendButton from '@pages/home/report/ReportActionCompose/SendButton'; import SilentCommentUpdater from '@pages/home/report/ReportActionCompose/SilentCommentUpdater'; import Suggestions from '@pages/home/report/ReportActionCompose/Suggestions'; import containerComposeStyles from '@styles/containerComposeStyles'; @@ -40,7 +41,6 @@ import * as Report from '@userActions/Report'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SendButton from '@pages/home/report/ReportActionCompose/SendButton'; import {defaultProps, propTypes} from './composerWithSuggestionsProps'; const {RNTextInputReset} = NativeModules; From 46c9d60399162fb2f2ff30ac5edd9760d2d0e159 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 13 Nov 2023 13:54:36 +0500 Subject: [PATCH 0028/1250] fix: pr review --- src/components/ArchivedReportFooter.js | 6 ++--- src/components/AttachmentModal.js | 20 ++++------------ src/components/AvatarWithDisplayName.js | 2 +- src/libs/OptionsListUtils.js | 11 +++++++-- src/libs/PersonalDetailsUtils.js | 15 +++++++++--- src/libs/ReportUtils.js | 6 ++--- src/libs/SidebarUtils.ts | 4 ++-- src/libs/actions/Task.js | 2 +- src/pages/ReportDetailsPage.js | 2 +- .../AttachmentPickerWithMenuItems.js | 3 ++- .../composerWithSuggestionsProps.js | 3 ++- .../ReportActionCompose/SuggestionMention.js | 24 ++++++------------- src/pages/home/report/ReportActionItem.js | 2 +- src/pages/home/report/ReportFooter.js | 2 ++ 14 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index a17cdb92136d..8c790f106ae7 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -46,14 +46,14 @@ const defaultProps = { function ArchivedReportFooter(props) { const archiveReason = lodashGet(props.reportClosedAction, 'originalMessage.reason', CONST.REPORT.ARCHIVE_REASON.DEFAULT); - let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName']); + let displayName = PersonalDetailsUtils.getDisplayNameOrDefault([props.report.ownerAccountID, 'displayName']); let oldDisplayName; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) { const newAccountID = props.reportClosedAction.originalMessage.newAccountID; const oldAccountID = props.reportClosedAction.originalMessage.oldAccountID; - displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [newAccountID, 'displayName']); - oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [oldAccountID, 'displayName']); + displayName = PersonalDetailsUtils.getDisplayNameOrDefault([newAccountID, 'displayName']); + oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault([oldAccountID, 'displayName']); } const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT; diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 24ac0421c5b2..57ed1ff3e131 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -2,7 +2,7 @@ import Str from 'expensify-common/lib/str'; import lodashExtend from 'lodash/extend'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {Animated, Keyboard, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -110,7 +110,7 @@ const defaultProps = { isWorkspaceAvatar: false, }; -function AttachmentModal(props) { +const AttachmentModal = forwardRef((props, ref) => { const onModalHideCallbackRef = useRef(null); const [isModalOpen, setIsModalOpen] = useState(props.defaultOpen); const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false); @@ -394,7 +394,7 @@ function AttachmentModal(props) { }, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]); useImperativeHandle( - props.forwardedRef, + ref, () => ({ displayFileInModal: validateAndDisplayFileToUpload, }), @@ -514,22 +514,12 @@ function AttachmentModal(props) { })} ); -} +}); AttachmentModal.propTypes = propTypes; AttachmentModal.defaultProps = defaultProps; AttachmentModal.displayName = 'AttachmentModal'; -const AttachmentModalWithRef = React.forwardRef((props, ref) => ( - -)); - -AttachmentModalWithRef.displayName = 'AttachmentModalWithRef'; - export default compose( withWindowDimensions, withLocalize, @@ -558,4 +548,4 @@ export default compose( key: ONYXKEYS.SESSION, }, }), -)(memo(AttachmentModalWithRef)); +)(memo(AttachmentModal)); diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 4be31b7d1a45..194a9239fa57 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -88,7 +88,7 @@ function AvatarWithDisplayName(props) { const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(props.report); const isMoneyRequestOrReport = ReportUtils.isMoneyRequestReport(props.report) || ReportUtils.isMoneyRequest(props.report); - const icons = ReportUtils.getIcons(props.report, null, props.policy); + const icons = ReportUtils.getIcons(props.report, null, '', -1, props.policy); const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID]); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(_.values(ownerPersonalDetails), false); const shouldShowSubscriptAvatar = ReportUtils.shouldReportShowSubscript(props.report); diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 485bfbbbf08d..6d5588548636 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -529,7 +529,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { (lastReportActions[report.reportID] && lastReportActions[report.reportID].originalMessage && lastReportActions[report.reportID].originalMessage.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { - displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), + displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault('displayName', lastActorDetails), policyName: ReportUtils.getPolicyName(report), }); } @@ -561,7 +561,14 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { result.text = reportName; result.searchText = getSearchText(report, reportName, personalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), personalDetail.login, personalDetail.accountID); + result.icons = ReportUtils.getIcons( + report, + UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), + personalDetail.login, + personalDetail.accountID, + undefined, + personalDetails, + ); result.subtitle = subtitle; return result; diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 842c06bb3228..bbcfd53454a9 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -17,13 +17,13 @@ Onyx.connect({ }); /** - * @param {Object | Null} passedPersonalDetails * @param {Array | String} pathToDisplayName * @param {String} [defaultValue] optional default display name value + * @param {Object | Null} passedPersonalDetails optional default personal details object * @returns {String} */ -function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defaultValue = '') { - const displayName = lodashGet(passedPersonalDetails || allPersonalDetails, pathToDisplayName); +function getDisplayNameOrDefault(pathToDisplayName, defaultValue = '', passedPersonalDetails = allPersonalDetails) { + const displayName = lodashGet(passedPersonalDetails, pathToDisplayName); return displayName || defaultValue || Localize.translateLocal('common.hidden'); } @@ -203,6 +203,14 @@ function isPersonalDetailsEmpty() { return !personalDetails.length; } +/** + * Get personal details object + * @returns {PersonalDetail} personal detail object + */ +function getPersonalDetails() { + return allPersonalDetails || {}; +} + export { getDisplayNameOrDefault, getPersonalDetailsByIDs, @@ -213,4 +221,5 @@ export { getPersonalDetailsByAccountID, getWhisperedToPersonalDetails, isPersonalDetailsEmpty, + getPersonalDetails, }; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 83cf62f6da04..38846dce5bc0 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1103,15 +1103,15 @@ function getWorkspaceIcon(report, policy = undefined) { * The Avatar sources can be URLs or Icon components according to the chat type. * * @param {Object} report - * @param {Object} passedPersonalDetails * @param {*} [defaultIcon] * @param {String} [defaultName] * @param {Number} [defaultAccountID] * @param {Object} [policy] + * @param {Object} passedPersonalDetails * @returns {Array<*>} */ -function getIcons(report, passedPersonalDetails, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined) { - const personalDetails = passedPersonalDetails || allPersonalDetails; +function getIcons(report, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined, passedPersonalDetails = allPersonalDetails) { + const personalDetails = passedPersonalDetails; if (_.isEmpty(report)) { const fallbackIcon = { source: defaultIcon || Expensicons.FallbackAvatar, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 97ae1d25cdd9..826db3995c43 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -425,7 +425,7 @@ function getOptionData( case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: { lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { policyName: ReportUtils.getPolicyName(report, false, policy), - displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), + displayName: PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', lastActorDetails), }); break; } @@ -515,7 +515,7 @@ function getOptionData( result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), '', -1, policy); + result.icons = ReportUtils.getIcons(report, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), '', -1, policy, personalDetails); result.searchText = OptionsListUtils.getSearchText(report, reportName, participantPersonalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); result.displayNamesWithTooltips = displayNamesWithTooltips; diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index e884a4d7a6b3..6329552d67e2 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -699,7 +699,7 @@ function getShareDestination(reportID, reports, personalDetails) { subtitle = ReportUtils.getChatRoomSubtitle(report); } return { - icons: ReportUtils.getIcons(report, personalDetails, Expensicons.FallbackAvatar), + icons: ReportUtils.getIcons(report, Expensicons.FallbackAvatar, '', -1, undefined, personalDetails), displayName: ReportUtils.getReportName(report), subtitle, displayNamesWithTooltips, diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index de25fdc3a081..935074462966 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -163,7 +163,7 @@ function ReportDetailsPage(props) { return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails), hasMultipleParticipants); }, [participants, props.personalDetails]); - const icons = useMemo(() => ReportUtils.getIcons(props.report, props.personalDetails, props.policies), [props.report, props.personalDetails, props.policies]); + const icons = useMemo(() => ReportUtils.getIcons(props.report, null, '', -1, props.policies, props.personalDetails), [props.report, props.personalDetails, props.policies]); const chatRoomSubtitleText = chatRoomSubtitle ? ( {}, + disabled: false, }; export {propTypes, defaultProps}; diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 6d85a0c349de..513e161dda41 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -1,16 +1,15 @@ import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; -import Onyx from 'react-native-onyx'; import _ from 'underscore'; import * as Expensicons from '@components/Icon/Expensicons'; import MentionSuggestions from '@components/MentionSuggestions'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import * as SuggestionProps from './suggestionProps'; /** @@ -38,20 +37,6 @@ const defaultProps = { forwardedRef: null, }; -/** - * We only need the personalDetails once because as long as the - * user is in the ReportScreen, these details won't be changing, - * hence we don't have to use it with `withOnyx`. - */ -let allPersonalDetails = {}; -// eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs -Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => { - allPersonalDetails = val; - }, -}); - function SuggestionMention({ value, setValue, @@ -65,7 +50,12 @@ function SuggestionMention({ measureParentContainer, isComposerFocused, }) { - const personalDetails = allPersonalDetails; + /** + * We only need the personalDetails once because as long as the + * user is in the ReportScreen, these details won't be changing, + * hence we don't have to use it with `withOnyx`. + */ + const personalDetails = PersonalDetailsUtils.getPersonalDetails(); const {translate} = useLocalize(); const previousValue = usePrevious(value); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index fe150be8db90..6c056c1b2527 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -365,7 +365,7 @@ function ReportActionItem(props) { ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName']); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault([props.report.ownerAccountID, 'displayName']); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index b407605a1c5d..d9490e17a688 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -137,6 +137,8 @@ export default compose( prevProps.isEmptyChat === nextProps.isEmptyChat && prevProps.lastReportAction === nextProps.lastReportAction && prevProps.shouldShowComposeInput === nextProps.shouldShowComposeInput && + prevProps.windowWidth === nextProps.windowWidth && + prevProps.isSmallScreenWidth === nextProps.isSmallScreenWidth && prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay, ), ); From 83d823ef9a37a5214319e54a41733d61061928ec Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 16 Nov 2023 12:16:32 +0500 Subject: [PATCH 0029/1250] fix: add default value --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index fc306a1f31bf..37b2e0add4ab 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -514,7 +514,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { (lastReportActions[report.reportID] && lastReportActions[report.reportID].originalMessage && lastReportActions[report.reportID].originalMessage.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { - displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault('displayName', lastActorDetails), + displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', lastActorDetails), policyName: ReportUtils.getPolicyName(report), }); } From 68a40f865bb0d47e1689eb6b4c11d671fb551e4d Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:34:45 +0100 Subject: [PATCH 0030/1250] add test navigator --- src/NAVIGATORS.ts | 1 + src/ROUTES.ts | 1 + src/SCREENS.ts | 1 + .../Navigation/AppNavigator/AuthScreens.js | 6 ++++ .../Navigators/SettingsNavigator.js | 36 +++++++++++++++++++ src/libs/Navigation/linkingConfig.js | 6 ++++ .../FloatingActionButtonAndPopover.js | 8 ++--- 7 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index a3a041e65684..52480d874d7b 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -5,5 +5,6 @@ export default { CENTRAL_PANE_NAVIGATOR: 'CentralPaneNavigator', RIGHT_MODAL_NAVIGATOR: 'RightModalNavigator', + SETTINGS_NAVIGATOR: 'Settings_Navigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', } as const; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 57d4eb8187ec..bd5f6cc422b3 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -58,6 +58,7 @@ export default { }, SETTINGS: 'settings', + SETTINGS_NEW: 'settings_new', SETTINGS_PROFILE: 'settings/profile', SETTINGS_SHARE_CODE: 'settings/shareCode', SETTINGS_DISPLAY_NAME: 'settings/profile/display-name', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index afc368858f55..f18fb49dff80 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -16,6 +16,7 @@ export default { NOT_FOUND: 'not-found', TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', VALIDATE_LOGIN: 'ValidateLogin', + SETTINGS_NEW: 'SettingsNew', SETTINGS: { ROOT: 'Settings_Root', PREFERENCES: 'Settings_Preferences', diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index aedb2fa8d741..52a7f0c41afe 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -35,6 +35,7 @@ import defaultScreenOptions from './defaultScreenOptions'; import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions'; import CentralPaneNavigator from './Navigators/CentralPaneNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; +import SettingsNavigator from './Navigators/SettingsNavigator'; const loadReportAttachments = () => require('../../../pages/home/report/ReportAttachments').default; const loadSidebarScreen = () => require('../../../pages/home/sidebar/SidebarScreen').default; @@ -335,6 +336,11 @@ function AuthScreens({isUsingMemoryOnlyKeys, lastUpdateIDAppliedToClient, sessio component={RightModalNavigator} listeners={modalScreenListeners} /> + + + + + + ); +} + +SettingsNavigator.propTypes = propTypes; +SettingsNavigator.displayName = 'SettingsNavigator'; + +export default SettingsNavigator; diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 44473998ac62..4137260e2390 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -429,6 +429,12 @@ export default { }, }, }, + + [NAVIGATORS.SETTINGS_NAVIGATOR]: { + screens: { + [SCREENS.SETTINGS_NEW]: ROUTES.SETTINGS_NEW, + } + } }, }, }; diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 739f7e3e0295..1eebf43d8a0a 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -243,11 +243,9 @@ function FloatingActionButtonAndPopover(props) { isActive={isCreateMenuActive} ref={anchorRef} onPress={() => { - if (isCreateMenuActive) { - hideCreateMenu(); - } else { - showCreateMenu(); - } + console.log('FloatingActionButtonAndPopover.js: showCreateMenu()'); + // navigate to new screen + Navigation.navigate(ROUTES.SETTINGS_NEW); }} /> From bddaa69fad1bfc22d4cd5afe66e38c7ebba4003e Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:43:00 +0100 Subject: [PATCH 0031/1250] draft --- src/ROUTES.ts | 1 + .../Navigators/SettingsNavigator.js | 41 +++++++++++++------ .../createCustomStackNavigator/index.js | 15 +++++-- src/libs/Navigation/linkingConfig.js | 4 +- .../FloatingActionButtonAndPopover.js | 2 +- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index bd5f6cc422b3..95d6f13a8921 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -59,6 +59,7 @@ export default { SETTINGS: 'settings', SETTINGS_NEW: 'settings_new', + SETTINGS_NEW_PROFILE: 'settings_new/profile', SETTINGS_PROFILE: 'settings/profile', SETTINGS_SHARE_CODE: 'settings/shareCode', SETTINGS_DISPLAY_NAME: 'settings/profile/display-name', diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index 46d80cb0fb55..ef4d0b30a05f 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -1,12 +1,13 @@ -import {createStackNavigator} from '@react-navigation/stack'; import PropTypes from 'prop-types'; import React from 'react'; -import {View} from 'react-native'; -import RHPScreenOptions from '@libs/Navigation/AppNavigator/RHPScreenOptions'; +import { View } from 'react-native'; import useThemeStyles from '@styles/useThemeStyles'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; - -const Stack = createStackNavigator(); +import useWindowDimensions from '@hooks/useWindowDimensions'; +import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; +import createCustomStackNavigator from '@libs/Navigation/AppNavigator/createCustomStackNavigator'; +import NAVIGATORS from '@src/NAVIGATORS'; +import ROUTES from '@src/ROUTES'; const propTypes = { /* Navigation functions provided by React Navigation */ @@ -15,18 +16,32 @@ const propTypes = { }).isRequired, }; +const RootStack = createCustomStackNavigator(); + function SettingsNavigator() { const styles = useThemeStyles(); + const {isSmallScreenWidth} = useWindowDimensions(); + const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth); return ( - - - - - + + + + + + ); } diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js index 8924b01e2acb..bb2edb3aa2bb 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js @@ -19,20 +19,23 @@ const propTypes = { /* Screen options defined for this navigator */ // eslint-disable-next-line react/forbid-prop-types screenOptions: PropTypes.object, + + centralRoute: PropTypes.string, }; const defaultProps = { initialRouteName: undefined, screenOptions: undefined, + centralRoute: NAVIGATORS.CENTRAL_PANE_NAVIGATOR, }; -function reduceReportRoutes(routes) { +function reduceReportRoutes(routes, centralRoute=NAVIGATORS.CENTRAL_PANE_NAVIGATOR) { const result = []; let count = 0; const reverseRoutes = [...routes].reverse(); reverseRoutes.forEach((route) => { - if (route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR) { + if (route.name === centralRoute) { // Remove all report routes except the last 3. This will improve performance. if (count < 3) { result.push(route); @@ -43,6 +46,8 @@ function reduceReportRoutes(routes) { } }); + console.log('reduceReportRoutes', centralRoute, routes, result); + return result.reverse(); } @@ -62,14 +67,16 @@ function ResponsiveStackNavigator(props) { }); const stateToRender = useMemo(() => { - const result = reduceReportRoutes(state.routes); + const result = reduceReportRoutes(state.routes, props.centralRoute); return { ...state, index: result.length - 1, routes: [...result], }; - }, [state]); + }, [props.centralRoute, state]); + + console.log('ResponsiveStackNavigator', props.centralRoute); return ( diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 4137260e2390..05f6e3c4e344 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -432,8 +432,8 @@ export default { [NAVIGATORS.SETTINGS_NAVIGATOR]: { screens: { - [SCREENS.SETTINGS_NEW]: ROUTES.SETTINGS_NEW, - } + [SCREENS.SETTINGS_NEW_PROFILE]: {path: ROUTES.SETTINGS_NEW_PROFILE}, + }, } }, }, diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 1eebf43d8a0a..c798121b1d2a 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -245,7 +245,7 @@ function FloatingActionButtonAndPopover(props) { onPress={() => { console.log('FloatingActionButtonAndPopover.js: showCreateMenu()'); // navigate to new screen - Navigation.navigate(ROUTES.SETTINGS_NEW); + Navigation.navigate(ROUTES.SETTINGS_NEW_PROFILE); }} /> From d8910f7d0d038a80b7b1e490008bf4b26a2f7527 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:53:09 +0100 Subject: [PATCH 0032/1250] add patch --- .../@react-navigation+stack+6.3.16+002+dontDetachScreen.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch index d64fc4fecf74..ac4e8bafa8ab 100644 --- a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch +++ b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch @@ -43,7 +43,7 @@ index 7558eb3..b7bb75e 100644 }) : STATE_TRANSITIONING_OR_BELOW_TOP; } + -+ const isHomeScreenAndNotOnTop = route.name === 'Home' && isScreenActive !== STATE_ON_TOP; ++ const isHomeScreenAndNotOnTop = (route.name === 'Home' || route.name === 'SettingsHome') && isScreenActive !== STATE_ON_TOP; + const { headerShown = true, From 52388b93907b5aeef07c91e5c03244494124e1bf Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:32:14 +0100 Subject: [PATCH 0033/1250] draft --- src/NAVIGATORS.ts | 1 - src/ROUTES.ts | 2 +- src/SCREENS.ts | 2 +- .../Navigation/AppNavigator/AuthScreens.js | 2 +- .../Navigators/SettingsNavigator.js | 27 ++++++++++++------- src/libs/Navigation/linkTo.js | 1 + src/libs/Navigation/linkingConfig.js | 15 +++++++++-- 7 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index 52480d874d7b..a3a041e65684 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -5,6 +5,5 @@ export default { CENTRAL_PANE_NAVIGATOR: 'CentralPaneNavigator', RIGHT_MODAL_NAVIGATOR: 'RightModalNavigator', - SETTINGS_NAVIGATOR: 'Settings_Navigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', } as const; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 95d6f13a8921..3d1a52230f3e 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -58,7 +58,7 @@ export default { }, SETTINGS: 'settings', - SETTINGS_NEW: 'settings_new', + SETTINGS_HOME: 'settings_new', SETTINGS_NEW_PROFILE: 'settings_new/profile', SETTINGS_PROFILE: 'settings/profile', SETTINGS_SHARE_CODE: 'settings/shareCode', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f18fb49dff80..b721d7f32c4b 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -16,7 +16,7 @@ export default { NOT_FOUND: 'not-found', TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', VALIDATE_LOGIN: 'ValidateLogin', - SETTINGS_NEW: 'SettingsNew', + SETTINGS_HOME: 'SettingsHome', SETTINGS: { ROOT: 'Settings_Root', PREFERENCES: 'Settings_Preferences', diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 52a7f0c41afe..1d8ce5c69164 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -337,7 +337,7 @@ function AuthScreens({isUsingMemoryOnlyKeys, lastUpdateIDAppliedToClient, sessio listeners={modalScreenListeners} /> diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index ef4d0b30a05f..da46b8a4a761 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -8,38 +8,47 @@ import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRoot import createCustomStackNavigator from '@libs/Navigation/AppNavigator/createCustomStackNavigator'; import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; +import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; const propTypes = { /* Navigation functions provided by React Navigation */ navigation: PropTypes.shape({ goBack: PropTypes.func.isRequired, + getState: PropTypes.func.isRequired, }).isRequired, }; const RootStack = createCustomStackNavigator(); -function SettingsNavigator() { +function SettingsNavigator({navigation}) { const styles = useThemeStyles(); const {isSmallScreenWidth} = useWindowDimensions(); const screenOptions = getRootNavigatorScreenOptions(isSmallScreenWidth); + console.log('navigation state', navigation.getState()); + return ( - + + {/* */} ); diff --git a/src/libs/Navigation/linkTo.js b/src/libs/Navigation/linkTo.js index 286074914cf7..72ff8f795a95 100644 --- a/src/libs/Navigation/linkTo.js +++ b/src/libs/Navigation/linkTo.js @@ -58,6 +58,7 @@ export default function linkTo(navigation, path, type) { const state = getStateFromPath(path); const action = getActionFromState(state, linkingConfig.config); + console.log('linkTo', state, action); // If action type is different than NAVIGATE we can't change it to the PUSH safely if (action.type === CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 05f6e3c4e344..1a63a8e47569 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -430,9 +430,20 @@ export default { }, }, - [NAVIGATORS.SETTINGS_NAVIGATOR]: { + [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { screens: { - [SCREENS.SETTINGS_NEW_PROFILE]: {path: ROUTES.SETTINGS_NEW_PROFILE}, + Settings: { + screens: { + [SCREENS.SETTINGS_HOME]: { + path: ROUTES.SETTINGS_HOME, + exact: true, + }, + [SCREENS.SETTINGS_NEW_PROFILE]: { + path: ROUTES.SETTINGS_NEW_PROFILE, + exact: true, + }, + } + } }, } }, From 0d665ef8c9270972710606527d09671c1648de77 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:02:19 +0100 Subject: [PATCH 0034/1250] another step --- .../Navigators/SettingsNavigator.js | 10 +++---- src/libs/Navigation/linkingConfig.js | 27 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index da46b8a4a761..fa0bc62cc9c0 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -2,15 +2,14 @@ import PropTypes from 'prop-types'; import React from 'react'; import { View } from 'react-native'; import useThemeStyles from '@styles/useThemeStyles'; -import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import useWindowDimensions from '@hooks/useWindowDimensions'; import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRootNavigatorScreenOptions'; import createCustomStackNavigator from '@libs/Navigation/AppNavigator/createCustomStackNavigator'; -import NAVIGATORS from '@src/NAVIGATORS'; -import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; +const loadSidebarScreen = () => require('../../../../pages/home/sidebar/SidebarScreen').default; + const propTypes = { /* Navigation functions provided by React Navigation */ navigation: PropTypes.shape({ @@ -33,15 +32,14 @@ function SettingsNavigator({navigation}) { diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 1a63a8e47569..71f7f6916355 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -432,18 +432,21 @@ export default { [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { screens: { - Settings: { - screens: { - [SCREENS.SETTINGS_HOME]: { - path: ROUTES.SETTINGS_HOME, - exact: true, - }, - [SCREENS.SETTINGS_NEW_PROFILE]: { - path: ROUTES.SETTINGS_NEW_PROFILE, - exact: true, - }, - } - } + [SCREENS.SETTINGS_HOME]: { + path: ROUTES.SETTINGS_HOME, + }, + SettingsCentralPane: { + path: ROUTES.SETTINGS_NEW_PROFILE, + exact: true, + }, + // Settings: { + // screens: { + // [SCREENS.SETTINGS_NEW_PROFILE]: { + // path: ROUTES.SETTINGS_NEW_PROFILE, + // exact: true, + // }, + // } + // } }, } }, From dba92bfb1fe64b727baf8d56665953c36b7c1846 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:05:44 +0100 Subject: [PATCH 0035/1250] another step forward --- src/SCREENS.ts | 1 + .../Navigation/AppNavigator/Navigators/SettingsNavigator.js | 2 +- src/libs/Navigation/linkingConfig.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b721d7f32c4b..b7def3ce8e23 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -17,6 +17,7 @@ export default { TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', VALIDATE_LOGIN: 'ValidateLogin', SETTINGS_HOME: 'SettingsHome', + SETTINGS_NEW_PROFILE: 'SettingsNewProfile', SETTINGS: { ROOT: 'Settings_Root', PREFERENCES: 'Settings_Preferences', diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index fa0bc62cc9c0..705eaf35eb7b 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -39,7 +39,7 @@ function SettingsNavigator({navigation}) { getComponent={loadSidebarScreen} /> diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 71f7f6916355..0d09a984ae61 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -435,7 +435,7 @@ export default { [SCREENS.SETTINGS_HOME]: { path: ROUTES.SETTINGS_HOME, }, - SettingsCentralPane: { + [SCREENS.SETTINGS_NEW_PROFILE]: { path: ROUTES.SETTINGS_NEW_PROFILE, exact: true, }, From 14e07418e95a4e342b33f278e8690d6525ae60a4 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:19:38 +0100 Subject: [PATCH 0036/1250] another step forward --- .../Navigators/SettingsNavigator.js | 7 +-- .../CustomRouter.js | 53 +++++++++++++------ .../createCustomStackNavigator/index.js | 1 + src/libs/Navigation/linkingConfig.js | 2 +- .../FloatingActionButtonAndPopover.js | 2 +- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index 705eaf35eb7b..3d82337c6ad7 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -32,6 +32,7 @@ function SettingsNavigator({navigation}) { - {/* */} ); diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js index 5d3eb38d49dc..2a05a90796bb 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js @@ -3,12 +3,14 @@ import lodashFindLast from 'lodash/findLast'; import _ from 'underscore'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; +import CentralPaneNavigator from '../Navigators/CentralPaneNavigator'; /** * @param {Object} state - react-navigation state + * @param {String} centralRoute - name of the central route * @returns {Boolean} */ -const isAtLeastOneCentralPaneNavigatorInState = (state) => _.find(state.routes, (r) => r.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); +const isAtLeastOneCentralPaneNavigatorInState = (state, centralRoute) => _.find(state.routes, (r) => r.name === centralRoute); /** * @param {Object} state - react-navigation state @@ -41,22 +43,37 @@ const getTopMostReportIDFromRHP = (state) => { * The report screen will self set proper reportID param based on the helper function findLastAccessedReport (look at ReportScreenWrapper for more info) * * @param {Object} state - react-navigation state + * @param {String} centralRoute - name of the central route */ -const addCentralPaneNavigatorRoute = (state) => { +const addCentralPaneNavigatorRoute = (state, centralRoute) => { const reportID = getTopMostReportIDFromRHP(state); - const centralPaneNavigatorRoute = { - name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR, - state: { - routes: [ - { - name: SCREENS.REPORT, - params: { - reportID, + let centralPaneNavigatorRoute; + if (centralRoute === NAVIGATORS.CENTRAL_PANE_NAVIGATOR) { + centralPaneNavigatorRoute = { + name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR, + state: { + routes: [ + { + name: SCREENS.REPORT, + params: { + reportID, + }, }, - }, - ], - }, - }; + ], + }, + }; + } else { + centralPaneNavigatorRoute = { + name: centralRoute, + state: { + routes: [ + { + name: 'SettingsCentralPane', + }, + ], + }, + }; + } state.routes.splice(1, 0, centralPaneNavigatorRoute); // eslint-disable-next-line no-param-reassign state.index = state.routes.length - 1; @@ -64,16 +81,20 @@ const addCentralPaneNavigatorRoute = (state) => { function CustomRouter(options) { const stackRouter = StackRouter(options); + const centralRoute = options.centralRoute || NAVIGATORS.CENTRAL_PANE_NAVIGATOR; + console.log('CustomRouter', centralRoute); return { ...stackRouter, getRehydratedState(partialState, {routeNames, routeParamList}) { + console.log('getRehydratedState', centralRoute); // Make sure that there is at least one CentralPaneNavigator (ReportScreen by default) in the state if this is a wide layout - if (!isAtLeastOneCentralPaneNavigatorInState(partialState) && !options.getIsSmallScreenWidth()) { + if (!isAtLeastOneCentralPaneNavigatorInState(partialState, centralRoute) && !options.getIsSmallScreenWidth()) { + console.log('getRehydratedState', centralRoute, 'adding CentralPaneNavigator'); // If we added a route we need to make sure that the state.stale is true to generate new key for this route // eslint-disable-next-line no-param-reassign partialState.stale = true; - addCentralPaneNavigatorRoute(partialState); + addCentralPaneNavigatorRoute(partialState, centralRoute); } const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList}); return state; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js index bb2edb3aa2bb..120e6e99dd61 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js @@ -64,6 +64,7 @@ function ResponsiveStackNavigator(props) { initialRouteName: props.initialRouteName, // Options for useNavigationBuilder won't update on prop change, so we need to pass a getter for the router to have the current state of isSmallScreenWidth. getIsSmallScreenWidth: () => isSmallScreenWidthRef.current, + centralRoute: props.centralRoute, }); const stateToRender = useMemo(() => { diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 0d09a984ae61..71f7f6916355 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -435,7 +435,7 @@ export default { [SCREENS.SETTINGS_HOME]: { path: ROUTES.SETTINGS_HOME, }, - [SCREENS.SETTINGS_NEW_PROFILE]: { + SettingsCentralPane: { path: ROUTES.SETTINGS_NEW_PROFILE, exact: true, }, diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index c798121b1d2a..5ad3e4431b32 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -245,7 +245,7 @@ function FloatingActionButtonAndPopover(props) { onPress={() => { console.log('FloatingActionButtonAndPopover.js: showCreateMenu()'); // navigate to new screen - Navigation.navigate(ROUTES.SETTINGS_NEW_PROFILE); + Navigation.navigate(ROUTES.SETTINGS_HOME); }} /> From 18ed44a367f7cdc2e423bf3d1cdb16d2fa90a584 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:39:52 +0100 Subject: [PATCH 0037/1250] another step forward 123 --- .../AppNavigator/Navigators/SettingsNavigator.js | 6 +++++- .../createCustomStackNavigator/CustomRouter.js | 11 ++++++----- .../AppNavigator/createCustomStackNavigator/index.js | 6 +++++- src/libs/Navigation/NavigationRoot.js | 1 + src/libs/Navigation/linkingConfig.js | 8 ++++++-- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index 3d82337c6ad7..13cd7ebfd27c 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -9,6 +9,7 @@ import SCREENS from '@src/SCREENS'; import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; const loadSidebarScreen = () => require('../../../../pages/home/sidebar/SidebarScreen').default; +const loadPage = () => require('../../../../pages/ErrorPage/NotFoundPage').default; const propTypes = { /* Navigation functions provided by React Navigation */ @@ -27,17 +28,20 @@ function SettingsNavigator({navigation}) { console.log('navigation state', navigation.getState()); + console.log('loadSidebarScreen', loadSidebarScreen, 'not found page', loadPage) + return ( _.find(state.routes, (r) => r.name === centralRoute); +const isAtLeastOneCentralPaneNavigatorInState = (state, centralRoute) => { + console.log('isAtLeastOneCentralPaneNavigatorInState', centralRoute); + return _.find(state.routes, (r) => r.name === centralRoute) +}; /** * @param {Object} state - react-navigation state @@ -81,16 +83,15 @@ const addCentralPaneNavigatorRoute = (state, centralRoute) => { function CustomRouter(options) { const stackRouter = StackRouter(options); - const centralRoute = options.centralRoute || NAVIGATORS.CENTRAL_PANE_NAVIGATOR; - console.log('CustomRouter', centralRoute); return { ...stackRouter, getRehydratedState(partialState, {routeNames, routeParamList}) { + const centralRoute = options.centralRoute(); console.log('getRehydratedState', centralRoute); // Make sure that there is at least one CentralPaneNavigator (ReportScreen by default) in the state if this is a wide layout if (!isAtLeastOneCentralPaneNavigatorInState(partialState, centralRoute) && !options.getIsSmallScreenWidth()) { - console.log('getRehydratedState', centralRoute, 'adding CentralPaneNavigator'); + console.log('getRehydratedState', centralRoute, 'adding central pane'); // If we added a route we need to make sure that the state.stale is true to generate new key for this route // eslint-disable-next-line no-param-reassign partialState.stale = true; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js index 120e6e99dd61..25fee9a02c77 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.js @@ -52,19 +52,23 @@ function reduceReportRoutes(routes, centralRoute=NAVIGATORS.CENTRAL_PANE_NAVIGAT } function ResponsiveStackNavigator(props) { + console.log('ResponsiveStackNavigator', props.centralRoute) const {isSmallScreenWidth} = useWindowDimensions(); const isSmallScreenWidthRef = useRef(isSmallScreenWidth); isSmallScreenWidthRef.current = isSmallScreenWidth; + const centralRouteRef = useRef(props.centralRoute); + centralRouteRef.current = props.centralRoute; + const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder(CustomRouter, { children: props.children, screenOptions: props.screenOptions, initialRouteName: props.initialRouteName, // Options for useNavigationBuilder won't update on prop change, so we need to pass a getter for the router to have the current state of isSmallScreenWidth. getIsSmallScreenWidth: () => isSmallScreenWidthRef.current, - centralRoute: props.centralRoute, + centralRoute: () => centralRouteRef.current, }); const stateToRender = useMemo(() => { diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index 2373066cf4bd..314a324dc0fe 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -75,6 +75,7 @@ function NavigationRoot(props) { if (!navigationRef.isReady() || !props.authenticated) { return; } + // TODO: Add force rehydration! // We need to force state rehydration so the CustomRouter can add the CentralPaneNavigator route if necessary. navigationRef.resetRoot(navigationRef.getRootState()); }, [isSmallScreenWidth, props.authenticated]); diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 71f7f6916355..2fd0db3cd48f 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -436,8 +436,12 @@ export default { path: ROUTES.SETTINGS_HOME, }, SettingsCentralPane: { - path: ROUTES.SETTINGS_NEW_PROFILE, - exact: true, + screens: { + [SCREENS.SETTINGS_NEW_PROFILE]: { + path: ROUTES.SETTINGS_NEW_PROFILE, + exact: true, + } + } }, // Settings: { // screens: { From 7fa40aedb098807d85aa709f9527e26080b58ab2 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:49:31 +0100 Subject: [PATCH 0038/1250] mini step forward --- .../Navigators/SettingsNavigator.js | 7 +- src/libs/Navigation/linkingConfig.js | 402 +++++++++--------- 2 files changed, 199 insertions(+), 210 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index 13cd7ebfd27c..4cfb5a9ae82b 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -7,9 +7,9 @@ import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRoot import createCustomStackNavigator from '@libs/Navigation/AppNavigator/createCustomStackNavigator'; import SCREENS from '@src/SCREENS'; import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; +import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -const loadSidebarScreen = () => require('../../../../pages/home/sidebar/SidebarScreen').default; -const loadPage = () => require('../../../../pages/ErrorPage/NotFoundPage').default; +const loadPage = () => require('../../../../pages/settings/InitialSettingsPage').default; const propTypes = { /* Navigation functions provided by React Navigation */ @@ -28,14 +28,11 @@ function SettingsNavigator({navigation}) { console.log('navigation state', navigation.getState()); - console.log('loadSidebarScreen', loadSidebarScreen, 'not found page', loadPage) - return ( Date: Tue, 21 Nov 2023 16:14:00 +0100 Subject: [PATCH 0039/1250] add initial state --- .../AppNavigator/Navigators/SettingsNavigator.js | 1 - .../createCustomStackNavigator/CustomRouter.js | 11 ++++++++++- src/libs/Navigation/Navigation.js | 8 +++++++- src/pages/settings/InitialSettingsPage.js | 1 + src/pages/settings/Preferences/PreferencesPage.js | 2 +- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js index 4cfb5a9ae82b..e1d68e58e973 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/SettingsNavigator.js @@ -7,7 +7,6 @@ import getRootNavigatorScreenOptions from '@libs/Navigation/AppNavigator/getRoot import createCustomStackNavigator from '@libs/Navigation/AppNavigator/createCustomStackNavigator'; import SCREENS from '@src/SCREENS'; import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators'; -import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; const loadPage = () => require('../../../../pages/settings/InitialSettingsPage').default; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js index 31bf4a7bb1b5..3a53a54e60ad 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js @@ -70,7 +70,7 @@ const addCentralPaneNavigatorRoute = (state, centralRoute) => { state: { routes: [ { - name: 'SettingsCentralPane', + name: SCREENS.SETTINGS.PREFERENCES, }, ], }, @@ -86,6 +86,15 @@ function CustomRouter(options) { return { ...stackRouter, + getInitialState({routeNames, routeParamList, routeGetIdList}) { + console.log('getInitialState', routeNames, routeParamList, routeGetIdList); + const centralRoute = options.centralRoute(); + const initialState = stackRouter.getInitialState({routeNames, routeParamList, routeGetIdList}); + if (!isAtLeastOneCentralPaneNavigatorInState(initialState, centralRoute) && !options.getIsSmallScreenWidth()) { + addCentralPaneNavigatorRoute(initialState, centralRoute); + } + return initialState; + }, getRehydratedState(partialState, {routeNames, routeParamList}) { const centralRoute = options.centralRoute(); console.log('getRehydratedState', centralRoute); diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 7a2c61ea7b53..bcf0effa37a4 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -66,7 +66,7 @@ const getActiveRouteIndex = function (route, index) { return getActiveRouteIndex(childActiveRoute, route.state.index || 0); } - if (route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR) { + if (route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR || route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) { return 0; } @@ -147,6 +147,12 @@ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = f navigationRef.current.goBack(); return; } + + if(lastRoute.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) { + console.log('lastRoute', lastRoute); + navigationRef.current.goBack(); + return; + } } if (shouldEnforceFallback || (isFirstRouteInNavigator && fallbackRoute)) { diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index d88105b31360..628ca489fc70 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -388,6 +388,7 @@ function InitialSettingsPage(props) { headerContent={headerContent} headerContainerStyles={[styles.staticHeaderImage, styles.justifyContentCenter]} backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.ROOT]} + onBackButtonPress={() => Navigation.goBack()} > {getMenuItems} diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js index 4dbc5fda9198..5c056385dd2d 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.js @@ -45,7 +45,7 @@ function PreferencesPage(props) { return ( Navigation.goBack(ROUTES.SETTINGS)} + shouldShowBackButton={false} backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.PREFERENCES]} illustration={LottieAnimations.PreferencesDJ} > From 6b1dee1cfb3ce68b8bb9c3a4283d3dee3f9db837 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:44:04 +0100 Subject: [PATCH 0040/1250] few fixes --- src/SCREENS.ts | 1 + .../AppNavigator/ModalStackNavigators.js | 6 +++--- .../CustomRouter.js | 2 +- src/libs/Navigation/Navigation.js | 21 ++++++++++++++----- src/libs/Navigation/linkingConfig.js | 5 +---- .../sidebar/PressableAvatarWithIndicator.js | 2 +- src/pages/settings/InitialSettingsPage.js | 7 ++++++- 7 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b7def3ce8e23..6ef776ef4507 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -21,6 +21,7 @@ export default { SETTINGS: { ROOT: 'Settings_Root', PREFERENCES: 'Settings_Preferences', + PROFILE: 'Settings_Profile', WORKSPACES: 'Settings_Workspaces', SECURITY: 'Settings_Security', STATUS: 'Settings_Status', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index a2f9bdd7a903..1368f12a0213 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -1,4 +1,4 @@ -import {CardStyleInterpolators, createStackNavigator} from '@react-navigation/stack'; +import {createStackNavigator} from '@react-navigation/stack'; import React from 'react'; import _ from 'underscore'; import styles from '@styles/styles'; @@ -7,7 +7,8 @@ import SCREENS from '@src/SCREENS'; const defaultSubRouteOptions = { cardStyle: styles.navigationScreenCardStyle, headerShown: false, - cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, + // TODO: Can remove it + // cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, }; /** @@ -131,7 +132,6 @@ const NewTeachersUniteNavigator = createModalStackNavigator({ }); const SettingsModalStackNavigator = createModalStackNavigator({ - [SCREENS.SETTINGS.ROOT]: () => require('../../../pages/settings/InitialSettingsPage').default, Settings_Share_Code: () => require('../../../pages/ShareCodePage').default, [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../pages/workspace/WorkspacesListPage').default, Settings_Profile: () => require('../../../pages/settings/Profile/ProfilePage').default, diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js index 3a53a54e60ad..5ac04ab884a4 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js @@ -70,7 +70,7 @@ const addCentralPaneNavigatorRoute = (state, centralRoute) => { state: { routes: [ { - name: SCREENS.SETTINGS.PREFERENCES, + name: SCREENS.SETTINGS.PROFILE, }, ], }, diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index bcf0effa37a4..da5e8fbaecf0 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -121,6 +121,7 @@ function navigate(route = ROUTES.HOME, type) { * @param {Boolean} shouldPopToTop - Should we navigate to LHN on back press */ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = false) { + console.log('goBack', 1, navigationRef.current.state); if (!canNavigate('goBack')) { return; } @@ -132,14 +133,17 @@ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = f return; } } + console.log('goBack', 2); if (!navigationRef.current.canGoBack()) { Log.hmmm('[Navigation] Unable to go back'); return; } + console.log('goBack', 3); const isFirstRouteInNavigator = !getActiveRouteIndex(navigationRef.current.getState()); if (isFirstRouteInNavigator) { + console.log('goBack', 4); const rootState = navigationRef.getRootState(); const lastRoute = _.last(rootState.routes); // If the user comes from a different flow (there is more than one route in RHP) we should go back to the previous flow on UP button press instead of using the fallbackRoute. @@ -148,18 +152,21 @@ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = f return; } - if(lastRoute.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) { - console.log('lastRoute', lastRoute); - navigationRef.current.goBack(); - return; - } + // if(lastRoute.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) { + // console.log('lastRoute', lastRoute); + // navigationRef.current.goBack(); + // return; + // } } + console.log('goBack', 5); if (shouldEnforceFallback || (isFirstRouteInNavigator && fallbackRoute)) { navigate(fallbackRoute, CONST.NAVIGATION.TYPE.UP); return; } + console.log('goBack', 6); + const isCentralPaneFocused = findFocusedRoute(navigationRef.current.getState()).name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR; const distanceFromPathInRootNavigator = getDistanceFromPathInRootNavigator(fallbackRoute); @@ -169,6 +176,8 @@ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = f return; } + console.log('goBack', 7); + // Add posibility to go back more than one screen in root navigator if that screen is on the stack. if (isCentralPaneFocused && fallbackRoute && distanceFromPathInRootNavigator > 0) { navigationRef.current.dispatch(StackActions.pop(distanceFromPathInRootNavigator)); @@ -176,6 +185,8 @@ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = f } navigationRef.current.goBack(); + + console.log('goBack', 8); } /** diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 63f97f0fe6f8..8b745639147e 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -234,9 +234,6 @@ export default { }, SettingsCentralPane: { screens: { - [SCREENS.SETTINGS.ROOT]: { - path: ROUTES.SETTINGS, - }, [SCREENS.SETTINGS.WORKSPACES]: { path: ROUTES.SETTINGS_WORKSPACES, exact: true, @@ -309,7 +306,7 @@ export default { path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT, exact: true, }, - Settings_Profile: { + [SCREENS.SETTINGS.PROFILE]: { path: ROUTES.SETTINGS_PROFILE, exact: true, }, diff --git a/src/pages/home/sidebar/PressableAvatarWithIndicator.js b/src/pages/home/sidebar/PressableAvatarWithIndicator.js index ddf89321b680..ec2f48cc6a91 100644 --- a/src/pages/home/sidebar/PressableAvatarWithIndicator.js +++ b/src/pages/home/sidebar/PressableAvatarWithIndicator.js @@ -46,7 +46,7 @@ function PressableAvatarWithIndicator({isCreateMenuOpen, currentUserPersonalDeta return; } - Navigation.navigate(ROUTES.SETTINGS); + Navigation.navigate(ROUTES.SETTINGS_HOME); }, [isCreateMenuOpen]); return ( diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 628ca489fc70..caccb1438293 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -382,13 +382,18 @@ function InitialSettingsPage(props) { ); + const navigateBackTo = lodashGet(props.route, 'params.backTo', ROUTES.HOME); + return ( Navigation.goBack()} + onBackButtonPress={() => { + console.log('navigateBackTo', navigateBackTo); + Navigation.goBack(navigateBackTo) + }} > {getMenuItems} From 8703e12c99f6c7bb3dba18e242c65ad2bf9b49ec Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 23 Nov 2023 09:38:07 +0100 Subject: [PATCH 0041/1250] fix closing settings --- src/pages/settings/InitialSettingsPage.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index caccb1438293..1ca111d205a0 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -390,10 +390,7 @@ function InitialSettingsPage(props) { headerContent={headerContent} headerContainerStyles={[styles.staticHeaderImage, styles.justifyContentCenter]} backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.ROOT]} - onBackButtonPress={() => { - console.log('navigateBackTo', navigateBackTo); - Navigation.goBack(navigateBackTo) - }} + onBackButtonPress={() => Navigation.navigate(navigateBackTo)} > {getMenuItems} From 248d664e47bd8c6234b7c63d868d2b9054903c27 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 23 Nov 2023 09:41:10 +0100 Subject: [PATCH 0042/1250] revert --- src/pages/settings/Preferences/PreferencesPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js index 5c056385dd2d..0c02fe9772fb 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.js @@ -45,7 +45,6 @@ function PreferencesPage(props) { return ( From a5ce5bfac7db7f8aca2c0d721f96b5cd8c13db74 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 23 Nov 2023 09:57:55 +0100 Subject: [PATCH 0043/1250] fix deeplink --- .../CustomRouter.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js index 5ac04ab884a4..36faa6816c86 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.js @@ -9,10 +9,7 @@ import SCREENS from '@src/SCREENS'; * @param {String} centralRoute - name of the central route * @returns {Boolean} */ -const isAtLeastOneCentralPaneNavigatorInState = (state, centralRoute) => { - console.log('isAtLeastOneCentralPaneNavigatorInState', centralRoute); - return _.find(state.routes, (r) => r.name === centralRoute) -}; +const isAtLeastOneCentralPaneNavigatorInState = (state, centralRoute) => _.find(state.routes, (r) => r.name === centralRoute); /** * @param {Object} state - react-navigation state @@ -48,9 +45,9 @@ const getTopMostReportIDFromRHP = (state) => { * @param {String} centralRoute - name of the central route */ const addCentralPaneNavigatorRoute = (state, centralRoute) => { - const reportID = getTopMostReportIDFromRHP(state); let centralPaneNavigatorRoute; if (centralRoute === NAVIGATORS.CENTRAL_PANE_NAVIGATOR) { + const reportID = getTopMostReportIDFromRHP(state); centralPaneNavigatorRoute = { name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR, state: { @@ -81,6 +78,17 @@ const addCentralPaneNavigatorRoute = (state, centralRoute) => { state.index = state.routes.length - 1; }; +const addLHPRoute = (state, centralRoute) => { + const settingsHomeRoute = { + name: SCREENS.SETTINGS_HOME, + }; + if (state.routes[0].name !== SCREENS.SETTINGS_HOME && centralRoute === 'SettingsCentralPane') { + state.routes.splice(0, 0, settingsHomeRoute); + // eslint-disable-next-line no-param-reassign + state.index = state.routes.length - 1; + } +} + function CustomRouter(options) { const stackRouter = StackRouter(options); @@ -106,6 +114,7 @@ function CustomRouter(options) { partialState.stale = true; addCentralPaneNavigatorRoute(partialState, centralRoute); } + addLHPRoute(partialState, centralRoute); const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList}); return state; }, From a30391373b3642f9316cc48124231de0952a3a5e Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 23 Nov 2023 10:11:31 +0100 Subject: [PATCH 0044/1250] add better linking --- src/libs/Navigation/linkingConfig.js | 364 +++++++++++++-------------- 1 file changed, 180 insertions(+), 184 deletions(-) diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 8b745639147e..a3ebd12838be 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -36,6 +36,178 @@ export default { [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: { screens: { + Settings: { + screens: { + Settings_Preferences_PriorityMode: { + path: ROUTES.SETTINGS_PRIORITY_MODE, + exact: true, + }, + Settings_Preferences_Language: { + path: ROUTES.SETTINGS_LANGUAGE, + exact: true, + }, + Settings_Preferences_Theme: { + path: ROUTES.SETTINGS_THEME, + exact: true, + }, + Settings_Close: { + path: ROUTES.SETTINGS_CLOSE, + exact: true, + }, + Settings_Wallet_DomainCards: { + path: ROUTES.SETTINGS_WALLET_DOMAINCARD.route, + exact: true, + }, + Settings_Wallet_ReportVirtualCardFraud: { + path: ROUTES.SETTINGS_REPORT_FRAUD.route, + exact: true, + }, + Settings_Wallet_EnablePayments: { + path: ROUTES.SETTINGS_ENABLE_PAYMENTS, + exact: true, + }, + Settings_Wallet_Transfer_Balance: { + path: ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE, + exact: true, + }, + Settings_Wallet_Choose_Transfer_Account: { + path: ROUTES.SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT, + exact: true, + }, + Settings_ReportCardLostOrDamaged: { + path: ROUTES.SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED.route, + exact: true, + }, + Settings_Wallet_Card_Activate: { + path: ROUTES.SETTINGS_WALLET_CARD_ACTIVATE.route, + exact: true, + }, + Settings_Wallet_Cards_Digital_Details_Update_Address: { + path: ROUTES.SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS.route, + exact: true, + }, + Settings_Add_Debit_Card: { + path: ROUTES.SETTINGS_ADD_DEBIT_CARD, + exact: true, + }, + Settings_Add_Bank_Account: { + path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT, + exact: true, + }, + Settings_Pronouns: { + path: ROUTES.SETTINGS_PRONOUNS, + exact: true, + }, + Settings_Display_Name: { + path: ROUTES.SETTINGS_DISPLAY_NAME, + exact: true, + }, + Settings_Timezone: { + path: ROUTES.SETTINGS_TIMEZONE, + exact: true, + }, + Settings_Timezone_Select: { + path: ROUTES.SETTINGS_TIMEZONE_SELECT, + exact: true, + }, + Settings_App_Download_Links: { + path: ROUTES.SETTINGS_APP_DOWNLOAD_LINKS, + exact: true, + }, + Settings_ContactMethods: { + path: ROUTES.SETTINGS_CONTACT_METHODS.route, + exact: true, + }, + Settings_ContactMethodDetails: { + path: ROUTES.SETTINGS_CONTACT_METHOD_DETAILS.route, + }, + Settings_Lounge_Access: { + path: ROUTES.SETTINGS_LOUNGE_ACCESS, + }, + Settings_NewContactMethod: { + path: ROUTES.SETTINGS_NEW_CONTACT_METHOD, + exact: true, + }, + Settings_PersonalDetails_Initial: { + path: ROUTES.SETTINGS_PERSONAL_DETAILS, + exact: true, + }, + Settings_PersonalDetails_LegalName: { + path: ROUTES.SETTINGS_PERSONAL_DETAILS_LEGAL_NAME, + exact: true, + }, + Settings_PersonalDetails_DateOfBirth: { + path: ROUTES.SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH, + exact: true, + }, + Settings_PersonalDetails_Address: { + path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS, + exact: true, + }, + Settings_PersonalDetails_Address_Country: { + path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.route, + exact: true, + }, + Settings_TwoFactorAuth: { + path: ROUTES.SETTINGS_2FA, + exact: true, + }, + [SCREENS.SETTINGS.STATUS]: { + path: ROUTES.SETTINGS_STATUS, + exact: true, + }, + Settings_Status_Set: { + path: ROUTES.SETTINGS_STATUS_SET, + exact: true, + }, + Workspace_Initial: { + path: ROUTES.WORKSPACE_INITIAL.route, + }, + Workspace_Settings: { + path: ROUTES.WORKSPACE_SETTINGS.route, + }, + Workspace_Settings_Currency: { + path: ROUTES.WORKSPACE_SETTINGS_CURRENCY.route, + }, + Workspace_Card: { + path: ROUTES.WORKSPACE_CARD.route, + }, + Workspace_Reimburse: { + path: ROUTES.WORKSPACE_REIMBURSE.route, + }, + Workspace_RateAndUnit: { + path: ROUTES.WORKSPACE_RATE_AND_UNIT.route, + }, + Workspace_Bills: { + path: ROUTES.WORKSPACE_BILLS.route, + }, + Workspace_Invoices: { + path: ROUTES.WORKSPACE_INVOICES.route, + }, + Workspace_Travel: { + path: ROUTES.WORKSPACE_TRAVEL.route, + }, + Workspace_Members: { + path: ROUTES.WORKSPACE_MEMBERS.route, + }, + Workspace_Invite: { + path: ROUTES.WORKSPACE_INVITE.route, + }, + Workspace_Invite_Message: { + path: ROUTES.WORKSPACE_INVITE_MESSAGE.route, + }, + ReimbursementAccount: { + path: ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.route, + exact: true, + }, + GetAssistance: { + path: ROUTES.GET_ASSISTANCE.route, + }, + KeyboardShortcuts: { + path: ROUTES.KEYBOARD_SHORTCUTS, + }, + } + }, Private_Notes: { screens: { PrivateNotes_View: ROUTES.PRIVATE_NOTES_VIEW.route, @@ -234,28 +406,20 @@ export default { }, SettingsCentralPane: { screens: { - [SCREENS.SETTINGS.WORKSPACES]: { - path: ROUTES.SETTINGS_WORKSPACES, - exact: true, - }, - [SCREENS.SETTINGS.PREFERENCES]: { - path: ROUTES.SETTINGS_PREFERENCES, - exact: true, - }, - Settings_Preferences_PriorityMode: { - path: ROUTES.SETTINGS_PRIORITY_MODE, + Settings_Share_Code: { + path: ROUTES.SETTINGS_SHARE_CODE, exact: true, }, - Settings_Preferences_Language: { - path: ROUTES.SETTINGS_LANGUAGE, + [SCREENS.SETTINGS.WORKSPACES]: { + path: ROUTES.SETTINGS_WORKSPACES, exact: true, }, - Settings_Preferences_Theme: { - path: ROUTES.SETTINGS_THEME, + [SCREENS.SETTINGS.PROFILE]: { + path: ROUTES.SETTINGS_PROFILE, exact: true, }, - Settings_Close: { - path: ROUTES.SETTINGS_CLOSE, + [SCREENS.SETTINGS.PREFERENCES]: { + path: ROUTES.SETTINGS_PREFERENCES, exact: true, }, [SCREENS.SETTINGS.SECURITY]: { @@ -266,180 +430,12 @@ export default { path: ROUTES.SETTINGS_WALLET, exact: true, }, - Settings_Wallet_DomainCards: { - path: ROUTES.SETTINGS_WALLET_DOMAINCARD.route, - exact: true, - }, - Settings_Wallet_ReportVirtualCardFraud: { - path: ROUTES.SETTINGS_REPORT_FRAUD.route, - exact: true, - }, - Settings_Wallet_EnablePayments: { - path: ROUTES.SETTINGS_ENABLE_PAYMENTS, - exact: true, - }, - Settings_Wallet_Transfer_Balance: { - path: ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE, - exact: true, - }, - Settings_Wallet_Choose_Transfer_Account: { - path: ROUTES.SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT, - exact: true, - }, - Settings_ReportCardLostOrDamaged: { - path: ROUTES.SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED.route, - exact: true, - }, - Settings_Wallet_Card_Activate: { - path: ROUTES.SETTINGS_WALLET_CARD_ACTIVATE.route, - exact: true, - }, - Settings_Wallet_Cards_Digital_Details_Update_Address: { - path: ROUTES.SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS.route, - exact: true, - }, - Settings_Add_Debit_Card: { - path: ROUTES.SETTINGS_ADD_DEBIT_CARD, - exact: true, - }, - Settings_Add_Bank_Account: { - path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT, - exact: true, - }, - [SCREENS.SETTINGS.PROFILE]: { - path: ROUTES.SETTINGS_PROFILE, - exact: true, - }, - Settings_Pronouns: { - path: ROUTES.SETTINGS_PRONOUNS, - exact: true, - }, - Settings_Display_Name: { - path: ROUTES.SETTINGS_DISPLAY_NAME, - exact: true, - }, - Settings_Timezone: { - path: ROUTES.SETTINGS_TIMEZONE, - exact: true, - }, - Settings_Timezone_Select: { - path: ROUTES.SETTINGS_TIMEZONE_SELECT, - exact: true, - }, Settings_About: { path: ROUTES.SETTINGS_ABOUT, exact: true, }, - Settings_App_Download_Links: { - path: ROUTES.SETTINGS_APP_DOWNLOAD_LINKS, - exact: true, - }, - Settings_ContactMethods: { - path: ROUTES.SETTINGS_CONTACT_METHODS.route, - exact: true, - }, - Settings_ContactMethodDetails: { - path: ROUTES.SETTINGS_CONTACT_METHOD_DETAILS.route, - }, - Settings_Lounge_Access: { - path: ROUTES.SETTINGS_LOUNGE_ACCESS, - }, - Settings_NewContactMethod: { - path: ROUTES.SETTINGS_NEW_CONTACT_METHOD, - exact: true, - }, - Settings_PersonalDetails_Initial: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS, - exact: true, - }, - Settings_PersonalDetails_LegalName: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_LEGAL_NAME, - exact: true, - }, - Settings_PersonalDetails_DateOfBirth: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH, - exact: true, - }, - Settings_PersonalDetails_Address: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS, - exact: true, - }, - Settings_PersonalDetails_Address_Country: { - path: ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.route, - exact: true, - }, - Settings_TwoFactorAuth: { - path: ROUTES.SETTINGS_2FA, - exact: true, - }, - Settings_Share_Code: { - path: ROUTES.SETTINGS_SHARE_CODE, - exact: true, - }, - [SCREENS.SETTINGS.STATUS]: { - path: ROUTES.SETTINGS_STATUS, - exact: true, - }, - Settings_Status_Set: { - path: ROUTES.SETTINGS_STATUS_SET, - exact: true, - }, - Workspace_Initial: { - path: ROUTES.WORKSPACE_INITIAL.route, - }, - Workspace_Settings: { - path: ROUTES.WORKSPACE_SETTINGS.route, - }, - Workspace_Settings_Currency: { - path: ROUTES.WORKSPACE_SETTINGS_CURRENCY.route, - }, - Workspace_Card: { - path: ROUTES.WORKSPACE_CARD.route, - }, - Workspace_Reimburse: { - path: ROUTES.WORKSPACE_REIMBURSE.route, - }, - Workspace_RateAndUnit: { - path: ROUTES.WORKSPACE_RATE_AND_UNIT.route, - }, - Workspace_Bills: { - path: ROUTES.WORKSPACE_BILLS.route, - }, - Workspace_Invoices: { - path: ROUTES.WORKSPACE_INVOICES.route, - }, - Workspace_Travel: { - path: ROUTES.WORKSPACE_TRAVEL.route, - }, - Workspace_Members: { - path: ROUTES.WORKSPACE_MEMBERS.route, - }, - Workspace_Invite: { - path: ROUTES.WORKSPACE_INVITE.route, - }, - Workspace_Invite_Message: { - path: ROUTES.WORKSPACE_INVITE_MESSAGE.route, - }, - ReimbursementAccount: { - path: ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.route, - exact: true, - }, - GetAssistance: { - path: ROUTES.GET_ASSISTANCE.route, - }, - KeyboardShortcuts: { - path: ROUTES.KEYBOARD_SHORTCUTS, - }, }, }, - // Settings: { - // screens: { - // [SCREENS.SETTINGS_NEW_PROFILE]: { - // path: ROUTES.SETTINGS_NEW_PROFILE, - // exact: true, - // }, - // } - // } }, } }, From f24f917933ccbda3a92e95bd0508932f62684f88 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 23 Nov 2023 10:30:07 +0100 Subject: [PATCH 0045/1250] general refactor --- src/components/HeaderWithBackButton/index.js | 7 +++++-- src/pages/ShareCodePage.js | 1 + src/pages/settings/AboutPage/AboutPage.js | 1 + src/pages/settings/Preferences/PreferencesPage.js | 1 + src/pages/settings/Profile/LoungeAccessPage.js | 2 +- src/pages/settings/Profile/ProfilePage.js | 1 + src/pages/settings/Security/SecuritySettingsPage.js | 1 + src/pages/settings/Wallet/WalletPage/WalletPage.js | 1 + src/pages/workspace/WorkspacesListPage.js | 1 + 9 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js index edb3b8d26831..528ddb3ad310 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import {Keyboard, View} from 'react-native'; +import { Keyboard, View } from 'react-native'; import AvatarWithDisplayName from '@components/AvatarWithDisplayName'; import Header from '@components/Header'; import Icon from '@components/Icon'; @@ -18,6 +18,7 @@ import * as StyleUtils from '@styles/StyleUtils'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import headerWithBackButtonPropTypes from './headerWithBackButtonPropTypes'; function HeaderWithBackButton({ @@ -40,6 +41,7 @@ function HeaderWithBackButton({ shouldShowPinButton = false, shouldShowThreeDotsButton = false, shouldDisableThreeDotsButton = false, + shouldShowBackButtonOnlyOnMobile = false, stepCounter = null, subtitle = '', title = '', @@ -59,6 +61,7 @@ function HeaderWithBackButton({ const {translate} = useLocalize(); const {isKeyboardShown} = useKeyboardState(); const waitForNavigate = useWaitForNavigation(); + const {isSmallScreenWidth} = useWindowDimensions(); return ( - {shouldShowBackButton && ( + {shouldShowBackButton && (!shouldShowBackButtonOnlyOnMobile || isSmallScreenWidth) && ( { diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index d8322b8c58d5..05a3578557f4 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -82,6 +82,7 @@ class ShareCodePage extends React.Component { Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(this.props.report.reportID) : ROUTES.SETTINGS)} + shouldShowBackButtonOnlyOnMobile /> diff --git a/src/pages/settings/AboutPage/AboutPage.js b/src/pages/settings/AboutPage/AboutPage.js index a88a07117f08..59adb9a7befc 100644 --- a/src/pages/settings/AboutPage/AboutPage.js +++ b/src/pages/settings/AboutPage/AboutPage.js @@ -104,6 +104,7 @@ function AboutPage(props) { Navigation.goBack(ROUTES.SETTINGS)} + shouldShowBackButtonOnlyOnMobile /> diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js index 0c02fe9772fb..cf2aee9d930d 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.js @@ -47,6 +47,7 @@ function PreferencesPage(props) { title={translate('common.preferences')} backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.PREFERENCES]} illustration={LottieAnimations.PreferencesDJ} + shouldShowBackButtonOnlyOnMobile > Navigation.goBack(ROUTES.SETTINGS)} + onBackButtonPress={() => Navigation.goBack(ROUTES)} illustration={LottieAnimations.ExpensifyLounge} > Navigation.goBack(ROUTES.SETTINGS)} + shouldShowBackButtonOnlyOnMobile /> diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.js b/src/pages/settings/Wallet/WalletPage/WalletPage.js index 23d4112eea21..2a6ae9dc7105 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.js +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.js @@ -326,6 +326,7 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen Navigation.goBack(ROUTES.SETTINGS)} + shouldShowBackButtonOnlyOnMobile /> diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.js index dc0c6d8b3043..dbb73d9dd300 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.js @@ -189,6 +189,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, u illustration={LottieAnimations.WorkspacePlanet} onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS)} title={translate('common.workspaces')} + shouldShowBackButtonOnlyOnMobile footer={ ); @@ -73,7 +72,6 @@ function ChronosOOOListActions(props) { ); } -ChronosOOOListActions.propTypes = propTypes; ChronosOOOListActions.displayName = 'ChronosOOOListActions'; -export default withLocalize(ChronosOOOListActions); +export default ChronosOOOListActions; diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 80eae24d9367..e7eaca2aab6a 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -210,7 +210,7 @@ function getZoneAbbreviation(datetime: string, selectedTimezone: SelectedTimezon * * @returns Sunday, July 9, 2023 */ -function formatToLongDateWithWeekday(datetime: string): string { +function formatToLongDateWithWeekday(datetime: string | Date): string { return format(new Date(datetime), CONST.DATE.LONG_DATE_FORMAT_WITH_WEEKDAY); } @@ -228,7 +228,7 @@ function formatToDayOfWeek(datetime: string): string { * * @returns 2:30 PM */ -function formatToLocalTime(datetime: string): string { +function formatToLocalTime(datetime: string | Date): string { return format(new Date(datetime), CONST.DATE.LOCAL_TIME_FORMAT); } diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index fc7f8eb8ba31..1b949ac48a3f 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -234,4 +234,16 @@ type OriginalMessage = | OriginalMessageMoved; export default OriginalMessage; -export type {ChronosOOOEvent, Decision, Reaction, ActionName, IOUMessage, Closed, OriginalMessageActionName, ChangeLog, OriginalMessageIOU, OriginalMessageCreated}; +export type { + ChronosOOOEvent, + Decision, + Reaction, + ActionName, + IOUMessage, + Closed, + OriginalMessageActionName, + ChangeLog, + OriginalMessageIOU, + OriginalMessageCreated, + OriginalMessageChronosOOOList, +}; From 1e162ea851278c4c4df66f5ece28d44d5068b6ff Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:29:00 +0100 Subject: [PATCH 0118/1250] move avatar picker --- src/pages/settings/InitialSettingsPage.js | 29 ++++++++++++++++++----- src/pages/settings/Profile/ProfilePage.js | 22 ----------------- src/styles/styles.ts | 6 +++++ 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 63718da5c75e..65c3c6bc3427 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -5,7 +5,7 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import Avatar from '@components/Avatar'; +import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import bankAccountPropTypes from '@components/bankAccountPropTypes'; import cardPropTypes from '@components/cardPropTypes'; import ConfirmModal from '@components/ConfirmModal'; @@ -35,6 +35,7 @@ import useTheme from '@styles/themes/useTheme'; import useThemeStyles from '@styles/useThemeStyles'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; +import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Session from '@userActions/Session'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; @@ -277,6 +278,10 @@ function InitialSettingsPage(props) { const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]); const generalMenuItems = useMemo(() => getMenuItemsSection(generaltMenuItemsData), [generaltMenuItemsData, getMenuItemsSection]); + const currentUserDetails = props.currentUserPersonalDetails || {}; + const avatarURL = lodashGet(currentUserDetails, 'avatar', ''); + const accountID = lodashGet(currentUserDetails, 'accountID', ''); + const headerContent = ( {_.isEmpty(props.currentUserPersonalDetails) || _.isUndefined(props.currentUserPersonalDetails.displayName) ? ( @@ -292,11 +297,23 @@ function InitialSettingsPage(props) { role={CONST.ACCESSIBILITY_ROLE.BUTTON} > - diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index efbef57dd920..b9698702503c 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -4,7 +4,6 @@ import React, {useEffect} from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; @@ -19,7 +18,6 @@ import * as UserUtils from '@libs/UserUtils'; import userPropTypes from '@pages/settings/userPropTypes'; import useThemeStyles from '@styles/useThemeStyles'; import * as App from '@userActions/App'; -import * as PersonalDetails from '@userActions/PersonalDetails'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -66,8 +64,6 @@ function ProfilePage(props) { }; const currentUserDetails = props.currentUserPersonalDetails || {}; const contactMethodBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList); - const avatarURL = lodashGet(currentUserDetails, 'avatar', ''); - const accountID = lodashGet(currentUserDetails, 'accountID', ''); const emojiCode = lodashGet(props, 'currentUserPersonalDetails.status.emojiCode', ''); const profileSettingsOptions = [ @@ -116,24 +112,6 @@ function ProfilePage(props) { shouldUseCentralPaneView /> - {_.map(profileSettingsOptions, (detail, index) => ( vertical: windowHeight - 75, } satisfies AnchorPosition), + createAccountMenuPositionProfile: () => + ({ + horizontal: 18, + ...getPopOverVerticalOffset(162), + } satisfies AnchorPosition), + createMenuPositionProfile: (windowWidth: number) => ({ horizontal: windowWidth - 355, From f2ddfa2858406c9defe6dc92b326c59931211666 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:34:08 +0100 Subject: [PATCH 0119/1250] add bottom borders --- src/pages/ShareCodePage.js | 1 + src/pages/settings/Profile/ProfilePage.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index e734ce4ec16e..8a3ba71082a5 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -83,6 +83,7 @@ class ShareCodePage extends React.Component { title={this.props.translate('common.shareCode')} onBackButtonPress={() => Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(this.props.report.reportID) : ROUTES.SETTINGS_HOME)} shouldUseCentralPaneView + shouldShowBorderBottom /> diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index b9698702503c..197de876cbdc 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -110,6 +110,7 @@ function ProfilePage(props) { title={props.translate('common.profile')} onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_HOME)} shouldUseCentralPaneView + shouldShowBorderBottom /> From 00925563e1f756a8481b88c114efa3e94efa90dc Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:06:25 +0100 Subject: [PATCH 0120/1250] complete merge with main --- .../AppNavigator/ModalStackNavigators.tsx | 14 +++++++------- .../CustomFullScreenRouter.tsx | 2 +- .../createCustomStackNavigator/CustomRouter.ts | 10 ++-------- src/libs/Navigation/linkingConfig.ts | 4 ++-- src/libs/Navigation/types.ts | 10 +++++----- 5 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index eda0b95b911a..622e761018a0 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -28,10 +28,10 @@ import type { TeachersUniteNavigatorParamList, WalletStatementNavigatorParamList, } from '@navigation/types'; -import styles from '@styles/styles'; import useThemeStyles from '@styles/useThemeStyles'; import SCREENS from '@src/SCREENS'; import type {Screen} from '@src/SCREENS'; +import { ThemeStyles } from '@styles/styles'; type Screens = Partial React.ComponentType>>; @@ -40,7 +40,7 @@ type Screens = Partial React.ComponentType>>; * * @param screens key/value pairs where the key is the name of the screen and the value is a functon that returns the lazy-loaded component */ -function createModalStackNavigator(screens: Screens, screenOptions?: StackNavigationOptions): React.ComponentType { +function createModalStackNavigator(screens: Screens, getScreenOptions?: (styles: ThemeStyles) => StackNavigationOptions): React.ComponentType { const ModalStackNavigator = createStackNavigator(); function ModalStack() { @@ -56,7 +56,7 @@ function createModalStackNavigator(screens: ); return ( - + {Object.keys(screens as Required).map((name) => ( require('../../../pages/workspace/WorkspacesListPage').default as React.ComponentType, - [SCREENS.SETTINGS.PREFERENCES]: () => require('../../../pages/settings/Preferences/PreferencesPage').default as React.ComponentType, + [SCREENS.SETTINGS.PREFERENCES.ROOT]: () => require('../../../pages/settings/Preferences/PreferencesPage').default as React.ComponentType, [SCREENS.SETTINGS.SECURITY]: () => require('../../../pages/settings/Security/SecuritySettingsPage').default as React.ComponentType, - [SCREENS.SETTINGS.PROFILE]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType, + [SCREENS.SETTINGS.PROFILE.ROOT]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType, [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType, - [SCREENS.SETTINGS.WALLET]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType, + [SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType, [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType, }, - {cardStyle: styles.navigationScreenCardStyle, headerShown: false}, + (styles) => ({cardStyle: styles.navigationScreenCardStyle, headerShown: false}), ); const SettingsModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx index 5b15e2b1f3ad..5e402162a349 100644 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx @@ -20,7 +20,7 @@ const addCentralPaneNavigatorRoute = (state: State) => { state: { routes: [ { - name: SCREENS.SETTINGS.PROFILE, + name: SCREENS.SETTINGS.PROFILE.ROOT, }, ], }, diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index 797afd3337d9..738cd8ec9de3 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -59,13 +59,7 @@ const addCentralPaneNavigatorRoute = (state: State) => { }; const mapScreenNameToSettingsScreenName: Record = { - [SCREENS.SETTINGS.DISPLAY_NAME]: SCREENS.SETTINGS.PROFILE, - [SCREENS.SETTINGS.CONTACT_METHODS]: SCREENS.SETTINGS.PROFILE, - [SCREENS.SETTINGS.CONTACT_METHOD_DETAILS]: SCREENS.SETTINGS.PROFILE, - [SCREENS.SETTINGS.SECURITY]: SCREENS.SETTINGS.SECURITY, - [SCREENS.SETTINGS.PREFERENCES_LANGUAGE]: SCREENS.SETTINGS.PREFERENCES, - [SCREENS.SETTINGS.PREFERENCES_THEME]: SCREENS.SETTINGS.PREFERENCES, - [SCREENS.SETTINGS.PREFERENCES_PRIORITY_MODE]: SCREENS.SETTINGS.PREFERENCES, + // [SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: SCREENS.SETTINGS.PROFILE, }; const handleSettingsOpened = (state: State) => { @@ -89,7 +83,7 @@ const handleSettingsOpened = (state: State) => { return; } - const settingsHomeRouteName = mapScreenNameToSettingsScreenName[settingsScreenName] || SCREENS.SETTINGS.PROFILE; + const settingsHomeRouteName = mapScreenNameToSettingsScreenName[settingsScreenName] || SCREENS.SETTINGS.PROFILE.ROOT; const fullScreenRoute = { name: NAVIGATORS.FULL_SCREEN_NAVIGATOR, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index d7a5312668b1..dd0b328a3222 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -472,7 +472,7 @@ const linkingConfig: LinkingOptions = { path: ROUTES.SETTINGS_WORKSPACES, exact: true, }, - [SCREENS.SETTINGS.PROFILE.STATUS]: { + [SCREENS.SETTINGS.PROFILE.ROOT]: { path: ROUTES.SETTINGS_PROFILE, exact: true, }, @@ -484,7 +484,7 @@ const linkingConfig: LinkingOptions = { path: ROUTES.SETTINGS_SECURITY, exact: true, }, - Settings_Wallet: { + [SCREENS.SETTINGS.WALLET.ROOT]: { path: ROUTES.SETTINGS_WALLET, exact: true, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 0887d22d4f1e..f6a10298530f 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -360,13 +360,13 @@ type RightModalNavigatorParamList = { }; type SettingsCentralPaneNavigatorParamList = { - Settings_Share_Code: undefined; + [SCREENS.SETTINGS.SHARE_CODE]: undefined; [SCREENS.SETTINGS.WORKSPACES]: undefined; - [SCREENS.SETTINGS.PROFILE]: undefined; - [SCREENS.SETTINGS.PREFERENCES]: undefined; + [SCREENS.SETTINGS.PROFILE.ROOT]: undefined; + [SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined; [SCREENS.SETTINGS.SECURITY]: undefined; - Settings_Wallet: undefined; - Settings_About: undefined; + [SCREENS.SETTINGS.WALLET.ROOT]: undefined; + [SCREENS.SETTINGS.ABOUT]: undefined; }; type FullScreenNavigatorParamList = { From 307e668ad6e2c77f435b3313f3e15bb6405543d9 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:33:19 +0100 Subject: [PATCH 0121/1250] fix issues --- src/components/AvatarWithImagePicker.js | 10 +++++++--- src/components/IllustratedHeaderPageLayout.js | 1 - src/components/QRShare/index.js | 2 +- .../Navigation/AppNavigator/ModalStackNavigators.tsx | 2 +- .../CustomFullScreenRouter.tsx | 2 -- src/pages/settings/AboutPage/AboutPage.js | 1 + src/pages/settings/InitialSettingsPage.js | 3 ++- src/pages/workspace/WorkspaceSettingsPage.js | 1 + 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index 9b061ba5c670..11d0e4e7b45c 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -94,6 +94,9 @@ const propTypes = { horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)), }), + + /** Style applied to the avatar */ + avatarStyle: stylePropTypes.isRequired, }; const defaultProps = { @@ -141,6 +144,7 @@ function AvatarWithImagePicker({ anchorAlignment, onImageSelected, editorMaskImage, + avatarStyle, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -287,7 +291,7 @@ function AvatarWithImagePicker({ return ( - + {source ? ( React.ComponentType>>; diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx index 5e402162a349..fbd62595f5b6 100644 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx @@ -11,8 +11,6 @@ const isAtLeastOneInState = (state: State, screenName: string): boolean => !!sta /** * Adds report route without any specific reportID to the state. * The report screen will self set proper reportID param based on the helper function findLastAccessedReport (look at ReportScreenWrapper for more info) - * - * @param {Object} state - react-navigation state */ const addCentralPaneNavigatorRoute = (state: State) => { const centralPaneNavigatorRoute = { diff --git a/src/pages/settings/AboutPage/AboutPage.js b/src/pages/settings/AboutPage/AboutPage.js index ac56758080cc..ac96c802d597 100644 --- a/src/pages/settings/AboutPage/AboutPage.js +++ b/src/pages/settings/AboutPage/AboutPage.js @@ -105,6 +105,7 @@ function AboutPage(props) { title={props.translate('initialSettingsPage.about')} onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_HOME)} shouldUseCentralPaneView + shouldShowBorderBottom /> diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 0c9849e71ec7..6513d2e2b05e 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -301,7 +301,8 @@ function InitialSettingsPage(props) { onImageRemoved={PersonalDetails.deleteAvatar} anchorPosition={styles.createAccountMenuPositionProfile(props.windowWidth)} anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP}} - size={CONST.AVATAR_SIZE.LARGE} + size={CONST.AVATAR_SIZE.XLARGE} + avatarStyle={styles.avatarXLarge} pendingAction={lodashGet(props.currentUserPersonalDetails, 'pendingFields.avatar', null)} errors={lodashGet(props.currentUserPersonalDetails, 'errorFields.avatar', null)} errorRowStyles={[styles.mt6]} diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js index d121134f26da..5f4e22711a5c 100644 --- a/src/pages/workspace/WorkspaceSettingsPage.js +++ b/src/pages/workspace/WorkspaceSettingsPage.js @@ -113,6 +113,7 @@ function WorkspaceSettingsPage({policy, currencyList, windowWidth, route}) { ( Date: Wed, 13 Dec 2023 17:53:25 +0300 Subject: [PATCH 0122/1250] minor fix --- src/pages/PrivateNotes/PrivateNotesEditPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index cb8d079f29e9..966088b66ea2 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -54,6 +54,7 @@ const defaultProps = { function PrivateNotesEditPage({route, personalDetailsList, report}) { const styles = useThemeStyles(); const {translate} = useLocalize(); + // We need to edit the note in markdown format, but display it in HTML format const parser = new ExpensiMark(); const [privateNote, setPrivateNote] = useState( From 520186968b576a49e179e9751da6c50ab3f3c418 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 12 Dec 2023 16:30:06 +0100 Subject: [PATCH 0123/1250] add const for bottom tab --- src/CONST.ts | 14 ++++++++++++++ src/NAVIGATORS.ts | 1 + src/ROUTES.ts | 2 ++ src/SCREENS.ts | 1 + 4 files changed, 18 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index 072f780b54ae..fc355887d833 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3018,6 +3018,20 @@ const CONST = { DEFAULT: 5, CAROUSEL: 3, }, + + TAB_TO_CENTRAL_PANE_MAPPING: { + [SCREENS.HOME]: [SCREENS.REPORT], + [SCREENS.ALL_SETTINGS]: [SCREENS.SETTINGS.WORKSPACES], + [SCREENS.WORKSPACE.INITIAL]: [ + SCREENS.WORKSPACE.SETTINGS, + SCREENS.WORKSPACE.CARD, + SCREENS.WORKSPACE.REIMBURSE, + SCREENS.WORKSPACE.BILLS, + SCREENS.WORKSPACE.INVOICES, + SCREENS.WORKSPACE.TRAVEL, + SCREENS.WORKSPACE.MEMBERS, + ], + }, } as const; export default CONST; diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index a3a041e65684..4bc11c5e1856 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -4,6 +4,7 @@ * */ export default { CENTRAL_PANE_NAVIGATOR: 'CentralPaneNavigator', + BOTTOM_TAB_NAVIGATOR: 'BottomTabNavigator', RIGHT_MODAL_NAVIGATOR: 'RightModalNavigator', FULL_SCREEN_NAVIGATOR: 'FullScreenNavigator', } as const; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 425ff73af56b..44563b74d48e 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -14,6 +14,8 @@ function getUrlWithBackToParam(url: TUrl, backTo?: string): const ROUTES = { HOME: '', + ALL_SETTINGS: 'all-settings', + // This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated CONCIERGE: 'concierge', FLAG_COMMENT: { diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 921f57953482..44f11c059321 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -12,6 +12,7 @@ const PROTECTED_SCREENS = { const SCREENS = { ...PROTECTED_SCREENS, + ALL_SETTINGS: 'AllSettings', REPORT: 'Report', NOT_FOUND: 'not-found', TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', From 4f6e8691d30b844dbb94640718a076d393a6814b Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 12 Dec 2023 16:30:35 +0100 Subject: [PATCH 0124/1250] patch for react-navigation --- .../@react-navigation+stack+6.3.16+002+dontDetachScreen.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch index d64fc4fecf74..f7e068686b36 100644 --- a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch +++ b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch @@ -43,7 +43,7 @@ index 7558eb3..b7bb75e 100644 }) : STATE_TRANSITIONING_OR_BELOW_TOP; } + -+ const isHomeScreenAndNotOnTop = route.name === 'Home' && isScreenActive !== STATE_ON_TOP; ++ const isHomeScreenAndNotOnTop = route.name === 'BottomTabNavigator' && isScreenActive !== STATE_ON_TOP; + const { headerShown = true, From 1592ff8bd153ded9f55a2577fdcff014c9d14895 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 12 Dec 2023 16:30:56 +0100 Subject: [PATCH 0125/1250] styles for bottom tab navigator --- src/styles/styles.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index fb851ecfcf54..7e60c9f79629 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -474,6 +474,16 @@ const styles = (theme: ThemeColors) => borderRadius: variables.buttonBorderRadius, }, + bottomTabBarContainer: { + height: 80, + borderTopWidth: 1, + borderTopColor: theme.border, + backgroundColor: theme.appBG, + flexDirection: 'row', + justifyContent: 'space-around', + alignItems: 'center', + }, + button: { backgroundColor: theme.buttonDefaultBG, borderRadius: variables.buttonBorderRadius, @@ -1333,15 +1343,6 @@ const styles = (theme: ThemeColors) => zIndex: 10, } satisfies ViewStyle), - floatingActionButtonContainer: { - position: 'absolute', - right: 20, - - // The bottom of the floating action button should align with the bottom of the compose box. - // The value should be equal to the height + marginBottom + marginTop of chatItemComposeSecondaryRow - bottom: variables.fabBottom, - }, - floatingActionButton: { backgroundColor: theme.success, height: variables.componentSizeLarge, From 4696731a75342ad6d7593b97c0bd84e7b7bbd773 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 16:05:58 +0100 Subject: [PATCH 0126/1250] public bottom tab navigator --- .../Navigators/PublicBottomTabNavigator.tsx | 28 +++++++++++++++++++ .../Navigation/AppNavigator/PublicScreens.tsx | 4 ++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx diff --git a/src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx new file mode 100644 index 000000000000..6ed58c178c2c --- /dev/null +++ b/src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx @@ -0,0 +1,28 @@ +import {StackNavigationOptions} from '@react-navigation/stack'; +import React from 'react'; +import createCustomBottomTabNavigator from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator'; +import {BottomTabNavigatorParamList} from '@libs/Navigation/types'; +import SignInPage from '@pages/signin/SignInPage'; +import SCREENS from '@src/SCREENS'; + +const Tab = createCustomBottomTabNavigator(); + +const screenOptions: StackNavigationOptions = { + headerShown: false, +}; + +// The structure for the HOME route have to be the same in public and auth screens. That's why we need to wrap the HOME screen with "fake" bottomTabNavigator. +function PublicBottomTabNavigator() { + return ( + + + + ); +} + +PublicBottomTabNavigator.displayName = 'BottomTabNavigator'; + +export default PublicBottomTabNavigator; diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.tsx b/src/libs/Navigation/AppNavigator/PublicScreens.tsx index 5c3171214bd9..0324cc85dcfa 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.tsx +++ b/src/libs/Navigation/AppNavigator/PublicScreens.tsx @@ -8,6 +8,7 @@ import SAMLSignInPage from '@pages/signin/SAMLSignInPage'; import SignInPage from '@pages/signin/SignInPage'; import UnlinkLoginPage from '@pages/UnlinkLoginPage'; import ValidateLoginPage from '@pages/ValidateLoginPage'; +import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import defaultScreenOptions from './defaultScreenOptions'; @@ -16,8 +17,9 @@ const RootStack = createStackNavigator(); function PublicScreens() { return ( + {/* The structure for the HOME route have to be the same in public and auth screens. That's why we need to wrap the HOME screen with "fake" bottomTabNavigator. */} From 0f61ed9e9f7d2a01468ee0359883c863277648dd Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 16:13:30 +0100 Subject: [PATCH 0127/1250] move workspace screens --- .../AppNavigator/ModalStackNavigators.tsx | 10 ----- .../BaseCentralPaneNavigator.tsx | 37 +++++++++++++++---- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 29449f52ecd6..c63dd80f7621 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -180,7 +180,6 @@ const NewTeachersUniteNavigator = createModalStackNavigator({ [SCREENS.SETTINGS.ROOT]: () => require('../../../pages/settings/InitialSettingsPage').default as React.ComponentType, [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType, - [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../pages/workspace/WorkspacesListPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.ROOT]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.PRONOUNS]: () => require('../../../pages/settings/Profile/PronounsPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.DISPLAY_NAME]: () => require('../../../pages/settings/Profile/DisplayNamePage').default as React.ComponentType, @@ -220,16 +219,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/AddPersonalBankAccountPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.STATUS]: () => require('../../../pages/settings/Profile/CustomStatus/StatusPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.STATUS_SET]: () => require('../../../pages/settings/Profile/CustomStatus/StatusSetPage').default as React.ComponentType, - [SCREENS.WORKSPACE.INITIAL]: () => require('../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType, - [SCREENS.WORKSPACE.SETTINGS]: () => require('../../../pages/workspace/WorkspaceSettingsPage').default as React.ComponentType, - [SCREENS.WORKSPACE.CURRENCY]: () => require('../../../pages/workspace/WorkspaceSettingsCurrencyPage').default as React.ComponentType, - [SCREENS.WORKSPACE.CARD]: () => require('../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType, - [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType, [SCREENS.WORKSPACE.RATE_AND_UNIT]: () => require('../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage').default as React.ComponentType, - [SCREENS.WORKSPACE.BILLS]: () => require('../../../pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType, - [SCREENS.WORKSPACE.INVOICES]: () => require('../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType, - [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType, - [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE]: () => require('../../../pages/workspace/WorkspaceInvitePage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx index 228ea6bd3dce..aade06d10b16 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx @@ -11,23 +11,44 @@ const Stack = createStackNavigator(); const url = getCurrentUrl(); const openOnAdminRoom = url ? new URL(url).searchParams.get('openOnAdminRoom') : undefined; +type Screens = Partial React.ComponentType>>; + +const workspaceSettingsScreens = { + [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../../../pages/workspace/WorkspacesListPage').default as React.ComponentType, + [SCREENS.WORKSPACE.SETTINGS]: () => require('../../../../../pages/workspace/WorkspaceSettingsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CARD]: () => require('../../../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType, + [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType, + [SCREENS.WORKSPACE.BILLS]: () => require('../../../../../pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.INVOICES]: () => require('../../../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType, + [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType, +} satisfies Screens; + function BaseCentralPaneNavigator() { const styles = useThemeStyles(); + const options = { + headerShown: false, + title: 'New Expensify', + + // Prevent unnecessary scrolling + cardStyle: styles.cardStyleNavigator, + }; return ( - + + + {Object.entries(workspaceSettingsScreens).map(([screenName, componentGetter]) => ( + + ))} ); } From c6f16005536cae98e9368e313dd9c238721bb0d5 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 16:14:18 +0100 Subject: [PATCH 0128/1250] improve types for tab to central pane mapping --- src/CONST.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index fc355887d833..0dad726d2056 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import Config from 'react-native-config'; import * as KeyCommand from 'react-native-key-command'; +import {BottomTabName, CentralPaneName} from '@libs/Navigation/types'; import * as Url from './libs/Url'; import SCREENS from './SCREENS'; @@ -3031,7 +3032,7 @@ const CONST = { SCREENS.WORKSPACE.TRAVEL, SCREENS.WORKSPACE.MEMBERS, ], - }, + } satisfies Record, } as const; export default CONST; From 2a22d8aa349277095cc29d7288094e9c1ca2c7c4 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 16:22:31 +0100 Subject: [PATCH 0129/1250] prepare BottomTabBarFloatingActionButton --- src/components/FloatingActionButton.js | 50 +++++++++---------- .../index.native.tsx | 3 ++ .../index.tsx | 42 ++++++++++++++++ .../BottomTabBarFloatingActionButton/types.ts | 5 ++ src/pages/home/sidebar/SidebarScreen/index.js | 36 +------------ 5 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.native.tsx create mode 100644 src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx create mode 100644 src/pages/home/sidebar/BottomTabBarFloatingActionButton/types.ts diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js index 791eb150f8c9..e0bbb2ddbe1c 100644 --- a/src/components/FloatingActionButton.js +++ b/src/components/FloatingActionButton.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React, {PureComponent} from 'react'; -import {Animated, Easing, View} from 'react-native'; +import {Animated, Easing} from 'react-native'; import compose from '@libs/compose'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -84,31 +84,29 @@ class FloatingActionButton extends PureComponent { return ( - - { - this.fabPressable = el; - if (this.props.buttonRef) { - this.props.buttonRef.current = el; - } - }} - accessibilityLabel={this.props.accessibilityLabel} - role={this.props.role} - pressDimmingValue={1} - onPress={(e) => { - // Drop focus to avoid blue focus ring. - this.fabPressable.blur(); - this.props.onPress(e); - }} - onLongPress={() => {}} - style={[this.props.themeStyles.floatingActionButton, this.props.StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]} - > - - - + { + this.fabPressable = el; + if (this.props.buttonRef) { + this.props.buttonRef.current = el; + } + }} + accessibilityLabel={this.props.accessibilityLabel} + role={this.props.role} + pressDimmingValue={1} + onPress={(e) => { + // Drop focus to avoid blue focus ring. + this.fabPressable.blur(); + this.props.onPress(e); + }} + onLongPress={() => {}} + style={[this.props.themeStyles.floatingActionButton, this.props.StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]} + > + + ); } diff --git a/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.native.tsx b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.native.tsx new file mode 100644 index 000000000000..74180256cc74 --- /dev/null +++ b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.native.tsx @@ -0,0 +1,3 @@ +import FloatingActionButton from '@components/FloatingActionButton'; + +export default FloatingActionButton; diff --git a/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx new file mode 100644 index 000000000000..43e4c049a83e --- /dev/null +++ b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx @@ -0,0 +1,42 @@ +import React, {useCallback, useRef} from 'react'; +import FloatingActionButtonAndPopover from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; +import FloatingActionButtonPopoverMenuRef from './types'; + +function BottomTabBarFloatingActionButton() { + const popoverModal = useRef(null); + + /** + * Method to hide popover when dragover. + */ + const hidePopoverOnDragOver = useCallback(() => { + if (!popoverModal.current) { + return; + } + popoverModal.current.hideCreateMenu(); + }, []); + + /** + * Method create event listener + */ + const createDragoverListener = () => { + document.addEventListener('dragover', hidePopoverOnDragOver); + }; + + /** + * Method remove event listener. + */ + const removeDragoverListener = () => { + document.removeEventListener('dragover', hidePopoverOnDragOver); + }; + + return ( + + ); +} + +export default BottomTabBarFloatingActionButton; diff --git a/src/pages/home/sidebar/BottomTabBarFloatingActionButton/types.ts b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/types.ts new file mode 100644 index 000000000000..e8fd03ee2adc --- /dev/null +++ b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/types.ts @@ -0,0 +1,5 @@ +type FloatingActionButtonPopoverMenuRef = { + hideCreateMenu: () => void; +}; + +export default FloatingActionButtonPopoverMenuRef; diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 0b4c520c78a2..7086e8a8561a 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -1,50 +1,18 @@ -import React, {useCallback, useRef} from 'react'; +import React from 'react'; import useWindowDimensions from '@hooks/useWindowDimensions'; import FreezeWrapper from '@libs/Navigation/FreezeWrapper'; import BaseSidebarScreen from './BaseSidebarScreen'; -import FloatingActionButtonAndPopover from './FloatingActionButtonAndPopover'; import sidebarPropTypes from './sidebarPropTypes'; function SidebarScreen(props) { - const popoverModal = useRef(null); const {isSmallScreenWidth} = useWindowDimensions(); - /** - * Method to hide popover when dragover. - */ - const hidePopoverOnDragOver = useCallback(() => { - if (!popoverModal.current) { - return; - } - popoverModal.current.hideCreateMenu(); - }, []); - - /** - * Method create event listener - */ - const createDragoverListener = () => { - document.addEventListener('dragover', hidePopoverOnDragOver); - }; - - /** - * Method remove event listener. - */ - const removeDragoverListener = () => { - document.removeEventListener('dragover', hidePopoverOnDragOver); - }; - return ( - - + /> ); } From 593618f94b500c124fee9177f1e44bad99291756 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 17:32:59 +0100 Subject: [PATCH 0130/1250] move tab to central pane mapping --- src/CONST.ts | 14 -------------- .../Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 14 deletions(-) create mode 100755 src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts diff --git a/src/CONST.ts b/src/CONST.ts index 0dad726d2056..dbb031b63438 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3019,20 +3019,6 @@ const CONST = { DEFAULT: 5, CAROUSEL: 3, }, - - TAB_TO_CENTRAL_PANE_MAPPING: { - [SCREENS.HOME]: [SCREENS.REPORT], - [SCREENS.ALL_SETTINGS]: [SCREENS.SETTINGS.WORKSPACES], - [SCREENS.WORKSPACE.INITIAL]: [ - SCREENS.WORKSPACE.SETTINGS, - SCREENS.WORKSPACE.CARD, - SCREENS.WORKSPACE.REIMBURSE, - SCREENS.WORKSPACE.BILLS, - SCREENS.WORKSPACE.INVOICES, - SCREENS.WORKSPACE.TRAVEL, - SCREENS.WORKSPACE.MEMBERS, - ], - } satisfies Record, } as const; export default CONST; diff --git a/src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts new file mode 100755 index 000000000000..1f728b142306 --- /dev/null +++ b/src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts @@ -0,0 +1,18 @@ +import SCREENS from '@src/SCREENS'; +import {BottomTabName, CentralPaneName} from './types'; + +const TAB_TO_CENTRAL_PANE_MAPPING: Record = { + [SCREENS.HOME]: [SCREENS.REPORT], + [SCREENS.ALL_SETTINGS]: [SCREENS.SETTINGS.WORKSPACES], + [SCREENS.WORKSPACE.INITIAL]: [ + SCREENS.WORKSPACE.SETTINGS, + SCREENS.WORKSPACE.CARD, + SCREENS.WORKSPACE.REIMBURSE, + SCREENS.WORKSPACE.BILLS, + SCREENS.WORKSPACE.INVOICES, + SCREENS.WORKSPACE.TRAVEL, + SCREENS.WORKSPACE.MEMBERS, + ], +}; + +export default TAB_TO_CENTRAL_PANE_MAPPING; From 70b3db637abfc5c85ed33997dcb0a512a3ad36dd Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 17:59:50 +0100 Subject: [PATCH 0131/1250] add and modify navigation types --- src/libs/Navigation/types.ts | 85 ++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index b69552f6fe0f..328147e6064f 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1,5 +1,15 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import {CommonActions, NavigationContainerRefWithCurrent, NavigationHelpers, NavigationState, NavigatorScreenParams, PartialRoute, Route} from '@react-navigation/native'; +import { + CommonActions, + NavigationContainerRefWithCurrent, + NavigationHelpers, + NavigationState, + NavigatorScreenParams, + ParamListBase, + PartialRoute, + PartialState, + Route, +} from '@react-navigation/native'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; @@ -30,8 +40,9 @@ type ActionNavigate = { type StackNavigationAction = GoBackAction | ResetAction | SetParamsAction | ActionNavigate | undefined; type NavigationStateRoute = NavigationState['routes'][number]; -type NavigationPartialRoute = PartialRoute>; +type NavigationPartialRoute = PartialRoute>; type StateOrRoute = NavigationState | NavigationStateRoute | NavigationPartialRoute; +type State = NavigationState | PartialState>; type CentralPaneNavigatorParamList = { [SCREENS.REPORT]: { @@ -39,6 +50,33 @@ type CentralPaneNavigatorParamList = { reportID: string; openOnAdminRoom?: boolean; }; + + [SCREENS.SETTINGS.WORKSPACES]: undefined; + [SCREENS.WORKSPACE.SETTINGS]: { + policyID: string; + }; + [SCREENS.WORKSPACE.CARD]: { + policyID: string; + }; + [SCREENS.WORKSPACE.REIMBURSE]: { + policyID: string; + }; + [SCREENS.WORKSPACE.BILLS]: { + policyID: string; + }; + [SCREENS.WORKSPACE.INVOICES]: { + policyID: string; + }; + [SCREENS.WORKSPACE.TRAVEL]: { + policyID: string; + }; + [SCREENS.WORKSPACE.MEMBERS]: { + policyID: string; + }; + [SCREENS.REIMBURSEMENT_ACCOUNT]: { + stepToOpen: string; + policyID: string; + }; }; type SettingsNavigatorParamList = { @@ -83,38 +121,14 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: undefined; [SCREENS.SETTINGS.PROFILE.STATUS]: undefined; [SCREENS.SETTINGS.PROFILE.STATUS_SET]: undefined; - [SCREENS.WORKSPACE.INITIAL]: undefined; - [SCREENS.WORKSPACE.SETTINGS]: undefined; [SCREENS.WORKSPACE.CURRENCY]: undefined; - [SCREENS.WORKSPACE.CARD]: { - policyID: string; - }; - [SCREENS.WORKSPACE.REIMBURSE]: { - policyID: string; - }; [SCREENS.WORKSPACE.RATE_AND_UNIT]: undefined; - [SCREENS.WORKSPACE.BILLS]: { - policyID: string; - }; - [SCREENS.WORKSPACE.INVOICES]: { - policyID: string; - }; - [SCREENS.WORKSPACE.TRAVEL]: { - policyID: string; - }; - [SCREENS.WORKSPACE.MEMBERS]: { - policyID: string; - }; [SCREENS.WORKSPACE.INVITE]: { policyID: string; }; [SCREENS.WORKSPACE.INVITE_MESSAGE]: { policyID: string; }; - [SCREENS.REIMBURSEMENT_ACCOUNT]: { - stepToOpen: string; - policyID: string; - }; [SCREENS.GET_ASSISTANCE]: { taskID: string; }; @@ -359,8 +373,14 @@ type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: NavigatorScreenParams; }; -type PublicScreensParamList = { +type BottomTabNavigatorParamList = { [SCREENS.HOME]: undefined; + [SCREENS.ALL_SETTINGS]: undefined; + [SCREENS.WORKSPACE.INITIAL]: undefined; +}; + +type PublicScreensParamList = { + [NAVIGATORS.BOTTOM_TAB_NAVIGATOR]: NavigatorScreenParams; [SCREENS.TRANSITION_BETWEEN_APPS]: { shouldForceLogin: string; email: string; @@ -381,7 +401,7 @@ type PublicScreensParamList = { }; type AuthScreensParamList = { - [SCREENS.HOME]: undefined; + [NAVIGATORS.BOTTOM_TAB_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: NavigatorScreenParams; [SCREENS.VALIDATE_LOGIN]: { accountID: string; @@ -412,15 +432,23 @@ type AuthScreensParamList = { type RootStackParamList = PublicScreensParamList & AuthScreensParamList; +type BottomTabName = keyof BottomTabNavigatorParamList; + +type CentralPaneName = keyof CentralPaneNavigatorParamList; + export type { NavigationRef, StackNavigationAction, CentralPaneNavigatorParamList, + BottomTabName, + CentralPaneName, RootStackParamList, StateOrRoute, NavigationStateRoute, + NavigationPartialRoute, NavigationRoot, AuthScreensParamList, + BottomTabNavigatorParamList, RightModalNavigatorParamList, PublicScreensParamList, MoneyRequestNavigatorParamList, @@ -448,4 +476,5 @@ export type { SignInNavigatorParamList, ReferralDetailsNavigatorParamList, ReimbursementAccountNavigatorParamList, + State, }; From 018c0557292e3f79784ec37773346118fb328cde Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:00:20 +0100 Subject: [PATCH 0132/1250] add helpers for navigation --- .../getMatchingBottomTabRouteForState.ts | 28 ++++++++++ .../getMatchingCentralPaneRouteForState.ts | 51 +++++++++++++++++++ .../Navigation/getTopmostBottomTabRoute.ts | 19 +++++++ .../Navigation/getTopmostCentralPaneRoute.ts | 30 +++++++++++ 4 files changed, 128 insertions(+) create mode 100644 src/libs/Navigation/getMatchingBottomTabRouteForState.ts create mode 100644 src/libs/Navigation/getMatchingCentralPaneRouteForState.ts create mode 100644 src/libs/Navigation/getTopmostBottomTabRoute.ts create mode 100644 src/libs/Navigation/getTopmostCentralPaneRoute.ts diff --git a/src/libs/Navigation/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/getMatchingBottomTabRouteForState.ts new file mode 100644 index 000000000000..ac4f225fd896 --- /dev/null +++ b/src/libs/Navigation/getMatchingBottomTabRouteForState.ts @@ -0,0 +1,28 @@ +// import CONST from '@src/CONST'; +import SCREENS from '@src/SCREENS'; +import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; +import TAB_TO_CENTRAL_PANE_MAPPING from './TAB_TO_CENTRAL_PANE_MAPPING'; +import {BottomTabName, NavigationPartialRoute, RootStackParamList, State} from './types'; + +// Get the route that matches the topmost central pane route in the navigation stack. e.g REPORT -> HOME +function getMatchingBottomTabRouteForState(state: State): NavigationPartialRoute { + const defaultRoute = {name: SCREENS.HOME}; + const topmostCentralPaneRoute = getTopmostCentralPaneRoute(state); + + if (topmostCentralPaneRoute === undefined) { + return defaultRoute; + } + + for (const [tabName, centralPaneNames] of Object.entries(TAB_TO_CENTRAL_PANE_MAPPING)) { + if (centralPaneNames.includes(topmostCentralPaneRoute.name)) { + if (tabName === SCREENS.WORKSPACE.INITIAL) { + return {name: tabName, params: topmostCentralPaneRoute.params}; + } + return {name: tabName as BottomTabName}; + } + } + + return defaultRoute; +} + +export default getMatchingBottomTabRouteForState; diff --git a/src/libs/Navigation/getMatchingCentralPaneRouteForState.ts b/src/libs/Navigation/getMatchingCentralPaneRouteForState.ts new file mode 100644 index 000000000000..f00107308b2d --- /dev/null +++ b/src/libs/Navigation/getMatchingCentralPaneRouteForState.ts @@ -0,0 +1,51 @@ +import NAVIGATORS from '@src/NAVIGATORS'; +import SCREENS from '@src/SCREENS'; +import getTopmostBottomTabRoute from './getTopmostBottomTabRoute'; +import TAB_TO_CENTRAL_PANE_MAPPING from './TAB_TO_CENTRAL_PANE_MAPPING'; +import {CentralPaneName, NavigationPartialRoute, RootStackParamList, State} from './types'; + +/** + * @param state - react-navigation state + */ +const getTopMostReportIDFromRHP = (state: State): string => { + if (!state) { + return ''; + } + + const topmostRightPane = state.routes.filter((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR).at(-1); + + if (topmostRightPane?.state) { + return getTopMostReportIDFromRHP(topmostRightPane.state); + } + + const topmostRoute = state.routes.at(-1); + + if (topmostRoute?.state) { + return getTopMostReportIDFromRHP(topmostRoute.state); + } + + if (topmostRoute?.params && 'reportID' in topmostRoute.params && typeof topmostRoute.params.reportID === 'string') { + return topmostRoute.params.reportID; + } + + return ''; +}; + +// Get matching central pane route for bottom tab navigator. e.g HOME -> REPORT +function getMatchingCentralPaneRouteForState(state: State): NavigationPartialRoute { + const topmostBottomTabRoute = getTopmostBottomTabRoute(state); + + const centralPaneName = TAB_TO_CENTRAL_PANE_MAPPING[topmostBottomTabRoute.name][0]; + + if (topmostBottomTabRoute.name === SCREENS.WORKSPACE.INITIAL) { + return {name: centralPaneName, params: topmostBottomTabRoute.params}; + } + + if (topmostBottomTabRoute.name === SCREENS.HOME) { + return {name: centralPaneName, params: {reportID: getTopMostReportIDFromRHP(state)}}; + } + + return {name: centralPaneName}; +} + +export default getMatchingCentralPaneRouteForState; diff --git a/src/libs/Navigation/getTopmostBottomTabRoute.ts b/src/libs/Navigation/getTopmostBottomTabRoute.ts new file mode 100644 index 000000000000..053f866bc3b5 --- /dev/null +++ b/src/libs/Navigation/getTopmostBottomTabRoute.ts @@ -0,0 +1,19 @@ +import {BottomTabName, NavigationPartialRoute, RootStackParamList, State} from './types'; + +function getTopmostBottomTabRoute(state: State): NavigationPartialRoute { + const bottomTabNavigatorRoute = state.routes[0]; + + if (!bottomTabNavigatorRoute || bottomTabNavigatorRoute.name !== 'BottomTabNavigator' || bottomTabNavigatorRoute.state === undefined) { + throw new Error('There is no bottomTabNavigator route mounted as the first route in the root state.'); + } + + const topmostBottomTabRoute = bottomTabNavigatorRoute.state.routes.at(-1); + + if (!topmostBottomTabRoute) { + throw new Error('BottomTabNavigator route have no routes.'); + } + + return {name: topmostBottomTabRoute.name as BottomTabName, params: topmostBottomTabRoute.params}; +} + +export default getTopmostBottomTabRoute; diff --git a/src/libs/Navigation/getTopmostCentralPaneRoute.ts b/src/libs/Navigation/getTopmostCentralPaneRoute.ts new file mode 100644 index 000000000000..a9935adf6fbf --- /dev/null +++ b/src/libs/Navigation/getTopmostCentralPaneRoute.ts @@ -0,0 +1,30 @@ +import NAVIGATORS from '@src/NAVIGATORS'; +import {CentralPaneName, NavigationPartialRoute, RootStackParamList, State} from './types'; + +// Get the name of topmost central pane route in the navigation stack. +function getTopmostCentralPaneRoute(state: State): NavigationPartialRoute | undefined { + if (!state) { + return; + } + + const topmostCentralPane = state.routes.filter((route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR).at(-1); + + if (!topmostCentralPane) { + return; + } + + if (!!topmostCentralPane.params && 'screen' in topmostCentralPane.params) { + return {name: topmostCentralPane.params.screen as CentralPaneName, params: topmostCentralPane.params.params}; + } + + if (!topmostCentralPane.state) { + return; + } + + // There will be at least one route in the central pane navigator. + const {name, params} = topmostCentralPane.state.routes.at(-1) as NavigationPartialRoute; + + return {name, params}; +} + +export default getTopmostCentralPaneRoute; From c1613c804f4681d3774551575fc5fddad503aa87 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:03:52 +0100 Subject: [PATCH 0133/1250] modify linking config --- src/libs/Navigation/linkingConfig.ts | 104 ++++++++++++++++++--------- 1 file changed, 71 insertions(+), 33 deletions(-) diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index e9e76f4a2e82..38117d89b611 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -1,12 +1,48 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import {LinkingOptions} from '@react-navigation/native'; +import {getStateFromPath, LinkingOptions, NavigationState, PartialState} from '@react-navigation/native'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import {RootStackParamList} from './types'; +import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForState'; +import {BottomTabName, NavigationPartialRoute, RootStackParamList} from './types'; + +function getStateWithProperTab(state: PartialState>) { + // If the bottom tab navigator state is defined we don't need to do anything. + const isBottomTabNavigatorStateDefined = state.routes.at(0)?.state !== undefined; + if (isBottomTabNavigatorStateDefined) { + return state; + } + + // If not, we need to insert the tab that matches the currently generated state. + const matchingBottomTabRoute = getMatchingBottomTabRouteForState(state); + + // We need to have at least one HOME route in the state, otherwise the app won't load. + const routesForBottomTabNavigator: Array> = [{name: SCREENS.HOME}]; + + if (matchingBottomTabRoute.name !== SCREENS.HOME) { + // If the generated state requires tab other than HOME, we need to insert it. + routesForBottomTabNavigator.push(matchingBottomTabRoute); + } + + const stateWithTab = {...state}; + + // The first route in root stack is always the BOTTOM_TAB_NAVIGATOR + stateWithTab.routes[0] = {name: NAVIGATORS.BOTTOM_TAB_NAVIGATOR, state: {routes: routesForBottomTabNavigator}}; + + return stateWithTab; +} const linkingConfig: LinkingOptions = { + getStateFromPath: (path, options) => { + const state = getStateFromPath(path, options); + + if (state === undefined) { + throw new Error('Unable to parse path'); + } + const stateWithTab = getStateWithProperTab(state as PartialState>); + return stateWithTab; + }, prefixes: [ 'app://-/', 'new-expensify://', @@ -17,7 +53,7 @@ const linkingConfig: LinkingOptions = { CONST.STAGING_NEW_EXPENSIFY_URL, ], config: { - initialRouteName: SCREENS.HOME, + initialRouteName: NAVIGATORS.BOTTOM_TAB_NAVIGATOR, screens: { // Main Routes [SCREENS.VALIDATE_LOGIN]: ROUTES.VALIDATE_LOGIN, @@ -34,13 +70,43 @@ const linkingConfig: LinkingOptions = { [CONST.DEMO_PAGES.MONEY2020]: ROUTES.MONEY2020, // Sidebar - [SCREENS.HOME]: { - path: ROUTES.HOME, + [NAVIGATORS.BOTTOM_TAB_NAVIGATOR]: { + path: '', + initialRouteName: SCREENS.HOME, + screens: { + [SCREENS.HOME]: ROUTES.HOME, + [SCREENS.ALL_SETTINGS]: ROUTES.ALL_SETTINGS, + [SCREENS.WORKSPACE.INITIAL]: { + path: ROUTES.WORKSPACE_INITIAL.route, + exact: true, + }, + }, }, [NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: { screens: { [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route, + + [SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES, + [SCREENS.WORKSPACE.SETTINGS]: ROUTES.WORKSPACE_SETTINGS.route, + [SCREENS.WORKSPACE.CARD]: { + path: ROUTES.WORKSPACE_CARD.route, + }, + [SCREENS.WORKSPACE.REIMBURSE]: { + path: ROUTES.WORKSPACE_REIMBURSE.route, + }, + [SCREENS.WORKSPACE.BILLS]: { + path: ROUTES.WORKSPACE_BILLS.route, + }, + [SCREENS.WORKSPACE.INVOICES]: { + path: ROUTES.WORKSPACE_INVOICES.route, + }, + [SCREENS.WORKSPACE.TRAVEL]: { + path: ROUTES.WORKSPACE_TRAVEL.route, + }, + [SCREENS.WORKSPACE.MEMBERS]: { + path: ROUTES.WORKSPACE_MEMBERS.route, + }, }, }, [SCREENS.NOT_FOUND]: '*', @@ -52,10 +118,6 @@ const linkingConfig: LinkingOptions = { [SCREENS.SETTINGS.ROOT]: { path: ROUTES.SETTINGS, }, - [SCREENS.SETTINGS.WORKSPACES]: { - path: ROUTES.SETTINGS_WORKSPACES, - exact: true, - }, [SCREENS.SETTINGS.PREFERENCES.ROOT]: { path: ROUTES.SETTINGS_PREFERENCES, exact: true, @@ -218,36 +280,12 @@ const linkingConfig: LinkingOptions = { path: ROUTES.SETTINGS_STATUS_SET, exact: true, }, - [SCREENS.WORKSPACE.INITIAL]: { - path: ROUTES.WORKSPACE_INITIAL.route, - }, - [SCREENS.WORKSPACE.SETTINGS]: { - path: ROUTES.WORKSPACE_SETTINGS.route, - }, [SCREENS.WORKSPACE.CURRENCY]: { path: ROUTES.WORKSPACE_SETTINGS_CURRENCY.route, }, - [SCREENS.WORKSPACE.CARD]: { - path: ROUTES.WORKSPACE_CARD.route, - }, - [SCREENS.WORKSPACE.REIMBURSE]: { - path: ROUTES.WORKSPACE_REIMBURSE.route, - }, [SCREENS.WORKSPACE.RATE_AND_UNIT]: { path: ROUTES.WORKSPACE_RATE_AND_UNIT.route, }, - [SCREENS.WORKSPACE.BILLS]: { - path: ROUTES.WORKSPACE_BILLS.route, - }, - [SCREENS.WORKSPACE.INVOICES]: { - path: ROUTES.WORKSPACE_INVOICES.route, - }, - [SCREENS.WORKSPACE.TRAVEL]: { - path: ROUTES.WORKSPACE_TRAVEL.route, - }, - [SCREENS.WORKSPACE.MEMBERS]: { - path: ROUTES.WORKSPACE_MEMBERS.route, - }, [SCREENS.WORKSPACE.INVITE]: { path: ROUTES.WORKSPACE_INVITE.route, }, From 2d0c560686f41bf20d9b4b2b2c733e6c88d3b981 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:11:10 +0100 Subject: [PATCH 0134/1250] add bottomTabBar --- .../BottomTabBar.tsx | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx new file mode 100644 index 000000000000..2f1cf0309455 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -0,0 +1,55 @@ +import {useNavigationState} from '@react-navigation/native'; +import React from 'react'; +import {View} from 'react-native'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import {PressableWithFeedback} from '@components/Pressable'; +import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; +import Navigation from '@libs/Navigation/Navigation'; +import {RootStackParamList} from '@libs/Navigation/types'; +import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; +import useThemeStyles from '@styles/useThemeStyles'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; + +function BottomTabBar() { + const styles = useThemeStyles(); + + // Parent navigator of the bottom tab bar is the root navigator. + const currentTabName = useNavigationState((state) => getTopmostBottomTabRoute(state).name); + + return ( + + { + Navigation.navigate(ROUTES.HOME); + }} + role={CONST.ACCESSIBILITY_ROLE.BUTTON} + accessibilityLabel="Chats" + > + + + + { + Navigation.navigate(ROUTES.ALL_SETTINGS); + }} + role={CONST.ACCESSIBILITY_ROLE.BUTTON} + accessibilityLabel="Settings" + > + + + + ); +} + +BottomTabBar.displayName = 'BottomTabBar'; + +export default BottomTabBar; From 62a63ecd9cf847b1226c5fc019006d76502f578c Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:13:32 +0100 Subject: [PATCH 0135/1250] add comment to the PublicBottomTabNavigator --- .../AppNavigator/Navigators/PublicBottomTabNavigator.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx index 6ed58c178c2c..ddf3443e9c06 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/PublicBottomTabNavigator.tsx @@ -5,6 +5,7 @@ import {BottomTabNavigatorParamList} from '@libs/Navigation/types'; import SignInPage from '@pages/signin/SignInPage'; import SCREENS from '@src/SCREENS'; +// This type is not exactly right because we are using the same route in public and auth screens. const Tab = createCustomBottomTabNavigator(); const screenOptions: StackNavigationOptions = { From 32ba7c6f00fc868f8db7aaa4f330a5e97ab85d83 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:15:01 +0100 Subject: [PATCH 0136/1250] update type in Navigation.js --- src/libs/Navigation/Navigation.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index c2dd3e76e7ad..7c1d2343159d 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -1,5 +1,5 @@ import {findFocusedRoute, getActionFromState} from '@react-navigation/core'; -import {CommonActions, EventMapCore, getPathFromState, NavigationState, PartialState, StackActions} from '@react-navigation/native'; +import {CommonActions, EventMapCore, getPathFromState, NavigationState, StackActions} from '@react-navigation/native'; import findLastIndex from 'lodash/findLastIndex'; import Log from '@libs/Log'; import CONST from '@src/CONST'; @@ -12,7 +12,7 @@ import originalGetTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; import linkTo from './linkTo'; import navigationRef from './navigationRef'; -import {StackNavigationAction, StateOrRoute} from './types'; +import {StackNavigationAction, State, StateOrRoute} from './types'; let resolveNavigationIsReadyPromise: () => void; const navigationIsReadyPromise = new Promise((resolve) => { @@ -286,7 +286,7 @@ function setIsNavigationReady() { * * @param state - react-navigation state object */ -function navContainsProtectedRoutes(state: NavigationState | PartialState | undefined): boolean { +function navContainsProtectedRoutes(state: State | undefined): boolean { if (!state?.routeNames || !Array.isArray(state.routeNames)) { return false; } From c0f13514fb53d08b005328fb807fcd8d32ccad0c Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:26:19 +0100 Subject: [PATCH 0137/1250] use proper color for BottomTabBar --- .../createCustomBottomTabNavigator/BottomTabBar.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 2f1cf0309455..3b58e110f0fd 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -8,12 +8,14 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute' import Navigation from '@libs/Navigation/Navigation'; import {RootStackParamList} from '@libs/Navigation/types'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; +import useTheme from '@styles/themes/useTheme'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; function BottomTabBar() { + const theme = useTheme(); const styles = useThemeStyles(); // Parent navigator of the bottom tab bar is the root navigator. @@ -30,7 +32,7 @@ function BottomTabBar() { > @@ -43,7 +45,7 @@ function BottomTabBar() { > From 7b4cef9fc413a55c194401d0397c818c349cb778 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:32:07 +0100 Subject: [PATCH 0138/1250] add bottom tab navigator --- .../Navigation/AppNavigator/AuthScreens.tsx | 6 +- .../Navigators/BottomTabNavigator.tsx | 61 +++++++++++++ .../createCustomBottomTabNavigator/index.tsx | 88 +++++++++++++++++++ 3 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx create mode 100644 src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 008015db7a90..4bad503bcdd7 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -35,6 +35,7 @@ import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; import createCustomStackNavigator from './createCustomStackNavigator'; import defaultScreenOptions from './defaultScreenOptions'; import getRootNavigatorScreenOptions from './getRootNavigatorScreenOptions'; +import BottomTabNavigator from './Navigators/BottomTabNavigator'; import CentralPaneNavigator from './Navigators/CentralPaneNavigator'; import RightModalNavigator from './Navigators/RightModalNavigator'; @@ -56,7 +57,6 @@ type AuthScreensProps = { }; const loadReportAttachments = () => require('../../../pages/home/report/ReportAttachments').default as React.ComponentType; -const loadSidebarScreen = () => require('../../../pages/home/sidebar/SidebarScreen').default as React.ComponentType; const loadValidateLoginPage = () => require('../../../pages/ValidateLoginPage').default as React.ComponentType; const loadLogOutPreviousUserPage = () => require('../../../pages/LogOutPreviousUserPage').default as React.ComponentType; const loadConciergePage = () => require('../../../pages/ConciergePage').default as React.ComponentType; @@ -255,9 +255,9 @@ function AuthScreens({lastUpdateIDAppliedToClient, session, lastOpenedPublicRoom require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType; + +const Tab = createCustomBottomTabNavigator(); + +// TODO-IDEAL replace with the actuall screen. +function SecondTab() { + return ( + + Expensify settings + + { + Navigation.navigate(ROUTES.SETTINGS_WORKSPACES); + }} + > + Workspaces + + + ); +} + +const screenOptions: StackNavigationOptions = { + headerShown: false, +}; + +function BottomTabNavigator() { + return ( + + + + + + ); +} + +BottomTabNavigator.displayName = 'BottomTabNavigator'; + +export default BottomTabNavigator; diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx new file mode 100644 index 000000000000..24be4ce5a174 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx @@ -0,0 +1,88 @@ +import { + createNavigatorFactory, + DefaultNavigatorOptions, + ParamListBase, + StackActionHelpers, + StackNavigationState, + StackRouter, + StackRouterOptions, + useNavigationBuilder, +} from '@react-navigation/native'; +import {StackNavigationEventMap, StackNavigationOptions, StackView} from '@react-navigation/stack'; +import PropTypes from 'prop-types'; +import React from 'react'; +import {View} from 'react-native'; +import {NavigationStateRoute} from '@libs/Navigation/types'; +import SCREENS from '@src/SCREENS'; +import BottomTabBar from './BottomTabBar'; + +type CustomNavigatorProps = DefaultNavigatorOptions, StackNavigationOptions, StackNavigationEventMap> & { + initialRouteName: string; +}; + +const propTypes = { + /* Children for the useNavigationBuilder hook */ + children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired, + + /* initialRouteName for this navigator */ + initialRouteName: PropTypes.oneOf([PropTypes.string, undefined]), + + /* Screen options defined for this navigator */ + // eslint-disable-next-line react/forbid-prop-types + screenOptions: PropTypes.object, +}; + +const defaultProps = { + initialRouteName: undefined, + screenOptions: undefined, +}; + +function getStateToRender(state: StackNavigationState): StackNavigationState { + const routesToRender = [state.routes.at(-1)] as NavigationStateRoute[]; + // We need to render at least one HOME screen to make sure everything load properly. + if (routesToRender[0].name !== SCREENS.HOME) { + const routeToRender = state.routes.find((route) => route.name === SCREENS.HOME); + if (routeToRender) { + routesToRender.unshift(routeToRender); + } + } + + return {...state, routes: routesToRender, index: routesToRender.length - 1}; +} + +function CustomBottomTabNavigator({initialRouteName, children, screenOptions, ...props}: CustomNavigatorProps) { + const {state, navigation, descriptors, NavigationContent} = useNavigationBuilder< + StackNavigationState, + StackRouterOptions, + StackActionHelpers, + StackNavigationOptions, + StackNavigationEventMap + >(StackRouter, { + children, + screenOptions, + initialRouteName, + }); + + const stateToRender = getStateToRender(state); + + return ( + + + + + + + ); +} + +CustomBottomTabNavigator.defaultProps = defaultProps; +CustomBottomTabNavigator.propTypes = propTypes; +CustomBottomTabNavigator.displayName = 'CustomBottomTabNavigator'; + +export default createNavigatorFactory(CustomBottomTabNavigator); From 2a23586dcd10668da78419dcc7b333ffad78adfe Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:39:41 +0100 Subject: [PATCH 0139/1250] move trigger for state rehydration to createCustomStackNavigator --- .../createCustomStackNavigator/index.tsx | 14 +++++++++----- src/libs/Navigation/NavigationRoot.tsx | 8 -------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index dd2e548064c4..9a882d79c6cb 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -1,7 +1,8 @@ import {createNavigatorFactory, ParamListBase, StackActionHelpers, StackNavigationState, useNavigationBuilder} from '@react-navigation/native'; import {StackNavigationEventMap, StackNavigationOptions, StackView} from '@react-navigation/stack'; -import React, {useMemo, useRef} from 'react'; +import React, {useEffect, useMemo} from 'react'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import navigationRef from '@libs/Navigation/navigationRef'; import NAVIGATORS from '@src/NAVIGATORS'; import CustomRouter from './CustomRouter'; import type {ResponsiveStackNavigatorProps, ResponsiveStackNavigatorRouterOptions} from './types'; @@ -30,10 +31,6 @@ function reduceReportRoutes(routes: Routes): Routes { function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { const {isSmallScreenWidth} = useWindowDimensions(); - const isSmallScreenWidthRef = useRef(isSmallScreenWidth); - - isSmallScreenWidthRef.current = isSmallScreenWidth; - const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder< StackNavigationState, ResponsiveStackNavigatorRouterOptions, @@ -46,6 +43,13 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { initialRouteName: props.initialRouteName, }); + useEffect(() => { + if (!navigationRef.isReady()) { + return; + } + navigationRef.resetRoot(navigationRef.getRootState()); + }, [isSmallScreenWidth]); + const stateToRender = useMemo(() => { const result = reduceReportRoutes(state.routes); diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 7c0b9ef4fc8c..564daa5787ac 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -71,14 +71,6 @@ function NavigationRoot({authenticated, onReady}: NavigationRootProps) { Navigation.setShouldPopAllStateOnUP(); }, [isSmallScreenWidth]); - useEffect(() => { - if (!navigationRef.isReady() || !authenticated) { - return; - } - // We need to force state rehydration so the CustomRouter can add the CentralPaneNavigator route if necessary. - navigationRef.resetRoot(navigationRef.getRootState()); - }, [isSmallScreenWidth, authenticated]); - const handleStateChange = (state: NavigationState | undefined) => { if (!state) { return; From 644c7708e52c278640bd4a40ee38ad806e47bed7 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 18:41:46 +0100 Subject: [PATCH 0140/1250] modify custom router for the CustomStackNavigator --- .../CustomRouter.ts | 84 ++++++++----------- 1 file changed, 36 insertions(+), 48 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index 435ebc00362b..73e26962e581 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -1,61 +1,44 @@ -import {NavigationState, PartialState, RouterConfigOptions, StackNavigationState, StackRouter} from '@react-navigation/native'; +import {RouterConfigOptions, StackNavigationState, StackRouter} from '@react-navigation/native'; import {ParamListBase} from '@react-navigation/routers'; import getIsSmallScreenWidth from '@libs/getIsSmallScreenWidth'; +import getMatchingCentralPaneRouteForState from '@libs/Navigation/getMatchingCentralPaneRouteForState'; +import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; +import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; +import TAB_TO_CENTRAL_PANE_MAPPING from '@libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING'; +import {RootStackParamList, State} from '@libs/Navigation/types'; import NAVIGATORS from '@src/NAVIGATORS'; -import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; -type State = NavigationState | PartialState; - -const isAtLeastOneCentralPaneNavigatorInState = (state: State): boolean => !!state.routes.find((route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); - -const getTopMostReportIDFromRHP = (state: State): string => { - if (!state) { - return ''; - } - - const topmostRightPane = state.routes.filter((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR).at(-1); - - if (topmostRightPane?.state) { - return getTopMostReportIDFromRHP(topmostRightPane.state); - } - - const topmostRoute = state.routes.at(-1); - - if (topmostRoute?.state) { - return getTopMostReportIDFromRHP(topmostRoute.state); - } - - if (topmostRoute?.params && 'reportID' in topmostRoute.params && typeof topmostRoute.params.reportID === 'string' && topmostRoute.params.reportID) { - return topmostRoute.params.reportID; - } - - return ''; -}; /** * Adds report route without any specific reportID to the state. * The report screen will self set proper reportID param based on the helper function findLastAccessedReport (look at ReportScreenWrapper for more info) * * @param state - react-navigation state */ -const addCentralPaneNavigatorRoute = (state: State) => { - const reportID = getTopMostReportIDFromRHP(state); +const addCentralPaneNavigatorRoute = (state: State) => { + const matchingCentralPaneRoute = getMatchingCentralPaneRouteForState(state); + + const bottomTabRoute = state.routes.filter((route) => route.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR); + const centralPaneRoutes = state.routes.filter((route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR); + + // TODO-IDEAL Both RHP and LHP add condition for the LHP + const modalRoutes = state.routes.filter((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); + const centralPaneNavigatorRoute = { name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR, - state: { - routes: [ - { - name: SCREENS.REPORT, - params: { - reportID, - }, - }, - ], + params: { + screen: matchingCentralPaneRoute.name, + params: matchingCentralPaneRoute.params, }, }; - state.routes.splice(1, 0, centralPaneNavigatorRoute); - // eslint-disable-next-line no-param-reassign, @typescript-eslint/non-nullable-type-assertion-style - (state.index as number) = state.routes.length - 1; + + // @ts-expect-error Updating read only property + // noinspection JSConstantReassignment + state.routes = [...bottomTabRoute, ...centralPaneRoutes, centralPaneNavigatorRoute, ...modalRoutes]; // eslint-disable-line + + // @ts-expect-error Updating read only property + // noinspection JSConstantReassignment + state.index = state.routes.length - 1; // eslint-disable-line }; function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { @@ -63,14 +46,19 @@ function CustomRouter(options: ResponsiveStackNavigatorRouterOptions) { return { ...stackRouter, - getRehydratedState(partialState: StackNavigationState, {routeNames, routeParamList, routeGetIdList}: RouterConfigOptions): StackNavigationState { + getRehydratedState(partialState: StackNavigationState, {routeNames, routeParamList, routeGetIdList}: RouterConfigOptions): StackNavigationState { const isSmallScreenWidth = getIsSmallScreenWidth(); // Make sure that there is at least one CentralPaneNavigator (ReportScreen by default) in the state if this is a wide layout - if (!isAtLeastOneCentralPaneNavigatorInState(partialState) && !isSmallScreenWidth) { - // If we added a route we need to make sure that the state.stale is true to generate new key for this route + const topmostCentralPaneRoute = getTopmostCentralPaneRoute(partialState); + const topmostBottomTabRoute = getTopmostBottomTabRoute(partialState); - // eslint-disable-next-line no-param-reassign - (partialState.stale as boolean) = true; + const isBottomTabMatchingCentralPane = topmostCentralPaneRoute && TAB_TO_CENTRAL_PANE_MAPPING[topmostBottomTabRoute.name].includes(topmostCentralPaneRoute.name); + + if (!isSmallScreenWidth && !isBottomTabMatchingCentralPane) { + // If we added a route we need to make sure that the state.stale is true to generate new key for this route + // @ts-expect-error Updating read only property + // noinspection JSConstantReassignment + partialState.stale = true; // eslint-disable-line addCentralPaneNavigatorRoute(partialState); } const state = stackRouter.getRehydratedState(partialState, {routeNames, routeParamList, routeGetIdList}); From 0c750973ae27f86c8c4d4d7ef404e75e8086e7df Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 13 Dec 2023 19:05:03 +0100 Subject: [PATCH 0141/1250] modify linkTo --- src/libs/Navigation/linkTo.ts | 84 +++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/linkTo.ts b/src/libs/Navigation/linkTo.ts index 8be8dd1ecfae..265887346b8c 100644 --- a/src/libs/Navigation/linkTo.ts +++ b/src/libs/Navigation/linkTo.ts @@ -1,13 +1,19 @@ -import {getActionFromState} from '@react-navigation/core'; -import {NavigationAction, NavigationContainerRef, NavigationState, PartialState} from '@react-navigation/native'; +import {getActionFromState, PartialState} from '@react-navigation/core'; +import {NavigationAction, NavigationContainerRef, NavigationState} from '@react-navigation/native'; import {Writable} from 'type-fest'; +import getIsSmallScreenWidth from '@libs/getIsSmallScreenWidth'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import {Route} from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; +import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForState'; +import getMatchingCentralPaneRouteForState from './getMatchingCentralPaneRouteForState'; import getStateFromPath from './getStateFromPath'; +import getTopmostBottomTabRoute from './getTopmostBottomTabRoute'; +import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; import getTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; -import {NavigationRoot, RootStackParamList, StackNavigationAction} from './types'; +import {NavigationRoot, RootStackParamList, StackNavigationAction, State} from './types'; type ActionPayloadParams = { screen?: string; @@ -28,7 +34,7 @@ type ActionPayload = { */ function getMinimalAction(action: NavigationAction, state: NavigationState): Writable { let currentAction: NavigationAction = action; - let currentState: NavigationState | PartialState | undefined = state; + let currentState: State | undefined = state; let currentTargetKey: string | undefined; while (currentAction.payload && 'name' in currentAction.payload && currentState?.routes[currentState.index ?? -1].name === currentAction.payload.name) { @@ -55,6 +61,27 @@ function getMinimalAction(action: NavigationAction, state: NavigationState): Wri return currentAction; } +// Because we need to change the type to push, we also need to set target for this action to the bottom tab navigator. +function getActionForBottomTabNavigator(action: StackNavigationAction, state: NavigationState): Writable | undefined { + const bottomTabNavigatorRoute = state.routes.at(0); + + if (!bottomTabNavigatorRoute || bottomTabNavigatorRoute.state === undefined || !action || action.type !== CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { + return; + } + + const params = action.payload.params as ActionPayloadParams; + const screen = params.screen; + + return { + type: CONST.NAVIGATION.ACTION_TYPE.PUSH, + payload: { + name: screen, + params: params.params, + }, + target: bottomTabNavigatorRoute.state.key, + }; +} + export default function linkTo(navigation: NavigationContainerRef | null, path: Route, type?: string, isActiveRoute?: boolean) { if (!navigation) { throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?"); @@ -69,18 +96,33 @@ export default function linkTo(navigation: NavigationContainerRef; + const stateFromPath = getStateFromPath(path) as PartialState>; + const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config); // If action type is different than NAVIGATE we can't change it to the PUSH safely if (action?.type === CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { + const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState); // In case if type is 'FORCED_UP' we replace current screen with the provided. This means the current screen no longer exists in the stack if (type === CONST.NAVIGATION.TYPE.FORCED_UP) { action.type = CONST.NAVIGATION.ACTION_TYPE.REPLACE; // If this action is navigating to the report screen and the top most navigator is different from the one we want to navigate - PUSH the new screen to the top of the stack - } else if (action.payload.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR && getTopmostReportId(rootState) !== getTopmostReportId(state)) { + } else if ( + action.payload.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR && + topmostCentralPaneRoute && + (topmostCentralPaneRoute.name !== SCREENS.REPORT || getTopmostReportId(rootState) !== getTopmostReportId(stateFromPath)) + ) { + // We need to push a tab if the tab doesn't match the central pane route that we are going to push. + const topmostBottomTabRoute = getTopmostBottomTabRoute(rootState); + const matchingBottomTabRoute = getMatchingBottomTabRouteForState(stateFromPath); + if (topmostBottomTabRoute.name !== matchingBottomTabRoute.name) { + root.dispatch({ + type: CONST.NAVIGATION.ACTION_TYPE.PUSH, + payload: matchingBottomTabRoute, + }); + } + action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH; // If the type is UP, we deeplinked into one of the RHP flows and we want to replace the current screen with the previous one in the flow @@ -91,6 +133,30 @@ export default function linkTo(navigation: NavigationContainerRef Date: Wed, 13 Dec 2023 19:22:56 +0100 Subject: [PATCH 0142/1250] fix linting errors --- src/CONST.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index dbb031b63438..072f780b54ae 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import Config from 'react-native-config'; import * as KeyCommand from 'react-native-key-command'; -import {BottomTabName, CentralPaneName} from '@libs/Navigation/types'; import * as Url from './libs/Url'; import SCREENS from './SCREENS'; From 87061091ef3938e84ed547160b7f8da0e11a8cfe Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Fri, 15 Dec 2023 12:45:01 +0100 Subject: [PATCH 0143/1250] add POP_TO_TOP when navigating to bottom tab on small screen --- src/libs/Navigation/linkTo.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/Navigation/linkTo.ts b/src/libs/Navigation/linkTo.ts index 265887346b8c..550b5ddb7d62 100644 --- a/src/libs/Navigation/linkTo.ts +++ b/src/libs/Navigation/linkTo.ts @@ -155,6 +155,12 @@ export default function linkTo(navigation: NavigationContainerRef Date: Fri, 15 Dec 2023 13:37:51 +0100 Subject: [PATCH 0144/1250] fix padding --- src/styles/styles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index a87dd8d09c6f..db4ff95b1b4c 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -1406,7 +1406,7 @@ const styles = (theme: ThemeColors) => createAccountMenuPositionProfile: () => ({ horizontal: 18, - ...getPopOverVerticalOffset(162), + ...getPopOverVerticalOffset(202 + 40), } satisfies AnchorPosition), createMenuPositionProfile: (windowWidth: number) => From 8be9983ca0461b9a6348f3f63ef6c36b816671fd Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Fri, 15 Dec 2023 15:38:18 +0100 Subject: [PATCH 0145/1250] add removed workspace currency page --- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index c63dd80f7621..26e2e7aac0f0 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -220,6 +220,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/settings/Profile/CustomStatus/StatusPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.STATUS_SET]: () => require('../../../pages/settings/Profile/CustomStatus/StatusSetPage').default as React.ComponentType, [SCREENS.WORKSPACE.RATE_AND_UNIT]: () => require('../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CURRENCY]: () => require('../../../pages/workspace/WorkspaceSettingsCurrencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE]: () => require('../../../pages/workspace/WorkspaceInvitePage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, From 6eb63989f167bc46427cd66ea74014ebfc630655 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Fri, 15 Dec 2023 17:00:22 +0100 Subject: [PATCH 0146/1250] change workspace settings to workspace overview --- src/CONST.ts | 2 +- src/ROUTES.ts | 12 ++++++------ src/SCREENS.ts | 4 ++-- src/languages/en.ts | 1 + src/languages/es.ts | 1 + .../AppNavigator/ModalStackNavigators.tsx | 2 +- .../BaseCentralPaneNavigator.tsx | 2 +- .../Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts | 2 +- src/libs/Navigation/linkingConfig.ts | 4 ++-- src/libs/Navigation/types.ts | 2 +- src/pages/workspace/WorkspaceInitialPage.js | 6 +++--- ...yPage.js => WorkspaceOverviewCurrencyPage.js} | 4 ++-- ...eSettingsPage.js => WorkspaceOverviewPage.js} | 16 ++++++++-------- 13 files changed, 30 insertions(+), 28 deletions(-) rename src/pages/workspace/{WorkspaceSettingsCurrencyPage.js => WorkspaceOverviewCurrencyPage.js} (97%) rename src/pages/workspace/{WorkspaceSettingsPage.js => WorkspaceOverviewPage.js} (95%) diff --git a/src/CONST.ts b/src/CONST.ts index 072f780b54ae..e55e3664a29b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1404,7 +1404,7 @@ const CONST = { GUIDES_CALL_TASK_IDS: { CONCIERGE_DM: 'NewExpensifyConciergeDM', WORKSPACE_INITIAL: 'WorkspaceHome', - WORKSPACE_SETTINGS: 'WorkspaceGeneralSettings', + WORKSPACE_OVERVIEW: 'WorkspaceGeneralSettings', WORKSPACE_CARD: 'WorkspaceCorporateCards', WORKSPACE_REIMBURSE: 'WorkspaceReimburseReceipts', WORKSPACE_BILLS: 'WorkspacePayBills', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 44563b74d48e..1ea8bca94cf6 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -422,13 +422,13 @@ const ROUTES = { route: 'workspace/:policyID/invite-message', getRoute: (policyID: string) => `workspace/${policyID}/invite-message` as const, }, - WORKSPACE_SETTINGS: { - route: 'workspace/:policyID/settings', - getRoute: (policyID: string) => `workspace/${policyID}/settings` as const, + WORKSPACE_OVERVIEW: { + route: 'workspace/:policyID/overview', + getRoute: (policyID: string) => `workspace/${policyID}/overview` as const, }, - WORKSPACE_SETTINGS_CURRENCY: { - route: 'workspace/:policyID/settings/currency', - getRoute: (policyID: string) => `workspace/${policyID}/settings/currency` as const, + WORKSPACE_OVERVIEW_CURRENCY: { + route: 'workspace/:policyID/overview/currency', + getRoute: (policyID: string) => `workspace/${policyID}/overview/currency` as const, }, WORKSPACE_CARD: { route: 'workspace/:policyID/card', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 44f11c059321..86913760e784 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -185,7 +185,7 @@ const SCREENS = { WORKSPACE: { INITIAL: 'Workspace_Initial', - SETTINGS: 'Workspace_Settings', + OVERVIEW: 'Workspace_Overview', CARD: 'Workspace_Card', REIMBURSE: 'Workspace_Reimburse', RATE_AND_UNIT: 'Workspace_RateAndUnit', @@ -195,7 +195,7 @@ const SCREENS = { MEMBERS: 'Workspace_Members', INVITE: 'Workspace_Invite', INVITE_MESSAGE: 'Workspace_Invite_Message', - CURRENCY: 'Workspace_Settings_Currency', + CURRENCY: 'Workspace_Overview_Currency', }, EDIT_REQUEST: { diff --git a/src/languages/en.ts b/src/languages/en.ts index c4a481cb71c0..fe7629a2a971 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1455,6 +1455,7 @@ export default { invoices: 'Invoices', travel: 'Travel', members: 'Members', + overview: 'Overview', bankAccount: 'Bank account', connectBankAccount: 'Connect bank account', testTransactions: 'Test transactions', diff --git a/src/languages/es.ts b/src/languages/es.ts index a91a8768a3ee..ee0a38d91c61 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1478,6 +1478,7 @@ export default { invoices: 'Enviar facturas', travel: 'Viajes', members: 'Miembros', + overview: 'Descripción', bankAccount: 'Cuenta bancaria', connectBankAccount: 'Conectar cuenta bancaria', testTransactions: 'Transacciones de prueba', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 26e2e7aac0f0..ce38daa4d6cd 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -220,7 +220,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/settings/Profile/CustomStatus/StatusPage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.STATUS_SET]: () => require('../../../pages/settings/Profile/CustomStatus/StatusSetPage').default as React.ComponentType, [SCREENS.WORKSPACE.RATE_AND_UNIT]: () => require('../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage').default as React.ComponentType, - [SCREENS.WORKSPACE.CURRENCY]: () => require('../../../pages/workspace/WorkspaceSettingsCurrencyPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CURRENCY]: () => require('../../../pages/workspace/WorkspaceOverviewCurrencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE]: () => require('../../../pages/workspace/WorkspaceInvitePage').default as React.ComponentType, [SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx index aade06d10b16..aa4e78137cd2 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx @@ -15,7 +15,7 @@ type Screens = Partial React.C const workspaceSettingsScreens = { [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../../../pages/workspace/WorkspacesListPage').default as React.ComponentType, - [SCREENS.WORKSPACE.SETTINGS]: () => require('../../../../../pages/workspace/WorkspaceSettingsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.OVERVIEW]: () => require('../../../../../pages/workspace/WorkspaceOverviewPage').default as React.ComponentType, [SCREENS.WORKSPACE.CARD]: () => require('../../../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType, [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType, [SCREENS.WORKSPACE.BILLS]: () => require('../../../../../pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType, diff --git a/src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts index 1f728b142306..458b592d9399 100755 --- a/src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts +++ b/src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts @@ -5,7 +5,7 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record = { [SCREENS.HOME]: [SCREENS.REPORT], [SCREENS.ALL_SETTINGS]: [SCREENS.SETTINGS.WORKSPACES], [SCREENS.WORKSPACE.INITIAL]: [ - SCREENS.WORKSPACE.SETTINGS, + SCREENS.WORKSPACE.OVERVIEW, SCREENS.WORKSPACE.CARD, SCREENS.WORKSPACE.REIMBURSE, SCREENS.WORKSPACE.BILLS, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 38117d89b611..67c71be61a86 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -88,7 +88,7 @@ const linkingConfig: LinkingOptions = { [SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route, [SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES, - [SCREENS.WORKSPACE.SETTINGS]: ROUTES.WORKSPACE_SETTINGS.route, + [SCREENS.WORKSPACE.OVERVIEW]: ROUTES.WORKSPACE_OVERVIEW.route, [SCREENS.WORKSPACE.CARD]: { path: ROUTES.WORKSPACE_CARD.route, }, @@ -281,7 +281,7 @@ const linkingConfig: LinkingOptions = { exact: true, }, [SCREENS.WORKSPACE.CURRENCY]: { - path: ROUTES.WORKSPACE_SETTINGS_CURRENCY.route, + path: ROUTES.WORKSPACE_OVERVIEW_CURRENCY.route, }, [SCREENS.WORKSPACE.RATE_AND_UNIT]: { path: ROUTES.WORKSPACE_RATE_AND_UNIT.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 328147e6064f..f9e85317959b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -52,7 +52,7 @@ type CentralPaneNavigatorParamList = { }; [SCREENS.SETTINGS.WORKSPACES]: undefined; - [SCREENS.WORKSPACE.SETTINGS]: { + [SCREENS.WORKSPACE.OVERVIEW]: { policyID: string; }; [SCREENS.WORKSPACE.CARD]: { diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js index 3c73687ca813..6078f453b6eb 100644 --- a/src/pages/workspace/WorkspaceInitialPage.js +++ b/src/pages/workspace/WorkspaceInitialPage.js @@ -56,7 +56,7 @@ const defaultProps = { * @param {string} policyID */ function openEditor(policyID) { - Navigation.navigate(ROUTES.WORKSPACE_SETTINGS.getRoute(policyID)); + Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW.getRoute(policyID)); } /** @@ -150,9 +150,9 @@ function WorkspaceInitialPage(props) { const hasCustomUnitsError = PolicyUtils.hasCustomUnitsError(policy); const menuItems = [ { - translationKey: 'workspace.common.settings', + translationKey: 'workspace.common.overview', icon: Expensicons.Gear, - action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_SETTINGS.getRoute(policy.id)))), + action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW.getRoute(policy.id)))), brickRoadIndicator: hasGeneralSettingsError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', }, { diff --git a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js b/src/pages/workspace/WorkspaceOverviewCurrencyPage.js similarity index 97% rename from src/pages/workspace/WorkspaceSettingsCurrencyPage.js rename to src/pages/workspace/WorkspaceOverviewCurrencyPage.js index ce1e1d7b8966..e58f53e60a6e 100644 --- a/src/pages/workspace/WorkspaceSettingsCurrencyPage.js +++ b/src/pages/workspace/WorkspaceOverviewCurrencyPage.js @@ -68,11 +68,11 @@ function WorkspaceSettingsCurrencyPage({currencyList, policy, isLoadingReportDat const headerMessage = searchText.trim() && !currencyItems.length ? translate('common.noResultsFound') : ''; - const onBackButtonPress = useCallback(() => Navigation.goBack(ROUTES.WORKSPACE_SETTINGS.getRoute(policy.id)), [policy.id]); + const onBackButtonPress = useCallback(() => Navigation.goBack(ROUTES.WORKSPACE_OVERVIEW.getRoute(policy.id)), [policy.id]); const onSelectCurrency = (item) => { Policy.updateGeneralSettings(policy.id, policy.name, item.keyForList); - Navigation.goBack(ROUTES.WORKSPACE_SETTINGS.getRoute(policy.id)); + Navigation.goBack(ROUTES.WORKSPACE_OVERVIEW.getRoute(policy.id)); }; return ( diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceOverviewPage.js similarity index 95% rename from src/pages/workspace/WorkspaceSettingsPage.js rename to src/pages/workspace/WorkspaceOverviewPage.js index d121134f26da..eda23089a038 100644 --- a/src/pages/workspace/WorkspaceSettingsPage.js +++ b/src/pages/workspace/WorkspaceOverviewPage.js @@ -56,7 +56,7 @@ const defaultProps = { ...policyDefaultProps, }; -function WorkspaceSettingsPage({policy, currencyList, windowWidth, route}) { +function WorkspaceOverviewPage({policy, currencyList, windowWidth, route}) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -90,15 +90,15 @@ function WorkspaceSettingsPage({policy, currencyList, windowWidth, route}) { return errors; }, []); - const onPressCurrency = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_SETTINGS_CURRENCY.getRoute(policy.id)), [policy.id]); + const onPressCurrency = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW_CURRENCY.getRoute(policy.id)), [policy.id]); const policyName = lodashGet(policy, 'name', ''); return ( {(hasVBA) => ( Date: Mon, 18 Dec 2023 16:21:41 +0700 Subject: [PATCH 0147/1250] fix: add singleExecution for menu items --- src/components/MenuItemGroup.js | 35 +++++++++++++++++++ .../PersonalDetailsInitialPage.js | 3 ++ 2 files changed, 38 insertions(+) create mode 100644 src/components/MenuItemGroup.js diff --git a/src/components/MenuItemGroup.js b/src/components/MenuItemGroup.js new file mode 100644 index 000000000000..60d27247a45f --- /dev/null +++ b/src/components/MenuItemGroup.js @@ -0,0 +1,35 @@ +import useSingleExecution from '@hooks/useSingleExecution'; +import _ from 'lodash'; +import PropTypes from 'prop-types'; +import React, {Children, cloneElement} from 'react'; + +const propTypes = { + /* Actual content wrapped by this component */ + children: PropTypes.node.isRequired, + + /** Whether or not to use the single execution hook */ + shouldUseSingleExecution: PropTypes.bool, +}; +const defaultProps = { + shouldUseSingleExecution: true +}; + +function MenuItemGroup(props){ + const {isExecuting, singleExecution} = useSingleExecution(); + const arrayChildren = Children.toArray(props.children); + + return <> + {Children.map(arrayChildren, (child, index) => { + return cloneElement(child,{ + ...child.props, + onPress: props.shouldUseSingleExecution ? singleExecution(child.props.onPress) : child.props.onPress + }) + })} + +} + +MenuItemGroup.displayName = 'MenuItemGroup'; +MenuItemGroup.propTypes = propTypes; +MenuItemGroup.defaultProps = defaultProps; + +export default MenuItemGroup; \ No newline at end of file diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js index cf6887b7e04c..a35ad78836b8 100644 --- a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js +++ b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js @@ -17,6 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import MenuItemGroup from '@components/MenuItemGroup'; const propTypes = { /* Onyx Props */ @@ -77,6 +78,7 @@ function PersonalDetailsInitialPage(props) { {props.translate('privatePersonalDetails.privateDataMessage')} + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS)} /> + )} From b81373faf01130e8f568e2bc3a63141933b28d78 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Dec 2023 16:32:51 +0700 Subject: [PATCH 0148/1250] disable when is executing --- src/components/MenuItemGroup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MenuItemGroup.js b/src/components/MenuItemGroup.js index 60d27247a45f..4fe5adf989b6 100644 --- a/src/components/MenuItemGroup.js +++ b/src/components/MenuItemGroup.js @@ -1,5 +1,4 @@ import useSingleExecution from '@hooks/useSingleExecution'; -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, {Children, cloneElement} from 'react'; @@ -22,7 +21,8 @@ function MenuItemGroup(props){ {Children.map(arrayChildren, (child, index) => { return cloneElement(child,{ ...child.props, - onPress: props.shouldUseSingleExecution ? singleExecution(child.props.onPress) : child.props.onPress + onPress: props.shouldUseSingleExecution ? singleExecution(child.props.onPress) : child.props.onPress, + disabled: child.props.disabled || isExecuting }) })} From 0853f6c27bb1409faf1b0c361b25e21f9f1e666b Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:58:54 +0100 Subject: [PATCH 0149/1250] cleanup: rename settings screen name --- ...react-navigation+stack+6.3.16+002+dontDetachScreen.patch | 2 +- src/ROUTES.ts | 2 -- src/SCREENS.ts | 1 - src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 3 +-- .../AppNavigator/Navigators/FullScreenNavigator.tsx | 2 +- .../AppNavigator/createCustomStackNavigator/CustomRouter.ts | 2 +- src/libs/Navigation/linkingConfig.ts | 6 +++--- src/libs/Navigation/types.ts | 2 +- src/pages/ShareCodePage.js | 2 +- src/pages/home/sidebar/PressableAvatarWithIndicator.js | 2 +- src/pages/settings/AboutPage/AboutPage.js | 2 +- src/pages/settings/Profile/ProfilePage.js | 2 +- src/pages/settings/Security/SecuritySettingsPage.js | 2 +- src/pages/settings/Wallet/WalletEmptyState.js | 2 +- src/pages/settings/Wallet/WalletPage/WalletPage.js | 2 +- src/pages/workspace/WorkspacesListPage.js | 2 +- 16 files changed, 16 insertions(+), 20 deletions(-) diff --git a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch index 0412f095f5ea..877521094cd4 100644 --- a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch +++ b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch @@ -43,7 +43,7 @@ index 7558eb3..b7bb75e 100644 }) : STATE_TRANSITIONING_OR_BELOW_TOP; } + -+ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'SettingsHome') && isScreenActive !== STATE_ON_TOP; ++ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Settings_Root') && isScreenActive !== STATE_ON_TOP; + const { headerShown = true, diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 103aa77b55e2..1ea8bca94cf6 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -59,8 +59,6 @@ const ROUTES = { }, SETTINGS: 'settings', - SETTINGS_HOME: 'settings_new', - SETTINGS_NEW_PROFILE: 'settings_new/profile', SETTINGS_PROFILE: 'settings/profile', SETTINGS_SHARE_CODE: 'settings/shareCode', SETTINGS_DISPLAY_NAME: 'settings/profile/display-name', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5f87b8deef9f..81e19295d289 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -18,7 +18,6 @@ const SCREENS = { TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', VALIDATE_LOGIN: 'ValidateLogin', UNLINK_LOGIN: 'UnlinkLogin', - SETTINGS_HOME: 'SettingsHome', SETTINGS_CENTRAL_PANE: 'SettingsCentralPane', SETTINGS: { ROOT: 'Settings_Root', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index ac40d45b7a09..c64650f91133 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -178,9 +178,9 @@ const NewTeachersUniteNavigator = createModalStackNavigator require('../../../pages/TeachersUnite/ImTeacherPage').default as React.ComponentType, }); -// should it be merged with SettingsModalStackNavigator? const AccountSettingsModalStackNavigator = createModalStackNavigator( { + [SCREENS.SETTINGS.ROOT]: () => require('../../../pages/settings/InitialSettingsPage').default as React.ComponentType, [SCREENS.SETTINGS.WORKSPACES]: () => require('../../../pages/workspace/WorkspacesListPage').default as React.ComponentType, [SCREENS.SETTINGS.PREFERENCES.ROOT]: () => require('../../../pages/settings/Preferences/PreferencesPage').default as React.ComponentType, [SCREENS.SETTINGS.SECURITY]: () => require('../../../pages/settings/Security/SecuritySettingsPage').default as React.ComponentType, @@ -193,7 +193,6 @@ const AccountSettingsModalStackNavigator = createModalStackNavigator( ); const SettingsModalStackNavigator = createModalStackNavigator({ - [SCREENS.SETTINGS.ROOT]: () => require('../../../pages/settings/InitialSettingsPage').default as React.ComponentType, [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.ROOT]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType, [SCREENS.SETTINGS.PROFILE.PRONOUNS]: () => require('../../../pages/settings/Profile/PronounsPage').default as React.ComponentType, diff --git a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx index 4c65bfa3ae71..cf9a0e5856f7 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/FullScreenNavigator.tsx @@ -20,7 +20,7 @@ function FullScreenNavigator() { diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index 599cb4c71cc6..c79f970239a7 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -76,7 +76,7 @@ const handleSettingsOpened = (state: State) => { state: { routes: [ { - name: SCREENS.SETTINGS_HOME, + name: SCREENS.SETTINGS.ROOT, }, { name: SCREENS.SETTINGS_CENTRAL_PANE, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 89ded1c7edee..f3a31de7e015 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -499,10 +499,10 @@ const linkingConfig: LinkingOptions = { }, [NAVIGATORS.FULL_SCREEN_NAVIGATOR]: { - initialRouteName: SCREENS.SETTINGS_HOME, + initialRouteName: SCREENS.SETTINGS.ROOT, screens: { - [SCREENS.SETTINGS_HOME]: { - path: ROUTES.SETTINGS_HOME, + [SCREENS.SETTINGS.ROOT]: { + path: ROUTES.SETTINGS, }, [SCREENS.SETTINGS_CENTRAL_PANE]: { screens: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 68c4bd21ec62..08a0f9727436 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -384,7 +384,7 @@ type SettingsCentralPaneNavigatorParamList = { }; type FullScreenNavigatorParamList = { - [SCREENS.SETTINGS_HOME]: undefined; + [SCREENS.SETTINGS.ROOT]: undefined; [SCREENS.SETTINGS_CENTRAL_PANE]: NavigatorScreenParams; }; diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index 8a3ba71082a5..5e8fe32b1cf5 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -81,7 +81,7 @@ class ShareCodePage extends React.Component { Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(this.props.report.reportID) : ROUTES.SETTINGS_HOME)} + onBackButtonPress={() => Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(this.props.report.reportID) : ROUTES.SETTINGS.ROOT)} shouldUseCentralPaneView shouldShowBorderBottom /> diff --git a/src/pages/home/sidebar/PressableAvatarWithIndicator.js b/src/pages/home/sidebar/PressableAvatarWithIndicator.js index 32dd4af8e1d7..d72aef2ef824 100644 --- a/src/pages/home/sidebar/PressableAvatarWithIndicator.js +++ b/src/pages/home/sidebar/PressableAvatarWithIndicator.js @@ -46,7 +46,7 @@ function PressableAvatarWithIndicator({isCreateMenuOpen, currentUserPersonalDeta return; } - Navigation.navigate(ROUTES.SETTINGS_HOME); + Navigation.navigate(ROUTES.SETTINGS); }, [isCreateMenuOpen]); return ( diff --git a/src/pages/settings/AboutPage/AboutPage.js b/src/pages/settings/AboutPage/AboutPage.js index ac96c802d597..819c3883fb40 100644 --- a/src/pages/settings/AboutPage/AboutPage.js +++ b/src/pages/settings/AboutPage/AboutPage.js @@ -103,7 +103,7 @@ function AboutPage(props) { <> Navigation.goBack(ROUTES.SETTINGS_HOME)} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS.ROOT)} shouldUseCentralPaneView shouldShowBorderBottom /> diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 197de876cbdc..036f5b5d9671 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -108,7 +108,7 @@ function ProfilePage(props) { > Navigation.goBack(ROUTES.SETTINGS_HOME)} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS.ROOT)} shouldUseCentralPaneView shouldShowBorderBottom /> diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index 95ce2dc7eb6f..52b15c72d7a5 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -66,7 +66,7 @@ function SecuritySettingsPage(props) { return ( Navigation.goBack(ROUTES.SETTINGS_HOME)} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS.ROOT)} shouldShowBackButton illustration={LottieAnimations.Safe} backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.SECURITY].backgroundColor} diff --git a/src/pages/settings/Wallet/WalletEmptyState.js b/src/pages/settings/Wallet/WalletEmptyState.js index c533c68c1b15..28eb3e31e9b4 100644 --- a/src/pages/settings/Wallet/WalletEmptyState.js +++ b/src/pages/settings/Wallet/WalletEmptyState.js @@ -38,7 +38,7 @@ function WalletEmptyState({onAddPaymentMethod}) { Navigation.goBack(ROUTES.SETTINGS_HOME)} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS.ROOT)} title={translate('common.wallet')} footer={ ",showToastMessageOnCopy:!1,toastMessage:"Code copied to clipboard",toastDuration:3e3}},o={...e,...t,copyToClipboardSettings:{...e.copyToClipboardSettings,...t.copyToClipboardSettings}},c=document.querySelectorAll("ul.tab > li > a");if(Array.prototype.forEach.call(c,(t=>{t.addEventListener("click",(e=>{e.preventDefault(),s(t),o.activateTabFromUrl&&n(t),o.syncTabsWithSameLabels&&l(t)}),!1)})),o.addCopyToClipboardButtons){const t=o.copyToClipboardSettings;r(t),t.showToastMessageOnCopy&&i(t.toastMessage)}o.activateTabFromUrl&&a()}}},925:(t,e,o)=>{const{getChildPosition:a,createElementFromHTML:n,findElementsWithTextContent:s,addClass:r}=o(918),l=t=>{const e=t.querySelectorAll("ul > li");Array.prototype.forEach.call(e,(t=>{t.classList.remove("active")}))},i=t=>{const e=t.parentNode,o=e.parentNode,n=a(e);if(e.className.includes("active"))return;const s=o.getAttribute("data-tab");if(!s)return;const r=document.getElementById(s);l(o),l(r),r.querySelectorAll("ul.tab-content > li")[n].classList.add("active"),e.classList.add("active")},c=(t,e)=>{if(navigator.clipboard&&window.isSecureContext)navigator.clipboard.writeText(t);else{const e=document.createElement("textarea");e.value=t,e.style.position="absolute",e.style.left="-999999px",document.body.prepend(e),e.select();try{document.execCommand("copy")}catch(t){console.error(t)}finally{e.remove()}}"function"==typeof e&&e()},d=t=>{r(document.getElementById("jekyll-tabs-copy-to-clipboard-message"),"show",t)};t.exports={removeActiveClasses:l,handleTabClicked:i,copyToClipboard:c,addCopyToClipboardButtons:({buttonHTML:t,showToastMessageOnCopy:e,toastDuration:o})=>{const a=document.querySelectorAll("ul.tab-content > li pre");for(let s=0;s{d(o)}),i.addEventListener("click",(()=>{c(r.innerText,p)}))}},activateTabFromUrl:()=>{const t=window.location.hash?.substring(1);if(!t)return;const e=document.getElementById(t);if(!e)return;const o=new URLSearchParams(window.location.search).get("active_tab");if(!o)return;const a=e.querySelector("li#"+o+" > a");a&&i(a)},updateUrlWithActiveTab:t=>{const e=t.parentNode,o=e.parentNode,a=new URLSearchParams(window.location.search);a.set("active_tab",e.id);const n=window.location.pathname+"?"+a.toString()+"#"+o.id;history.replaceState(null,"",n)},syncTabsWithSameLabels:t=>{const e=s("a",t.textContent);for(let o=0;o{const e=document.createElement("div");e.id="jekyll-tabs-copy-to-clipboard-message",e.textContent=t,document.getElementsByTagName("body")[0].appendChild(e)}}}},e={},function o(a){var n=e[a];if(void 0!==n)return n.exports;var s=e[a]={exports:{}};return t[a](s,s.exports,o),s.exports}(613);var t,e})); - -window.addEventListener('load', function () { - jekyllTabs.init({ - syncTabsWithSameLabels: true, - }); -}); From 1891c00f10b734f45dbbeb0ad652c73a6bd9e9dd Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 01:44:48 +0530 Subject: [PATCH 0849/1250] rm whitespace --- docs/_config.yml | 1 - .../deposit-accounts/Deposit-Accounts-USD.md | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index b68bcdf8a1f3..888f0b24a91e 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -20,6 +20,5 @@ plugins: - jekyll-seo-tag - jekyll-redirect-from - whitelist: - jekyll-redirect-from diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/deposit-accounts/Deposit-Accounts-USD.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/deposit-accounts/Deposit-Accounts-USD.md index 8880e923ce3b..0bc5cb0ad955 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/deposit-accounts/Deposit-Accounts-USD.md +++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/deposit-accounts/Deposit-Accounts-USD.md @@ -2,7 +2,6 @@ title: Deposit Accounts - USD description: How to add a deposit account to receive payments for yourself or your business (US) --- - # Overview There are two types of deposit-only accounts: @@ -75,4 +74,4 @@ There are a few reasons a reimbursement may be unsuccessful. The first step is t If you aren’t sure, please reach out to Concierge and we can assist! -{% include faq-end.md %} \ No newline at end of file +{% include faq-end.md %} From f3e572665d7f48ade61db8f8bfdb59318134241f Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 02:15:23 +0530 Subject: [PATCH 0850/1250] fix lint --- docs/assets/js/main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js index 14f455247dd0..69252be6d33c 100644 --- a/docs/assets/js/main.js +++ b/docs/assets/js/main.js @@ -232,6 +232,7 @@ window.addEventListener('DOMContentLoaded', () => { }); } + // eslint-disable-line es/no-optional-chaining expensifyClassicTab?.addEventListener('click', () => { expensifyClassicTab.classList.add('active'); expensifyClassicContent.classList.remove('hidden'); @@ -244,13 +245,13 @@ window.addEventListener('DOMContentLoaded', () => { }); }); + // eslint-disable-line es/no-optional-chaining newExpensifyTab?.addEventListener('click', () => { newExpensifyTab.classList.add('active'); newExpensifyContent.classList.remove('hidden'); expensifyClassicTab.classList.remove('active'); expensifyClassicContent.classList.add('hidden'); - active = '#new-expensify'; window.tocbot.refresh({ ...tocbotOptions, contentSelector: '#new-expensify' From 8c4c1766d971a9c1a343a7904155c52c68e2d5eb Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 02:16:29 +0530 Subject: [PATCH 0851/1250] fix lint --- docs/assets/js/platform-tabs.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/assets/js/platform-tabs.js b/docs/assets/js/platform-tabs.js index d49d898517c4..97e6efc554e2 100644 --- a/docs/assets/js/platform-tabs.js +++ b/docs/assets/js/platform-tabs.js @@ -1,6 +1,3 @@ -const expensifyClassic = document.getElementById('platform-tab-expensify-classic'); -const newExpensify = document.getElementById('platform-tab-new-expensify'); - const expensifyClassicContent = document.getElementById('expensify-classic'); const newExpensifyContent = document.getElementById('new-expensify'); From 889c4bbbd1539c3470ccc50f3608fa1934a8b060 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 02:22:31 +0530 Subject: [PATCH 0852/1250] fix lint --- docs/assets/js/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js index 69252be6d33c..caafd0ac1c67 100644 --- a/docs/assets/js/main.js +++ b/docs/assets/js/main.js @@ -232,7 +232,7 @@ window.addEventListener('DOMContentLoaded', () => { }); } - // eslint-disable-line es/no-optional-chaining + // eslint-disable-next-line es/no-optional-chaining expensifyClassicTab?.addEventListener('click', () => { expensifyClassicTab.classList.add('active'); expensifyClassicContent.classList.remove('hidden'); @@ -245,7 +245,7 @@ window.addEventListener('DOMContentLoaded', () => { }); }); - // eslint-disable-line es/no-optional-chaining + // eslint-disable-next-line es/no-optional-chaining newExpensifyTab?.addEventListener('click', () => { newExpensifyTab.classList.add('active'); newExpensifyContent.classList.remove('hidden'); From 5771cf87191d3edfa2c9161210a3cfe56e5e7cb8 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 02:31:59 +0530 Subject: [PATCH 0853/1250] add breakpoint for mobile --- docs/_sass/_main.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index 1c0728b152ea..a238b3d0f7f3 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -853,6 +853,11 @@ button { h1 { padding: 0; } + + @include maxBreakpoint($breakpoint-tablet) { + flex-direction: column; + gap: 20px; + } } #platform-tabs { From 97a7a4c01269ccf331ef479dbd4124a2bde93343 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 02:41:39 +0530 Subject: [PATCH 0854/1250] fix lint --- docs/assets/js/main.js | 46 ++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js index caafd0ac1c67..31c480bb8aaf 100644 --- a/docs/assets/js/main.js +++ b/docs/assets/js/main.js @@ -232,31 +232,33 @@ window.addEventListener('DOMContentLoaded', () => { }); } - // eslint-disable-next-line es/no-optional-chaining - expensifyClassicTab?.addEventListener('click', () => { - expensifyClassicTab.classList.add('active'); - expensifyClassicContent.classList.remove('hidden'); - - newExpensifyTab.classList.remove('active'); - newExpensifyContent.classList.add('hidden'); - window.tocbot.refresh({ - ...tocbotOptions, - contentSelector: '#expensify-classic' + if (expensifyClassicTab) { + expensifyClassicTab.addEventListener('click', () => { + expensifyClassicTab.classList.add('active'); + expensifyClassicContent.classList.remove('hidden'); + + newExpensifyTab.classList.remove('active'); + newExpensifyContent.classList.add('hidden'); + window.tocbot.refresh({ + ...tocbotOptions, + contentSelector: '#expensify-classic' + }); }); - }); + } - // eslint-disable-next-line es/no-optional-chaining - newExpensifyTab?.addEventListener('click', () => { - newExpensifyTab.classList.add('active'); - newExpensifyContent.classList.remove('hidden'); - - expensifyClassicTab.classList.remove('active'); - expensifyClassicContent.classList.add('hidden'); - window.tocbot.refresh({ - ...tocbotOptions, - contentSelector: '#new-expensify' + if (newExpensifyTab) { + newExpensifyTab.addEventListener('click', () => { + newExpensifyTab.classList.add('active'); + newExpensifyContent.classList.remove('hidden'); + + expensifyClassicTab.classList.remove('active'); + expensifyClassicContent.classList.add('hidden'); + window.tocbot.refresh({ + ...tocbotOptions, + contentSelector: '#new-expensify' + }); }); - }); + } document.getElementById('header-button').addEventListener('click', toggleHeaderMenu); From b914b57684e3c40d67f19656e27c0b46287fa07b Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 02:43:02 +0530 Subject: [PATCH 0855/1250] Revert "fix lint" This reverts commit 97a7a4c01269ccf331ef479dbd4124a2bde93343. --- docs/assets/js/main.js | 46 ++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js index 31c480bb8aaf..caafd0ac1c67 100644 --- a/docs/assets/js/main.js +++ b/docs/assets/js/main.js @@ -232,33 +232,31 @@ window.addEventListener('DOMContentLoaded', () => { }); } - if (expensifyClassicTab) { - expensifyClassicTab.addEventListener('click', () => { - expensifyClassicTab.classList.add('active'); - expensifyClassicContent.classList.remove('hidden'); - - newExpensifyTab.classList.remove('active'); - newExpensifyContent.classList.add('hidden'); - window.tocbot.refresh({ - ...tocbotOptions, - contentSelector: '#expensify-classic' - }); + // eslint-disable-next-line es/no-optional-chaining + expensifyClassicTab?.addEventListener('click', () => { + expensifyClassicTab.classList.add('active'); + expensifyClassicContent.classList.remove('hidden'); + + newExpensifyTab.classList.remove('active'); + newExpensifyContent.classList.add('hidden'); + window.tocbot.refresh({ + ...tocbotOptions, + contentSelector: '#expensify-classic' }); - } + }); - if (newExpensifyTab) { - newExpensifyTab.addEventListener('click', () => { - newExpensifyTab.classList.add('active'); - newExpensifyContent.classList.remove('hidden'); - - expensifyClassicTab.classList.remove('active'); - expensifyClassicContent.classList.add('hidden'); - window.tocbot.refresh({ - ...tocbotOptions, - contentSelector: '#new-expensify' - }); + // eslint-disable-next-line es/no-optional-chaining + newExpensifyTab?.addEventListener('click', () => { + newExpensifyTab.classList.add('active'); + newExpensifyContent.classList.remove('hidden'); + + expensifyClassicTab.classList.remove('active'); + expensifyClassicContent.classList.add('hidden'); + window.tocbot.refresh({ + ...tocbotOptions, + contentSelector: '#new-expensify' }); - } + }); document.getElementById('header-button').addEventListener('click', toggleHeaderMenu); From bee81409c5f996f08af54285db5b336d2bd18567 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 02:43:42 +0530 Subject: [PATCH 0856/1250] run prettier --- docs/assets/js/main.js | 14 +++++++------- docs/assets/js/platform-tabs.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js index caafd0ac1c67..473bd544c7b8 100644 --- a/docs/assets/js/main.js +++ b/docs/assets/js/main.js @@ -194,7 +194,7 @@ const tocbotOptions = { // If there is a fixed article scroll container, set to calculate titles' offset scrollContainer: 'content-area', -} +}; window.addEventListener('DOMContentLoaded', () => { injectFooterCopywrite(); @@ -212,14 +212,14 @@ window.addEventListener('DOMContentLoaded', () => { const expensifyClassicTab = document.getElementById('platform-tab-expensify-classic'); const newExpensifyTab = document.getElementById('platform-tab-new-expensify'); - + const expensifyClassicContent = document.getElementById('expensify-classic'); const newExpensifyContent = document.getElementById('new-expensify'); let contentSelector; if (expensifyClassicContent) { contentSelector = '#expensify-classic'; - } else if(newExpensifyContent) { + } else if (newExpensifyContent) { contentSelector = '#new-expensify'; } else { contentSelector = '.article-toc-content'; @@ -236,12 +236,12 @@ window.addEventListener('DOMContentLoaded', () => { expensifyClassicTab?.addEventListener('click', () => { expensifyClassicTab.classList.add('active'); expensifyClassicContent.classList.remove('hidden'); - + newExpensifyTab.classList.remove('active'); newExpensifyContent.classList.add('hidden'); window.tocbot.refresh({ ...tocbotOptions, - contentSelector: '#expensify-classic' + contentSelector: '#expensify-classic', }); }); @@ -249,12 +249,12 @@ window.addEventListener('DOMContentLoaded', () => { newExpensifyTab?.addEventListener('click', () => { newExpensifyTab.classList.add('active'); newExpensifyContent.classList.remove('hidden'); - + expensifyClassicTab.classList.remove('active'); expensifyClassicContent.classList.add('hidden'); window.tocbot.refresh({ ...tocbotOptions, - contentSelector: '#new-expensify' + contentSelector: '#new-expensify', }); }); diff --git a/docs/assets/js/platform-tabs.js b/docs/assets/js/platform-tabs.js index 97e6efc554e2..d9890fde9d25 100644 --- a/docs/assets/js/platform-tabs.js +++ b/docs/assets/js/platform-tabs.js @@ -17,7 +17,7 @@ if (newExpensifyContent) { tab.id = 'platform-tab-new-expensify'; if (!expensifyClassicContent) { - tab.classList.add('active'); + tab.classList.add('active'); } platformTabs.appendChild(tab); } From cd576d8291384e4f05ed435c7836f20dca3adb71 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Tue, 30 Jan 2024 02:34:59 +0500 Subject: [PATCH 0857/1250] fix: update recently used field implementation --- src/ONYXKEYS.ts | 6 ++++-- src/libs/actions/Report.ts | 13 ++++++------- src/pages/EditReportFieldDropdownPage.tsx | 10 +++++----- src/pages/EditReportFieldPage.tsx | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7abf6db1769d..cfcae988d6b5 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -243,6 +243,9 @@ const ONYXKEYS = { // Max width supported for HTML element MAX_CANVAS_WIDTH: 'maxCanvasWidth', + // Stores the recently used report fields + RECENTLY_USED_REPORT_FIELDS: 'recentlyUsedReportFields', + /** Collection Keys */ COLLECTION: { DOWNLOAD: 'download_', @@ -256,7 +259,6 @@ const ONYXKEYS = { POLICY_TAX_RATE: 'policyTaxRates_', POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_', POLICY_REPORT_FIELDS: 'policyReportFields_', - POLICY_RECENTLY_USED_REPORT_FIELDS: 'policyRecentlyUsedReportFields_', WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_', WORKSPACE_INVITE_MESSAGE_DRAFT: 'workspaceInviteMessageDraft_', REPORT: 'report_', @@ -440,6 +442,7 @@ type OnyxValues = { [ONYXKEYS.MAX_CANVAS_AREA]: number; [ONYXKEYS.MAX_CANVAS_HEIGHT]: number; [ONYXKEYS.MAX_CANVAS_WIDTH]: number; + [ONYXKEYS.RECENTLY_USED_REPORT_FIELDS]: OnyxTypes.RecentlyUsedReportFields; // Collections [ONYXKEYS.COLLECTION.DOWNLOAD]: OnyxTypes.Download; @@ -451,7 +454,6 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories; [ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS]: OnyxTypes.PolicyReportFields; - [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_REPORT_FIELDS]: OnyxTypes.RecentlyUsedReportFields; [ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMembers; [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: Record; [ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index afb3f3711746..fe273aefd000 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -176,10 +176,9 @@ Linking.getInitialURL().then((url) => { reportIDDeeplinkedFromOldDot = reportID; }); -let allRecentlyUsedReportFields: OnyxCollection = {}; +let allRecentlyUsedReportFields: OnyxEntry = {}; Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_REPORT_FIELDS, - waitForCollectionCallback: true, + key: ONYXKEYS.RECENTLY_USED_REPORT_FIELDS, callback: (val) => (allRecentlyUsedReportFields = val), }); @@ -1526,8 +1525,8 @@ function updatePolicyReportName(reportID: string, value: string) { API.write('RenameReport', parameters, {optimisticData, failureData, successData}); } -function updatePolicyReportField(policyID: string, reportID: string, reportField: PolicyReportField) { - const recentlyUsedValues = allRecentlyUsedReportFields?.[policyID]?.[reportField.fieldID] ?? []; +function updatePolicyReportField(reportID: string, reportField: PolicyReportField) { + const recentlyUsedValues = allRecentlyUsedReportFields?.[reportField.fieldID] ?? []; const optimisticData: OnyxUpdate[] = [ { @@ -1547,7 +1546,7 @@ function updatePolicyReportField(policyID: string, reportID: string, reportField if (reportField.type === 'dropdown') { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_REPORT_FIELDS}${policyID}`, + key: ONYXKEYS.RECENTLY_USED_REPORT_FIELDS, value: { [reportField.fieldID]: [...new Set([...recentlyUsedValues, reportField.value])], }, @@ -1572,7 +1571,7 @@ function updatePolicyReportField(policyID: string, reportID: string, reportField if (reportField.type === 'dropdown') { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_REPORT_FIELDS}${policyID}`, + key: ONYXKEYS.RECENTLY_USED_REPORT_FIELDS, value: { [reportField.fieldID]: recentlyUsedValues, }, diff --git a/src/pages/EditReportFieldDropdownPage.tsx b/src/pages/EditReportFieldDropdownPage.tsx index 448ec5d68773..2815867778eb 100644 --- a/src/pages/EditReportFieldDropdownPage.tsx +++ b/src/pages/EditReportFieldDropdownPage.tsx @@ -32,17 +32,17 @@ type EditReportFieldDropdownPageComponentProps = { }; type EditReportFieldDropdownPageOnyxProps = { - policyRecentlyUsedReportFields: OnyxEntry; + recentlyUsedReportFields: OnyxEntry; }; type EditReportFieldDropdownPageProps = EditReportFieldDropdownPageComponentProps & EditReportFieldDropdownPageOnyxProps; -function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, fieldOptions, policyRecentlyUsedReportFields}: EditReportFieldDropdownPageProps) { +function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, fieldOptions, recentlyUsedReportFields}: EditReportFieldDropdownPageProps) { const [searchValue, setSearchValue] = useState(''); const styles = useThemeStyles(); const {getSafeAreaMargins} = useStyleUtils(); const {translate} = useLocalize(); - const recentlyUsedOptions = useMemo(() => policyRecentlyUsedReportFields?.[fieldID] ?? [], [policyRecentlyUsedReportFields, fieldID]); + const recentlyUsedOptions = useMemo(() => recentlyUsedReportFields?.[fieldID] ?? [], [recentlyUsedReportFields, fieldID]); const sections = useMemo(() => { const filteredRecentOptions = recentlyUsedOptions.filter((option) => option.toLowerCase().includes(searchValue.toLowerCase())); @@ -105,7 +105,7 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, EditReportFieldDropdownPage.displayName = 'EditReportFieldDropdownPage'; export default withOnyx({ - policyRecentlyUsedReportFields: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_REPORT_FIELDS}${policyID}`, + recentlyUsedReportFields: { + key: () => ONYXKEYS.RECENTLY_USED_REPORT_FIELDS, }, })(EditReportFieldDropdownPage); diff --git a/src/pages/EditReportFieldPage.tsx b/src/pages/EditReportFieldPage.tsx index 2cb47918ce34..24ade45993f6 100644 --- a/src/pages/EditReportFieldPage.tsx +++ b/src/pages/EditReportFieldPage.tsx @@ -68,7 +68,7 @@ function EditReportFieldPage({route, policy, report, policyReportFields}: EditRe if (isReportFieldTitle) { ReportActions.updatePolicyReportName(report.reportID, value); } else { - ReportActions.updatePolicyReportField(report.policyID ?? '', report.reportID, {...reportField, value}); + ReportActions.updatePolicyReportField(report.reportID, {...reportField, value}); } Navigation.dismissModal(report?.reportID); From aaeefdcf3f53a53dbe6bcbab100b631a0bfec577 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 05:02:00 +0530 Subject: [PATCH 0858/1250] add gap b/w buttons --- docs/_sass/_main.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index a238b3d0f7f3..0689c1b39b56 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -867,6 +867,7 @@ button { text-align: center; font-weight: 700; font-size: 13px; + gap: 4px; } #platform-tabs > * { From 687ae7a6813b3053c06a11bcf865ed2f7e3874f5 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 05:03:56 +0530 Subject: [PATCH 0859/1250] fix case --- docs/assets/js/platform-tabs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/assets/js/platform-tabs.js b/docs/assets/js/platform-tabs.js index d9890fde9d25..18746efc5a84 100644 --- a/docs/assets/js/platform-tabs.js +++ b/docs/assets/js/platform-tabs.js @@ -5,7 +5,7 @@ const platformTabs = document.getElementById('platform-tabs'); if (expensifyClassicContent) { const tab = document.createElement('div'); - tab.innerHTML = 'Expensify classic'; + tab.innerHTML = 'Expensify Classic'; tab.id = 'platform-tab-expensify-classic'; tab.classList.add('active'); platformTabs.appendChild(tab); @@ -13,7 +13,7 @@ if (expensifyClassicContent) { if (newExpensifyContent) { const tab = document.createElement('div'); - tab.innerHTML = 'New expensify'; + tab.innerHTML = 'New Expensify'; tab.id = 'platform-tab-new-expensify'; if (!expensifyClassicContent) { From 72ead7ba41d8a2c74d9a3302fc68baa6783d1338 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 06:07:39 +0530 Subject: [PATCH 0860/1250] handle long titles --- docs/_sass/_main.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index 0689c1b39b56..f0713796b68e 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -350,6 +350,7 @@ button { h1 { &.title { font-size: 2.25em; + flex: 1; } } @@ -873,7 +874,7 @@ button { #platform-tabs > * { cursor: pointer; border-radius: 20px; - padding: 10px 20px; + padding: 9px 20px; } #platform-tabs > .active { From 47c20b94928ba2ebf58f3d47d5cb66e50f6dc495 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 06:11:28 +0530 Subject: [PATCH 0861/1250] fix color --- docs/_sass/_main.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index f0713796b68e..d4ab85a2fa41 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -878,6 +878,6 @@ button { } #platform-tabs > .active { - color:#fff; - background-color: #1A3D32; + color: $color-text; + background-color: $color-button-background; } From a8468c7568fdf60dcbf5211f74d2ab12ae91965a Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 30 Jan 2024 06:24:13 +0530 Subject: [PATCH 0862/1250] fix button size --- docs/_sass/_main.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss index d4ab85a2fa41..fc95a9cce7f0 100644 --- a/docs/_sass/_main.scss +++ b/docs/_sass/_main.scss @@ -874,7 +874,10 @@ button { #platform-tabs > * { cursor: pointer; border-radius: 20px; - padding: 9px 20px; + padding: 10px 20px; + box-sizing: border-box; + height: 36px; + line-height: 16px; } #platform-tabs > .active { From 4be5bb247457315d805d5674f46a29663013da9d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 30 Jan 2024 11:15:22 +0800 Subject: [PATCH 0863/1250] don't apply the hovered color when the option is focused --- src/components/OptionRow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/OptionRow.tsx b/src/components/OptionRow.tsx index 97e85cacf42d..8cba73a11b5d 100644 --- a/src/components/OptionRow.tsx +++ b/src/components/OptionRow.tsx @@ -209,14 +209,14 @@ function OptionRow({ ) : ( ))} From 1609194b5ddec696afb3e8474c1a05784ddf3184 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 30 Jan 2024 12:43:31 +0800 Subject: [PATCH 0864/1250] fix rbr doesn't show --- src/libs/SidebarUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index ec6abe1e48b2..ace326b533db 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -276,7 +276,7 @@ function getOptionData({ const result: ReportUtils.OptionData = { text: '', alternateText: null, - allReportErrors: undefined, + allReportErrors: OptionsListUtils.getAllReportErrors(report, reportActions), brickRoadIndicator: null, tooltipText: null, subtitle: null, @@ -318,7 +318,6 @@ function getOptionData({ result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); result.shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report); result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom || report.pendingFields.createChat : undefined; - result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions); result.brickRoadIndicator = hasErrors || hasViolations ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; result.ownerAccountID = report.ownerAccountID; result.managerID = report.managerID; From 56d5d2c717e0da30066fee5eda7f0da605fbf50e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 30 Jan 2024 14:29:28 +0700 Subject: [PATCH 0865/1250] refactor logic --- src/libs/ReportActionsUtils.ts | 5 ++++- src/libs/ReportUtils.ts | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index b0f66ee6a99a..ba93d8169dab 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -806,6 +806,9 @@ function hasRequestFromCurrentAccount(reportID: string, currentAccountID: number return reportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && action.actorAccountID === currentAccountID); } +/** + * @private + */ function isReportActionUnread(reportAction: OnyxEntry, lastReadTime: string) { if (!lastReadTime) { return Boolean(!isCreatedAction(reportAction)); @@ -815,7 +818,7 @@ function isReportActionUnread(reportAction: OnyxEntry, lastReadTim } /** - * Check whether the report action of the report is unread or not + * Check whether the current report action of the report is unread or not * */ function isCurrentActionUnread(report: Report | EmptyObject, reportAction: ReportAction, reportActions: ReportActions): boolean { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 960ad55537ea..2646f6c36051 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -12,7 +12,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; import CONST from '@src/CONST'; import type {ParentNavigationSummaryParams, TranslationPaths} from '@src/languages/types'; -import ONYXKEYS from '@src/ONYXKEYS'; +import ONYXKEYS, { OnyxCollectionKey } from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, PersonalDetails, PersonalDetailsList, Policy, PolicyReportField, Report, ReportAction, ReportMetadata, Session, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; @@ -4613,34 +4613,38 @@ function getAllAncestorReportActions( if (!report) { return []; } - const convertReports: OnyxCollection = {}; - const convertReportActions: OnyxCollection = {}; + const convertedReports: OnyxCollection = {}; + const convertedReportActions: OnyxCollection = {}; Object.values(reports ?? {}).forEach((itemReport) => { if (!itemReport) { return; } - convertReports[itemReport.reportID] = itemReport; + convertedReports[itemReport.reportID] = itemReport; }); Object.keys(reportActions ?? {}).forEach((actionKey) => { if (!actionKey) { return; } - const reportID = CollectionUtils.extractCollectionItemID(actionKey as `reportActions_${string}`); - convertReportActions[reportID] = reportActions?.[actionKey] ?? null; + const reportID = CollectionUtils.extractCollectionItemID(actionKey as `${OnyxCollectionKey}${string}`); + convertedReportActions[reportID] = reportActions?.[actionKey] ?? null; }); const allAncestors: Ancestor[] = []; let parentReportID = report.parentReportID; let parentReportActionID = report.parentReportActionID; + // Store the child of parent report let currentReport = report; let currentUnread = shouldHideThreadDividerLine; + while (parentReportID) { - const parentReport = convertReports?.[parentReportID]; - const parentReportAction = convertReportActions?.[parentReportID]?.[parentReportActionID ?? ''] ?? null; + const parentReport = convertedReports?.[parentReportID]; + const parentReportAction = convertedReportActions?.[parentReportID]?.[parentReportActionID ?? ''] ?? null; + if (!parentReportAction || ReportActionsUtils.isTransactionThread(parentReportAction) || !parentReport) { break; } - const isParentReportActionUnread = ReportActionsUtils.isCurrentActionUnread(parentReport, parentReportAction, convertReportActions?.[parentReportID] ?? {}); + + const isParentReportActionUnread = ReportActionsUtils.isCurrentActionUnread(parentReport, parentReportAction, convertedReportActions?.[parentReportID] ?? {}); allAncestors.push({ report: currentReport, reportAction: parentReportAction, From b871e06773a54c0d466c175ed23fc59986e53b9c Mon Sep 17 00:00:00 2001 From: Dylan Date: Tue, 30 Jan 2024 14:40:35 +0700 Subject: [PATCH 0866/1250] hide error if request is paid --- src/components/ReportActionItem/MoneyRequestView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 3121328138ee..dcf602a293d9 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -313,8 +313,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate shouldShowRightIcon={canEditMerchant} titleStyle={styles.flex1} onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.MERCHANT))} - brickRoadIndicator={hasViolations('merchant') || (hasErrors && isEmptyMerchant && isPolicyExpenseChat) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} - error={hasErrors && isPolicyExpenseChat && isEmptyMerchant ? translate('common.error.enterMerchant') : ''} + brickRoadIndicator={hasViolations('merchant') || (!isSettled && !isCancelled && hasErrors && isEmptyMerchant && isPolicyExpenseChat) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + error={!isSettled && !isCancelled && hasErrors && isPolicyExpenseChat && isEmptyMerchant ? translate('common.error.enterMerchant') : ''} /> {canUseViolations && } From 56c81389396909c9c63a71d9e0a63a074634b5a8 Mon Sep 17 00:00:00 2001 From: Amarparab2024 <156438377+Amarparab2024@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:54:07 +0530 Subject: [PATCH 0867/1250] @Amarparab2024 pass shouldEnableMaxHeight as prop --- src/pages/SearchPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index c420371f5a65..11b48645b57d 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -174,6 +174,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports, nav testID={SearchPage.displayName} onEntryTransitionEnd={updateOptions} navigation={navigation} + shouldEnableMaxHeight > {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( <> From 84071e878eea68cbcfc224d9ce6a43359f77b53f Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 30 Jan 2024 09:49:16 +0100 Subject: [PATCH 0868/1250] Migrate WorkspacesListPage to TS --- ...acesListPage.js => WorkspacesListPage.tsx} | 228 +++++++++--------- src/pages/workspace/WorkspacesListRow.tsx | 4 +- 2 files changed, 118 insertions(+), 114 deletions(-) rename src/pages/workspace/{WorkspacesListPage.js => WorkspacesListPage.tsx} (66%) diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.tsx similarity index 66% rename from src/pages/workspace/WorkspacesListPage.js rename to src/pages/workspace/WorkspacesListPage.tsx index addd8c3c5e38..142571e23679 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -1,8 +1,8 @@ -import PropTypes from 'prop-types'; import React, {useCallback, useMemo, useState} from 'react'; import {FlatList, ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import ConfirmModal from '@components/ConfirmModal'; import FeatureList from '@components/FeatureList'; @@ -10,7 +10,9 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import LottieAnimations from '@components/LottieAnimations'; +import type {MenuItemProps} from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import type {OfflineWithFeedbackProps} from '@components/OfflineWithFeedback'; import {PressableWithoutFeedback} from '@components/Pressable'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -19,59 +21,59 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import policyMemberPropType from '@pages/policyMemberPropType'; -import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes'; -import reportPropTypes from '@pages/reportPropTypes'; import * as App from '@userActions/App'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; +import type {PolicyMembers, Policy as PolicyType, ReimbursementAccount, Report} from '@src/types/onyx'; +import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; +import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import WorkspacesListRow from './WorkspacesListRow'; -const propTypes = { - /** The list of this user's policies */ - policies: PropTypes.objectOf( - PropTypes.shape({ - /** The ID of the policy */ - ID: PropTypes.string, +type WorkspaceItem = Required> & + Pick & + Pick & + Pick & { + action: () => void; + dismissError: () => void; + iconType?: ValueOf; + policyID?: string; + adminRoom?: string | null; + announceRoom?: string | null; + }; - /** The name of the policy */ - name: PropTypes.string, +// eslint-disable-next-line react/no-unused-prop-types +type GetMenuItem = {item: WorkspaceItem; index: number}; - /** The type of the policy */ - type: PropTypes.string, +type ChatType = { + adminRoom?: string | null; + announceRoom?: string | null; +}; - /** The user's role in the policy */ - role: PropTypes.string, +type ChatPolicyType = Record; - /** The current action that is waiting to happen on the policy */ - pendingAction: PropTypes.oneOf(_.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)), - }), - ), +type WorkspaceListPageOnyxProps = { + /** The list of this user's policies */ + policies: OnyxCollection; /** Bank account attached to free plan */ - reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes, + reimbursementAccount: OnyxEntry; /** A collection of objects for all policies which key policy member objects by accountIDs */ - allPolicyMembers: PropTypes.objectOf(PropTypes.objectOf(policyMemberPropType)), + allPolicyMembers: OnyxCollection; /** All reports shared with the user (coming from Onyx) */ - reports: PropTypes.objectOf(reportPropTypes), + reports: OnyxCollection; }; -const defaultProps = { - policies: {}, - allPolicyMembers: {}, - reimbursementAccount: {}, - reports: {}, -}; +type WorkspaceListPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceListPageOnyxProps; const workspaceFeatures = [ { @@ -90,11 +92,8 @@ const workspaceFeatures = [ /** * Dismisses the errors on one item - * - * @param {string} policyID - * @param {string} pendingAction */ -function dismissWorkspaceError(policyID, pendingAction) { +function dismissWorkspaceError(policyID: string, pendingAction: OnyxCommon.PendingAction | undefined) { if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { Policy.clearDeleteWorkspaceError(policyID); return; @@ -107,8 +106,7 @@ function dismissWorkspaceError(policyID, pendingAction) { throw new Error('Not implemented'); } -// TODO: Rewrite this component to TS according to existing migration on the main branch -function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, reports}) { +function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, reports}: WorkspaceListPageProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -116,21 +114,19 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r const {isSmallScreenWidth} = useWindowDimensions(); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); - const [policyIDToDelete, setPolicyIDToDelete] = useState(null); - const [policyNameToDelete, setPolicyNameToDelete] = useState(null); + const [policyIDToDelete, setPolicyIDToDelete] = useState(''); + const [policyNameToDelete, setPolicyNameToDelete] = useState(''); const confirmDeleteAndHideModal = () => { Policy.deleteWorkspace(policyIDToDelete, [], policyNameToDelete); setIsDeleteModalOpen(false); }; + /** * Gets the menu item for each workspace - * - * @param {Object} item - * @returns {JSX} */ const getMenuItem = useCallback( - ({item}) => { + ({item, index}: GetMenuItem) => { const threeDotsMenuItems = [ // Check if the user is an admin of the workspace ...(item.role === CONST.POLICY.ROLE.ADMIN @@ -139,7 +135,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r icon: Expensicons.Trashcan, text: translate('workspace.common.delete'), onSelected: () => { - setPolicyIDToDelete(item.policyID); + setPolicyIDToDelete(item.policyID ?? ''); setPolicyNameToDelete(item.title); setIsDeleteModalOpen(true); }, @@ -147,30 +143,31 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r { icon: Expensicons.Hashtag, text: translate('workspace.common.goToRoom', {roomName: CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS}), - onSelected: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.adminRoom)), + onSelected: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.adminRoom ?? '')), }, ] : []), { icon: Expensicons.Hashtag, text: translate('workspace.common.goToRoom', {roomName: CONST.REPORT.WORKSPACE_CHAT_ROOMS.ANNOUNCE}), - onSelected: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.announceRoom)), + onSelected: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.announceRoom ?? '')), }, ]; return ( item.action()} + onPress={item.action} > {({hovered}) => ( - _.reduce( - reports, - (result, report) => { - if (!report || !report.reportID || !report.policyID) { - return result; - } + const policyRooms = useMemo(() => { + if (!reports || isEmptyObject(reports)) { + return; + } - if (!result[report.policyID]) { - // eslint-disable-next-line no-param-reassign - result[report.policyID] = {}; - } + return Object.values(reports).reduce((result, report) => { + if (!report?.reportID || !report.policyID) { + return result; + } - switch (report.chatType) { - case CONST.REPORT.CHAT_TYPE.POLICY_ADMINS: - // eslint-disable-next-line no-param-reassign - result[report.policyID].adminRoom = report.reportID; - break; - case CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE: - // eslint-disable-next-line no-param-reassign - result[report.policyID].announceRoom = report.reportID; - break; - default: - break; - } + if (!result[report.policyID]) { + // eslint-disable-next-line no-param-reassign + result[report.policyID] = {}; + } - return result; - }, - {}, - ), - [reports], - ); + switch (report.chatType) { + case CONST.REPORT.CHAT_TYPE.POLICY_ADMINS: + // eslint-disable-next-line no-param-reassign + result[report.policyID].adminRoom = report.reportID; + break; + case CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE: + // eslint-disable-next-line no-param-reassign + result[report.policyID].announceRoom = report.reportID; + break; + default: + break; + } + + return result; + }, {}); + }, [reports]); /** * Add free policies (workspaces) to the list of menu items and returns the list of menu items - * @returns {Array} the menu item list */ const workspaces = useMemo(() => { - const reimbursementAccountBrickRoadIndicator = !_.isEmpty(reimbursementAccount.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; - return _.chain(policies) - .filter((policy) => PolicyUtils.shouldShowPolicy(policy, isOffline)) - .map((policy) => ({ - title: policy.name, - icon: policy.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy.name), - iconType: policy.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, - action: () => Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policy.id)), - iconFill: theme.textLight, - fallbackIcon: Expensicons.FallbackWorkspaceAvatar, - brickRoadIndicator: reimbursementAccountBrickRoadIndicator || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, allPolicyMembers), - pendingAction: policy.pendingAction, - errors: policy.errors, - dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), - disabled: policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - policyID: policy.id, - adminRoom: policyRooms[policy.id] ? policyRooms[policy.id].adminRoom : null, - announceRoom: policyRooms[policy.id] ? policyRooms[policy.id].announceRoom : null, - ownerAccountID: policy.ownerAccountID, - role: policy.role, - })) - .sortBy((policy) => policy.title.toLowerCase()) - .value(); - }, [reimbursementAccount.errors, policies, isOffline, theme.textLight, allPolicyMembers, policyRooms]); + const reimbursementAccountBrickRoadIndicator = reimbursementAccount?.errors ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined; + if (isEmptyObject(policies)) { + return []; + } + + return Object.values(policies) + .filter((policy): policy is PolicyType => PolicyUtils.shouldShowPolicy(policy, !!isOffline)) + .map( + (policy): WorkspaceItem => ({ + title: policy.name, + icon: policy.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy.name), + action: () => Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policy.id)), + brickRoadIndicator: reimbursementAccountBrickRoadIndicator ?? PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, allPolicyMembers), + pendingAction: policy.pendingAction, + errors: policy.errors, + dismissError: () => { + if (!policy.pendingAction) { + return; + } + dismissWorkspaceError(policy.id, policy.pendingAction); + }, + disabled: policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + iconType: policy.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, + iconFill: theme.textLight, + fallbackIcon: Expensicons.FallbackWorkspaceAvatar, + policyID: policy.id, + adminRoom: policyRooms?.[policy.id] ? policyRooms[policy.id].adminRoom : null, + announceRoom: policyRooms?.[policy.id] ? policyRooms[policy.id].announceRoom : null, + ownerAccountID: policy.ownerAccountID, + role: policy.role, + type: policy.type, + }), + ) + .sort((a, b) => a.title.toLowerCase().localeCompare(b.title.toLowerCase())); + }, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, allPolicyMembers, policyRooms]); - if (_.isEmpty(workspaces)) { + if (isEmptyObject(workspaces)) { return ( App.createWorkspaceWithPolicyDraftAndNavigateToIt()} + // @ts-expect-error TODO: Remove once FeatureList (https://github.com/Expensify/App/issues/25039) is migrated to TS illustration={LottieAnimations.WorkspacePlanet} illustrationBackgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.WORKSPACES].backgroundColor} // We use this style to vertically center the illustration, as the original illustration is not centered @@ -336,6 +342,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r ({ policies: { key: ONYXKEYS.COLLECTION.POLICY, }, @@ -389,5 +393,5 @@ export default compose( reports: { key: ONYXKEYS.COLLECTION.REPORT, }, - }), -)(WorkspacesListPage); + })(WorkspacesListPage), +); diff --git a/src/pages/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index f2ba9a3e6355..0ef871ecf432 100644 --- a/src/pages/workspace/WorkspacesListRow.tsx +++ b/src/pages/workspace/WorkspacesListRow.tsx @@ -26,8 +26,8 @@ type WorkspacesListRowProps = WithCurrentUserPersonalDetailsProps & { /** Account ID of the workspace's owner */ ownerAccountID?: number; - /** Type of workspace. Type personal is not valid in this context so it's omitted */ - workspaceType: typeof CONST.POLICY.TYPE.FREE | typeof CONST.POLICY.TYPE.CORPORATE | typeof CONST.POLICY.TYPE.TEAM; + /** Type of workspace */ + workspaceType?: ValueOf; /** Icon to show next to the workspace name */ workspaceIcon?: AvatarSource; From 80dc8cb62a85374bdde1b7ef005a991dc0720dfb Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 30 Jan 2024 09:49:22 +0100 Subject: [PATCH 0869/1250] fix access to WS overview (188) --- src/pages/workspace/WorkspaceOverviewPage.js | 1 + src/pages/workspace/WorkspacePageWithSections.tsx | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/WorkspaceOverviewPage.js b/src/pages/workspace/WorkspaceOverviewPage.js index 7115e1092384..dd3945136c47 100644 --- a/src/pages/workspace/WorkspaceOverviewPage.js +++ b/src/pages/workspace/WorkspaceOverviewPage.js @@ -70,6 +70,7 @@ function WorkspaceOverviewPage({policy, currencyList, route}) { shouldShowLoading={false} shouldUseScrollView shouldShowOfflineIndicatorInWideScreen + shouldShowNonAdmin > {(hasVBA) => ( <> diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 2eadc82b6c28..ce9d82ed9d9d 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -67,6 +67,9 @@ type WorkspacePageWithSectionsProps = WithPolicyAndFullscreenLoadingProps & shouldShowOfflineIndicatorInWideScreen?: boolean; + /** Whether to show this page to non admin policy members */ + shouldShowNonAdmin?: boolean; + /** Policy values needed in the component */ policy: OnyxEntry; }; @@ -94,12 +97,12 @@ function WorkspacePageWithSections({ user, shouldShowLoading = true, shouldShowOfflineIndicatorInWideScreen = false, + shouldShowNonAdmin = false, }: WorkspacePageWithSectionsProps) { const styles = useThemeStyles(); useNetwork({onReconnect: () => fetchData(shouldSkipVBBACall)}); const isLoading = reimbursementAccount?.isLoading ?? true; - const isOverview = route.name; const achState = reimbursementAccount?.achData?.state ?? ''; const isUsingECard = user?.isUsingExpensifyCard ?? false; const policyID = route.params.policyID; @@ -124,9 +127,8 @@ function WorkspacePageWithSections({ return true; } - // TODO - check is the value of isOveriew is correct - return !PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPendingDeletePolicy(policy) || !isOverview; - }, [isOverview, policy]); + return (!PolicyUtils.isPolicyAdmin(policy) && !shouldShowNonAdmin) || PolicyUtils.isPendingDeletePolicy(policy); + }, [shouldShowNonAdmin, policy]); return ( Date: Tue, 30 Jan 2024 09:15:14 +0000 Subject: [PATCH 0870/1250] fix: remove params --- src/libs/PersonalDetailsUtils.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 945c0b581b16..2391874c83f3 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -203,10 +203,6 @@ function getEffectiveDisplayName(personalDetail?: PersonalDetails): string | und /** * Creates a new displayName for a user based on passed personal details or login. - * - * @param login - user's login - * @param passedPersonalDetails - details object with firstName and lastName - * @returns - The effective display name */ function createDisplayName(login: string, passedPersonalDetails: Pick | OnyxEntry): string { // If we have a number like +15857527441@expensify.sms then let's remove @expensify.sms and format it @@ -229,11 +225,6 @@ function createDisplayName(login: string, passedPersonalDetails: Pick Date: Tue, 30 Jan 2024 10:42:41 +0100 Subject: [PATCH 0871/1250] fix WS switching when non admin (209) --- src/libs/Navigation/switchPolicyID.ts | 7 ++++++- src/libs/Navigation/types.ts | 1 + src/pages/WorkspaceSwitcherPage.js | 5 +++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index fe3abaaf5546..521045ddb89f 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -60,7 +60,7 @@ function getActionForBottomTabNavigator(action: StackNavigationAction, state: Na }; } -export default function switchPolicyID(navigation: NavigationContainerRef | null, {policyID, route}: switchPolicyIDParams) { +export default function switchPolicyID(navigation: NavigationContainerRef | null, {policyID, route, isPolicyAdmin = true}: switchPolicyIDParams) { if (!navigation) { throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?"); } @@ -119,6 +119,11 @@ export default function switchPolicyID(navigation: NavigationContainerRef { - const policyID = option.policyID; + const {policyID, isPolicyAdmin} = option; if (policyID) { setSelectedOption(option); @@ -121,7 +121,7 @@ function WorkspaceSwitcherPage({policies}) { setActiveWorkspaceID(policyID); Navigation.goBack(); if (policyID !== activeWorkspaceID) { - Navigation.navigateWithSwitchPolicyID({policyID}); + Navigation.navigateWithSwitchPolicyID({policyID, isPolicyAdmin}); } }, []); @@ -147,6 +147,7 @@ function WorkspaceSwitcherPage({policies}) { ], boldStyle: hasUnreadData(policy.id), keyForList: policy.id, + isPolicyAdmin: PolicyUtils.isPolicyAdmin(policy), })) .value(), [policies, getIndicatorTypeForPolicy, hasUnreadData], From bc6e500f1653e8e14f3670826a2a40a0b0c9610b Mon Sep 17 00:00:00 2001 From: Dylan Date: Tue, 30 Jan 2024 17:09:43 +0700 Subject: [PATCH 0872/1250] fix lint --- src/components/ReportActionItem/MoneyRequestView.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index dcf602a293d9..f8be53e101cc 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -313,7 +313,11 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate shouldShowRightIcon={canEditMerchant} titleStyle={styles.flex1} onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.MERCHANT))} - brickRoadIndicator={hasViolations('merchant') || (!isSettled && !isCancelled && hasErrors && isEmptyMerchant && isPolicyExpenseChat) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={ + hasViolations('merchant') || (!isSettled && !isCancelled && hasErrors && isEmptyMerchant && isPolicyExpenseChat) + ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR + : '' + } error={!isSettled && !isCancelled && hasErrors && isPolicyExpenseChat && isEmptyMerchant ? translate('common.error.enterMerchant') : ''} /> {canUseViolations && } From 044d52165692e71a2fe2845dbe6e8ca0fea80f57 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 30 Jan 2024 11:10:35 +0100 Subject: [PATCH 0873/1250] Handle displaying brick roads on the chat button tab --- .../BottomTabBar.tsx | 29 +++++---- src/libs/WorkspacesUtils.ts | 64 ++++++++++++++++--- src/pages/home/sidebar/AllSettingsScreen.tsx | 4 +- src/styles/index.ts | 6 +- 4 files changed, 75 insertions(+), 28 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index d7e561f67b6a..0fe0e7557610 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -14,7 +14,7 @@ import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList} from '@libs/Navigation/types'; -import {checkIfWorkspaceHasError} from '@libs/WorkspacesUtils'; +import {checkIfWorkspaceSettingsTabHasRBR, getChatTabBrickRoad} from '@libs/WorkspacesUtils'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -34,7 +34,9 @@ function BottomTabBar() { return topmostBottomTabRoute?.name ?? SCREENS.HOME; }); - const showWorkspaceRedBrickRoad = checkIfWorkspaceHasError(activeWorkspaceID) && currentTabName === SCREENS.HOME; + const showWorkspaceRedBrickRoad = checkIfWorkspaceSettingsTabHasRBR(activeWorkspaceID) && currentTabName === SCREENS.HOME; + + const chatTabBrickRoad = currentTabName !== SCREENS.HOME ? getChatTabBrickRoad(activeWorkspaceID) : undefined; return ( @@ -48,12 +50,17 @@ function BottomTabBar() { wrapperStyle={styles.flexGrow1} style={styles.bottomTabBarItem} > - + + + {chatTabBrickRoad && ( + + )} + @@ -76,11 +83,7 @@ function BottomTabBar() { width={variables.iconBottomBar} height={variables.iconBottomBar} /> - {showWorkspaceRedBrickRoad && ( - - )} + {showWorkspaceRedBrickRoad && } diff --git a/src/libs/WorkspacesUtils.ts b/src/libs/WorkspacesUtils.ts index ae6489f10217..f0d93183e480 100644 --- a/src/libs/WorkspacesUtils.ts +++ b/src/libs/WorkspacesUtils.ts @@ -71,7 +71,7 @@ const getBrickRoadForPolicy = (report: Report): BrickRoad => { return shouldShowGreenDotIndicator ? CONST.BRICK_ROAD.GBR : undefined; }; -function hasGlobalWorkspaceError(policies: OnyxCollection, policyMembers: OnyxCollection) { +function hasGlobalWorkspaceSettingsRBR(policies: OnyxCollection, policyMembers: OnyxCollection) { const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => !!policy)); const cleanAllPolicyMembers = Object.fromEntries(Object.entries(policyMembers ?? {}).filter(([, policyMemberValues]) => !!policyMemberValues)); @@ -85,16 +85,52 @@ function hasGlobalWorkspaceError(policies: OnyxCollection, policyMembers return errorCheckingMethods.some((errorCheckingMethod) => errorCheckingMethod()); } -function hasWorkspaceRedBrickRoad(policy: Policy) { +function hasWorkspaceSettingsRBR(policy: Policy) { const policyMemberError = allPolicyMembers ? hasPolicyMemberError(allPolicyMembers[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policy.id}`]) : false; - return hasPolicyError(policy) || hasCustomUnitsError(policy) || policyMemberError; + return Object.keys(reimbursementAccount?.errors ?? {}).length > 0 || hasPolicyError(policy) || hasCustomUnitsError(policy) || policyMemberError; } -function checkIfWorkspaceHasError(policyID?: string) { - // TODO: Handle reimbursmentAccount error +function getChatTabBrickRoad(policyID?: string): BrickRoad | undefined { + if (!allReports) { + return undefined; + } + + let brickRoad: BrickRoad | undefined; + + Object.keys(allReports).forEach((report) => { + if (brickRoad === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR) { + return; + } + + if (policyID && policyID !== allReports?.[report]?.policyID) { + return; + } + + const policyReport = allReports ? allReports[report] : null; + + if (!policyReport) { + return; + } + + const workspaceBrickRoad = getBrickRoadForPolicy(policyReport); + + if (workspaceBrickRoad === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR) { + brickRoad = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; + return; + } + + if (!brickRoad && workspaceBrickRoad) { + brickRoad = workspaceBrickRoad; + } + }); + + return brickRoad; +} + +function checkIfWorkspaceSettingsTabHasRBR(policyID?: string) { if (!policyID) { - return hasGlobalWorkspaceError(allPolicies, allPolicyMembers); + return hasGlobalWorkspaceSettingsRBR(allPolicies, allPolicyMembers); } const policy = allPolicies ? allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] : null; @@ -102,7 +138,7 @@ function checkIfWorkspaceHasError(policyID?: string) { return false; } - return hasWorkspaceRedBrickRoad(policy); + return hasWorkspaceSettingsRBR(policy); } /** @@ -123,7 +159,7 @@ function getWorkspacesBrickRoads(): Record { return; } - if (hasWorkspaceRedBrickRoad(policy)) { + if (hasWorkspaceSettingsRBR(policy)) { workspacesBrickRoadsMap[policy.id] = CONST.BRICK_ROAD.RBR; } }); @@ -131,7 +167,7 @@ function getWorkspacesBrickRoads(): Record { Object.keys(allReports).forEach((report) => { const policyID = allReports?.[report]?.policyID ?? CONST.POLICY.EMPTY; const policyReport = allReports ? allReports[report] : null; - if (!policyID || !policyReport || workspacesBrickRoadsMap[policyID] === CONST.BRICK_ROAD.RBR) { + if (!policyReport || workspacesBrickRoadsMap[policyID] === CONST.BRICK_ROAD.RBR) { return; } const workspaceBrickRoad = getBrickRoadForPolicy(policyReport); @@ -175,5 +211,13 @@ function getWorkspacesUnreadStatuses(): Record { return workspacesUnreadStatuses; } -export {getBrickRoadForPolicy, getWorkspacesBrickRoads, getWorkspacesUnreadStatuses, hasGlobalWorkspaceError, checkIfWorkspaceHasError, hasWorkspaceRedBrickRoad}; +export { + getBrickRoadForPolicy, + getWorkspacesBrickRoads, + getWorkspacesUnreadStatuses, + hasGlobalWorkspaceSettingsRBR, + checkIfWorkspaceSettingsTabHasRBR, + hasWorkspaceSettingsRBR, + getChatTabBrickRoad, +}; export type {BrickRoad}; diff --git a/src/pages/home/sidebar/AllSettingsScreen.tsx b/src/pages/home/sidebar/AllSettingsScreen.tsx index 655bf8fd824f..200c7ca634b7 100644 --- a/src/pages/home/sidebar/AllSettingsScreen.tsx +++ b/src/pages/home/sidebar/AllSettingsScreen.tsx @@ -11,7 +11,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; -import {hasGlobalWorkspaceError} from '@libs/WorkspacesUtils'; +import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesUtils'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -47,7 +47,7 @@ function AllSettingsScreen({policies, policyMembers}: AllSettingsScreenProps) { })(); }, focused: !isSmallScreenWidth, - brickRoadIndicator: hasGlobalWorkspaceError(policies, policyMembers) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + brickRoadIndicator: hasGlobalWorkspaceSettingsRBR(policies, policyMembers) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }, { translationKey: 'allSettingsScreen.subscriptions', diff --git a/src/styles/index.ts b/src/styles/index.ts index b5ab85c6fd92..ba2f66b5d709 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1380,9 +1380,9 @@ const styles = (theme: ThemeColors) => zIndex: 10, } satisfies ViewStyle), - bottomTabStatusIndicator: { + bottomTabStatusIndicator: (backgroundColor = theme.danger) => ({ borderColor: theme.sidebar, - backgroundColor: theme.danger, + backgroundColor, borderRadius: 8, borderWidth: 2, position: 'absolute', @@ -1391,7 +1391,7 @@ const styles = (theme: ThemeColors) => height: 16, width: 16, zIndex: 10, - }, + }), floatingActionButton: { backgroundColor: theme.success, From cfc6291bb64e83a13ac93f4b191a92b6bfe52ea2 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 30 Jan 2024 11:12:03 +0100 Subject: [PATCH 0874/1250] fix test --- tests/ui/UnreadIndicatorsTest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index e4d4d877f66b..6da7005bfb77 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -43,6 +43,7 @@ jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ jest.mock('react-native-reanimated', () => ({ ...jest.requireActual('react-native-reanimated/mock'), createAnimatedPropAdapter: jest.fn, + useReducedMotion: jest.fn, })); /** From 106e8d78161d42d279306c0e115668cd82062fa5 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 30 Jan 2024 11:24:01 +0100 Subject: [PATCH 0875/1250] Refactor bottomTabStatusIndicator style --- src/styles/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index ba2f66b5d709..84e23ebc652e 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1386,10 +1386,10 @@ const styles = (theme: ThemeColors) => borderRadius: 8, borderWidth: 2, position: 'absolute', - right: -10, - top: -10, - height: 16, - width: 16, + right: -3, + top: -4, + height: 12, + width: 12, zIndex: 10, }), From 22e06e0e973fd32a6d7ff35bc42e502f79ed4990 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 30 Jan 2024 11:37:58 +0100 Subject: [PATCH 0876/1250] Remove BrickRoadsUtils file --- src/libs/BrickRoadsUtils.ts | 74 ------------------------------------- 1 file changed, 74 deletions(-) delete mode 100644 src/libs/BrickRoadsUtils.ts diff --git a/src/libs/BrickRoadsUtils.ts b/src/libs/BrickRoadsUtils.ts deleted file mode 100644 index db7cc40a7940..000000000000 --- a/src/libs/BrickRoadsUtils.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type {OnyxCollection} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report} from '@src/types/onyx'; -import * as OptionsListUtils from './OptionsListUtils'; -import * as ReportActionsUtils from './ReportActionsUtils'; -import * as ReportUtils from './ReportUtils'; - -let allReports: OnyxCollection; - -type BrickRoad = ValueOf | undefined; - -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - -/** - * @param report - * @returns BrickRoad for the policy passed as a param - */ -const getBrickRoadForPolicy = (report: Report): BrickRoad => { - const reportActions = ReportActionsUtils.getAllReportActions(report.reportID); - const reportErrors = OptionsListUtils.getAllReportErrors(report, reportActions); - const doesReportContainErrors = Object.keys(reportErrors ?? {}).length !== 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined; - if (doesReportContainErrors) { - return CONST.BRICK_ROAD.RBR; - } - - // To determine if the report requires attention from the current user, we need to load the parent report action - let itemParentReportAction = {}; - if (report.parentReportID) { - const itemParentReportActions = ReportActionsUtils.getAllReportActions(report.parentReportID); - itemParentReportAction = report.parentReportActionID ? itemParentReportActions[report.parentReportActionID] : {}; - } - const reportOption = {...report, isUnread: ReportUtils.isUnread(report), isUnreadWithMention: ReportUtils.isUnreadWithMention(report)}; - const shouldShowGreenDotIndicator = ReportUtils.requiresAttentionFromCurrentUser(reportOption, itemParentReportAction); - return shouldShowGreenDotIndicator ? CONST.BRICK_ROAD.GBR : undefined; -}; - -/** - * @returns a map where the keys are policyIDs and the values are BrickRoads for each policy - */ -function getWorkspacesBrickRoads(): Record { - if (!allReports) { - return {}; - } - - // The key in this map is the workspace id - const workspacesBrickRoadsMap: Record = {}; - - Object.keys(allReports).forEach((report) => { - const policyID = allReports?.[report]?.policyID; - const policyReport = allReports ? allReports[report] : null; - if (!policyID || !policyReport || workspacesBrickRoadsMap[policyID] === CONST.BRICK_ROAD.RBR) { - return; - } - const workspaceBrickRoad = getBrickRoadForPolicy(policyReport); - - if (!workspaceBrickRoad && !!workspacesBrickRoadsMap[policyID]) { - return; - } - - workspacesBrickRoadsMap[policyID] = workspaceBrickRoad; - }); - - return workspacesBrickRoadsMap; -} - -export {getBrickRoadForPolicy, getWorkspacesBrickRoads}; -export type {BrickRoad}; From 15cffe2c5e3d97036d855bbd8237a092cd3b29f5 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 30 Jan 2024 11:41:30 +0100 Subject: [PATCH 0877/1250] Fix Onyx types import --- src/components/ReportActionItem/MoneyRequestAction.tsx | 2 +- src/components/ReportActionItem/MoneyRequestPreview.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index 51c814c1b2c6..ff29bf5b0ee8 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -1,7 +1,7 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx/lib/types'; +import type {OnyxEntry} from 'react-native-onyx'; import RenderHTML from '@components/RenderHTML'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; diff --git a/src/components/ReportActionItem/MoneyRequestPreview.tsx b/src/components/ReportActionItem/MoneyRequestPreview.tsx index c17c5ecd4129..bde8063690b6 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview.tsx @@ -5,7 +5,7 @@ import React from 'react'; import {View} from 'react-native'; import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx/lib/types'; +import type {OnyxEntry} from 'react-native-onyx'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MoneyRequestSkeletonView from '@components/MoneyRequestSkeletonView'; From 303454c85dc6f76d734e53b9ef2c50eb98ff436d Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 30 Jan 2024 11:47:24 +0100 Subject: [PATCH 0878/1250] Fix margin for a brick road in MenuItem --- src/components/MenuItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 10dccfcf1c5c..d93a8bbbc9ee 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -573,7 +573,7 @@ function MenuItem( )} {!!brickRoadIndicator && ( - + Date: Tue, 30 Jan 2024 11:48:11 +0100 Subject: [PATCH 0879/1250] Fix Onyx types import --- src/pages/settings/Security/CloseAccountPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Security/CloseAccountPage.tsx b/src/pages/settings/Security/CloseAccountPage.tsx index 2287752cd786..f1a0d064fe0a 100644 --- a/src/pages/settings/Security/CloseAccountPage.tsx +++ b/src/pages/settings/Security/CloseAccountPage.tsx @@ -3,7 +3,7 @@ import Str from 'expensify-common/lib/str'; import React, {useEffect, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx/lib/types'; +import type {OnyxEntry} from 'react-native-onyx'; import ConfirmModal from '@components/ConfirmModal'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; From 7df2fb79a1a458a448df1b9d708a58e8071f0b32 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 30 Jan 2024 18:12:28 +0700 Subject: [PATCH 0880/1250] merge main --- src/libs/ReportUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2646f6c36051..c0a8337da4b2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -12,7 +12,8 @@ import * as Expensicons from '@components/Icon/Expensicons'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; import CONST from '@src/CONST'; import type {ParentNavigationSummaryParams, TranslationPaths} from '@src/languages/types'; -import ONYXKEYS, { OnyxCollectionKey } from '@src/ONYXKEYS'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type { OnyxCollectionKey } from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, PersonalDetails, PersonalDetailsList, Policy, PolicyReportField, Report, ReportAction, ReportMetadata, Session, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; From d13b95469eab345d9cc5252dcb0a1d26ff7a8074 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 30 Jan 2024 18:17:25 +0700 Subject: [PATCH 0881/1250] fix lint --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c0a8337da4b2..e446c630b1c9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -13,7 +13,7 @@ import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvata import CONST from '@src/CONST'; import type {ParentNavigationSummaryParams, TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type { OnyxCollectionKey } from '@src/ONYXKEYS'; +import type {OnyxCollectionKey} from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, PersonalDetails, PersonalDetailsList, Policy, PolicyReportField, Report, ReportAction, ReportMetadata, Session, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; @@ -4644,7 +4644,7 @@ function getAllAncestorReportActions( if (!parentReportAction || ReportActionsUtils.isTransactionThread(parentReportAction) || !parentReport) { break; } - + const isParentReportActionUnread = ReportActionsUtils.isCurrentActionUnread(parentReport, parentReportAction, convertedReportActions?.[parentReportID] ?? {}); allAncestors.push({ report: currentReport, From 5cbebb3e20b38067de2732ffb44cec0d268aa4b8 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 30 Jan 2024 12:23:25 +0100 Subject: [PATCH 0882/1250] CR fixes --- src/pages/workspace/WorkspacesListPage.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 142571e23679..0dc29394b6d5 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -93,7 +93,7 @@ const workspaceFeatures = [ /** * Dismisses the errors on one item */ -function dismissWorkspaceError(policyID: string, pendingAction: OnyxCommon.PendingAction | undefined) { +function dismissWorkspaceError(policyID: string, pendingAction: OnyxCommon.PendingAction) { if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { Policy.clearDeleteWorkspaceError(policyID); return; @@ -114,10 +114,14 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r const {isSmallScreenWidth} = useWindowDimensions(); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); - const [policyIDToDelete, setPolicyIDToDelete] = useState(''); - const [policyNameToDelete, setPolicyNameToDelete] = useState(''); + const [policyIDToDelete, setPolicyIDToDelete] = useState(); + const [policyNameToDelete, setPolicyNameToDelete] = useState(); const confirmDeleteAndHideModal = () => { + if (!policyIDToDelete || !policyNameToDelete) { + return; + } + Policy.deleteWorkspace(policyIDToDelete, [], policyNameToDelete); setIsDeleteModalOpen(false); }; From d8b642dce2245c07579598e0f556e7a03d113190 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Tue, 30 Jan 2024 12:32:20 +0100 Subject: [PATCH 0883/1250] dont save not-found path --- src/libs/Navigation/NavigationRoot.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index c424000dc2a0..f022f5c6a657 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -1,5 +1,5 @@ import type {NavigationState} from '@react-navigation/native'; -import {DefaultTheme, NavigationContainer} from '@react-navigation/native'; +import {DefaultTheme, findFocusedRoute, NavigationContainer} from '@react-navigation/native'; import React, {useEffect, useMemo, useRef} from 'react'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useCurrentReportID from '@hooks/useCurrentReportID'; @@ -10,6 +10,7 @@ import Log from '@libs/Log'; import {getPathFromURL} from '@libs/Url'; import {updateLastVisitedPath} from '@userActions/App'; import type {Route} from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import AppNavigator from './AppNavigator'; import getPolicyIdFromState from './getPolicyIdFromState'; import linkingConfig from './linkingConfig'; @@ -41,7 +42,12 @@ function parseAndLogRoute(state: NavigationState) { } const currentPath = customGetPathFromState(state, linkingConfig.config); - updateLastVisitedPath(currentPath); + + const focusedRoute = findFocusedRoute(state); + + if (focusedRoute?.name !== SCREENS.NOT_FOUND) { + updateLastVisitedPath(currentPath); + } // Don't log the route transitions from OldDot because they contain authTokens if (currentPath.includes('/transition')) { From 671cb6eff15305dc68c295269ad62354a8180718 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:57:42 +0100 Subject: [PATCH 0884/1250] fix offline indicator on full page not found view (191) --- src/pages/workspace/WorkspacePageWithSections.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index ce9d82ed9d9d..736baf0ae90e 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -136,7 +136,7 @@ function WorkspacePageWithSections({ shouldEnablePickerAvoiding={false} shouldEnableMaxHeight testID={WorkspacePageWithSections.displayName} - shouldShowOfflineIndicatorInWideScreen={shouldShowOfflineIndicatorInWideScreen} + shouldShowOfflineIndicatorInWideScreen={shouldShowOfflineIndicatorInWideScreen && !shouldShow} > Date: Tue, 30 Jan 2024 09:01:06 +0100 Subject: [PATCH 0885/1250] Refactor ReportScreen perf tests --- tests/perf-test/ReportScreen.perf-test.js | 60 +++++++++++------------ tests/utils/ReportTestUtils.js | 9 +++- tests/utils/collections/reportActions.ts | 21 ++++++-- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/tests/perf-test/ReportScreen.perf-test.js b/tests/perf-test/ReportScreen.perf-test.js index 5a144e715f5b..f5bc3f985227 100644 --- a/tests/perf-test/ReportScreen.perf-test.js +++ b/tests/perf-test/ReportScreen.perf-test.js @@ -15,7 +15,10 @@ import * as Localize from '../../src/libs/Localize'; import ONYXKEYS from '../../src/ONYXKEYS'; import {ReportAttachmentsProvider} from '../../src/pages/home/report/ReportAttachmentsContext'; import ReportScreen from '../../src/pages/home/ReportScreen'; -import * as LHNTestUtils from '../utils/LHNTestUtils'; +import createCollection from '../utils/collections/createCollection'; +import createPersonalDetails from '../utils/collections/personalDetails'; +import createRandomPolicy from '../utils/collections/policies'; +import createRandomReport from '../utils/collections/reports'; import PusherHelper from '../utils/PusherHelper'; import * as ReportTestUtils from '../utils/ReportTestUtils'; import * as TestHelper from '../utils/TestHelper'; @@ -56,6 +59,7 @@ jest.mock('../../src/hooks/useEnvironment', () => jest.mock('../../src/libs/Permissions', () => ({ canUseLinkPreviews: jest.fn(() => true), + canUseDefaultRooms: jest.fn(() => true), })); jest.mock('../../src/hooks/usePermissions.ts'); @@ -103,6 +107,18 @@ afterEach(() => { PusherHelper.teardown(); }); +const policies = createCollection( + (item) => `${ONYXKEYS.COLLECTION.POLICY}${item.id}`, + (index) => createRandomPolicy(index), + 10, +); + +const personalDetails = createCollection( + (item) => item.accountID, + (index) => createPersonalDetails(index), + 20, +); + /** * This is a helper function to create a mock for the addListener function of the react-navigation library. * The reason we need this is because we need to trigger the transitionEnd event in our tests to simulate @@ -152,7 +168,11 @@ function ReportScreenWrapper(args) { ); } -test.skip('[ReportScreen] should render ReportScreen with composer interactions', () => { +const report = {...createRandomReport(1), policyID: '1'}; +const reportActions = ReportTestUtils.getMockedReportActionsMap(500); +const mockRoute = {params: {reportID: '1'}}; + +test('[ReportScreen] should render ReportScreen with composer interactions', () => { const {triggerTransitionEnd, addListener} = createAddListenerMock(); const scenario = async () => { /** @@ -166,9 +186,6 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions' await act(triggerTransitionEnd); - // Query for the report list - await screen.findByTestId('report-actions-list'); - // Query for the composer const composer = await screen.findByTestId('composer'); @@ -189,15 +206,6 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions' await screen.findByLabelText(hintHeaderText); }; - const policy = { - policyID: 1, - name: 'Testing Policy', - }; - - const report = LHNTestUtils.getFakeReport(); - const reportActions = ReportTestUtils.getMockedReportActionsMap(1000); - const mockRoute = {params: {reportID: '1'}}; - const navigation = {addListener}; return waitForBatchedUpdates() @@ -206,9 +214,9 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions' [ONYXKEYS.IS_SIDEBAR_LOADED]: true, [`${ONYXKEYS.COLLECTION.REPORT}${mockRoute.params.reportID}`]: report, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${mockRoute.params.reportID}`]: reportActions, - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: personalDetails, [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + [`${ONYXKEYS.COLLECTION.POLICY}`]: policies, [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${mockRoute.params.reportID}`]: { isLoadingReportActions: false, }, @@ -225,7 +233,7 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions' ); }); -test.skip('[ReportScreen] should press of the report item', () => { +test('[ReportScreen] should press of the report item', () => { const {triggerTransitionEnd, addListener} = createAddListenerMock(); const scenario = async () => { /** @@ -239,12 +247,9 @@ test.skip('[ReportScreen] should press of the report item', () => { await act(triggerTransitionEnd); - // Query for the report list + // // Query for the report list await screen.findByTestId('report-actions-list'); - // Query for the composer - await screen.findByTestId('composer'); - const hintReportPreviewText = Localize.translateLocal('iou.viewDetails'); // Query for report preview buttons @@ -254,15 +259,6 @@ test.skip('[ReportScreen] should press of the report item', () => { fireEvent.press(reportPreviewButtons[0]); }; - const policy = { - policyID: 123, - name: 'Testing Policy', - }; - - const report = LHNTestUtils.getFakeReport(); - const reportActions = ReportTestUtils.getMockedReportActionsMap(1000); - const mockRoute = {params: {reportID: '2'}}; - const navigation = {addListener}; return waitForBatchedUpdates() @@ -271,9 +267,9 @@ test.skip('[ReportScreen] should press of the report item', () => { [ONYXKEYS.IS_SIDEBAR_LOADED]: true, [`${ONYXKEYS.COLLECTION.REPORT}${mockRoute.params.reportID}`]: report, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${mockRoute.params.reportID}`]: reportActions, - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: personalDetails, [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + [`${ONYXKEYS.COLLECTION.POLICY}`]: policies, [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${mockRoute.params.reportID}`]: { isLoadingReportActions: false, }, diff --git a/tests/utils/ReportTestUtils.js b/tests/utils/ReportTestUtils.js index 910f2200876b..86899e4045f6 100644 --- a/tests/utils/ReportTestUtils.js +++ b/tests/utils/ReportTestUtils.js @@ -1,4 +1,5 @@ import _ from 'underscore'; +import createRandomReportAction from './collections/reportActions'; const actionNames = ['ADDCOMMENT', 'IOU', 'REPORTPREVIEW', 'CLOSED']; @@ -51,7 +52,13 @@ const getMockedReportActionsMap = (length = 100) => { const mockReports = Array.from({length}, (__, i) => { const reportID = i + 1; const actionName = i === 0 ? 'CREATED' : actionNames[i % actionNames.length]; - const reportAction = getFakeReportAction(reportID, actionName); + const reportAction = { + ...createRandomReportAction(reportID), + actionName, + originalMessage: { + linkedReportID: reportID.toString(), + }, + }; return {[reportID]: reportAction}; }); diff --git a/tests/utils/collections/reportActions.ts b/tests/utils/collections/reportActions.ts index cc258e89c041..4efff8b3748a 100644 --- a/tests/utils/collections/reportActions.ts +++ b/tests/utils/collections/reportActions.ts @@ -1,4 +1,4 @@ -import {rand, randAggregation, randBoolean, randPastDate, randWord} from '@ngneat/falso'; +import {rand, randAggregation, randBoolean, randWord} from '@ngneat/falso'; import CONST from '@src/CONST'; import type {ReportAction} from '@src/types/onyx'; @@ -17,6 +17,19 @@ const flattenActionNamesValues = (actionNames: any) => { return result; }; +const addZero = (value: number): string | number => (value < 10 ? `0${value}` : value); + +const getRandomDate = (): string => { + const randomTimestamp = Math.random() * new Date().getTime(); + const randomDate = new Date(randomTimestamp); + + const formattedDate = `${randomDate.getFullYear()}-${addZero(randomDate.getMonth() + 1)}-${addZero(randomDate.getDate())} ${addZero(randomDate.getHours())}:${addZero( + randomDate.getMinutes(), + )}:${addZero(randomDate.getSeconds())}.${randomDate.getMilliseconds()}`; + + return formattedDate; +}; + export default function createRandomReportAction(index: number): ReportAction { return { // we need to add any here because of the way we are generating random values @@ -32,7 +45,7 @@ export default function createRandomReportAction(index: number): ReportAction { text: randWord(), }, ], - created: randPastDate().toISOString(), + created: getRandomDate(), message: [ { type: randWord(), @@ -57,13 +70,13 @@ export default function createRandomReportAction(index: number): ReportAction { ], originalMessage: { html: randWord(), - type: rand(Object.values(CONST.IOU.REPORT_ACTION_TYPE)), + lastModified: getRandomDate(), }, whisperedToAccountIDs: randAggregation(), avatar: randWord(), automatic: randBoolean(), shouldShow: randBoolean(), - lastModified: randPastDate().toISOString(), + lastModified: getRandomDate(), pendingAction: rand(Object.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)), delegateAccountID: index, errors: {}, From 1dd6943016b8eec1f5f9d07ae34b9dbc28a0475b Mon Sep 17 00:00:00 2001 From: someone-here Date: Tue, 30 Jan 2024 18:31:04 +0530 Subject: [PATCH 0886/1250] Fix conflict --- src/libs/API/parameters/AddWorkspaceRoomParams.ts | 2 +- src/libs/API/parameters/UpdateRoomDescriptionParams.ts | 6 ++++++ src/libs/API/parameters/UpdateWelcomeMessageParams.ts | 6 ------ src/libs/API/parameters/index.ts | 2 +- src/libs/API/types.ts | 4 ++-- src/libs/actions/Report.ts | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 src/libs/API/parameters/UpdateRoomDescriptionParams.ts delete mode 100644 src/libs/API/parameters/UpdateWelcomeMessageParams.ts diff --git a/src/libs/API/parameters/AddWorkspaceRoomParams.ts b/src/libs/API/parameters/AddWorkspaceRoomParams.ts index f7cbff9565ef..847324586cb5 100644 --- a/src/libs/API/parameters/AddWorkspaceRoomParams.ts +++ b/src/libs/API/parameters/AddWorkspaceRoomParams.ts @@ -9,7 +9,7 @@ type AddWorkspaceRoomParams = { reportName?: string; visibility?: ValueOf; writeCapability?: WriteCapability; - welcomeMessage?: string; + description?: string; }; export default AddWorkspaceRoomParams; diff --git a/src/libs/API/parameters/UpdateRoomDescriptionParams.ts b/src/libs/API/parameters/UpdateRoomDescriptionParams.ts new file mode 100644 index 000000000000..4d78d07a9189 --- /dev/null +++ b/src/libs/API/parameters/UpdateRoomDescriptionParams.ts @@ -0,0 +1,6 @@ +type UpdateRoomDescriptionParams = { + reportID: string; + description: string; +}; + +export default UpdateRoomDescriptionParams; diff --git a/src/libs/API/parameters/UpdateWelcomeMessageParams.ts b/src/libs/API/parameters/UpdateWelcomeMessageParams.ts deleted file mode 100644 index a2d3b59fe3fa..000000000000 --- a/src/libs/API/parameters/UpdateWelcomeMessageParams.ts +++ /dev/null @@ -1,6 +0,0 @@ -type UpdateWelcomeMessageParams = { - reportID: string; - welcomeMessage: string; -}; - -export default UpdateWelcomeMessageParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 039398c0fbf6..58b1e4fa9d3c 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -83,7 +83,7 @@ export type {default as TogglePinnedChatParams} from './TogglePinnedChatParams'; export type {default as DeleteCommentParams} from './DeleteCommentParams'; export type {default as UpdateCommentParams} from './UpdateCommentParams'; export type {default as UpdateReportNotificationPreferenceParams} from './UpdateReportNotificationPreferenceParams'; -export type {default as UpdateWelcomeMessageParams} from './UpdateWelcomeMessageParams'; +export type {default as UpdateRoomDescriptionParams} from './UpdateRoomDescriptionParams'; export type {default as UpdateReportWriteCapabilityParams} from './UpdateReportWriteCapabilityParams'; export type {default as AddWorkspaceRoomParams} from './AddWorkspaceRoomParams'; export type {default as UpdatePolicyRoomNameParams} from './UpdatePolicyRoomNameParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index f58ebc30b4a2..203bac4eecc6 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -84,7 +84,7 @@ const WRITE_COMMANDS = { DELETE_COMMENT: 'DeleteComment', UPDATE_COMMENT: 'UpdateComment', UPDATE_REPORT_NOTIFICATION_PREFERENCE: 'UpdateReportNotificationPreference', - UPDATE_WELCOME_MESSAGE: 'UpdateWelcomeMessage', + UPDATE_ROOM_DESCRIPTION: 'UpdateRoomDescription', UPDATE_REPORT_WRITE_CAPABILITY: 'UpdateReportWriteCapability', ADD_WORKSPACE_ROOM: 'AddWorkspaceRoom', UPDATE_POLICY_ROOM_NAME: 'UpdatePolicyRoomName', @@ -192,7 +192,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.DELETE_COMMENT]: Parameters.DeleteCommentParams; [WRITE_COMMANDS.UPDATE_COMMENT]: Parameters.UpdateCommentParams; [WRITE_COMMANDS.UPDATE_REPORT_NOTIFICATION_PREFERENCE]: Parameters.UpdateReportNotificationPreferenceParams; - [WRITE_COMMANDS.UPDATE_WELCOME_MESSAGE]: Parameters.UpdateWelcomeMessageParams; + [WRITE_COMMANDS.UPDATE_ROOM_DESCRIPTION]: Parameters.UpdateRoomDescriptionParams; [WRITE_COMMANDS.UPDATE_REPORT_WRITE_CAPABILITY]: Parameters.UpdateReportWriteCapabilityParams; [WRITE_COMMANDS.ADD_WORKSPACE_ROOM]: Parameters.AddWorkspaceRoomParams; [WRITE_COMMANDS.UPDATE_POLICY_ROOM_NAME]: Parameters.UpdatePolicyRoomNameParams; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 40644276722a..dd8d6f4ed186 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -38,7 +38,7 @@ import type { UpdateReportNotificationPreferenceParams, UpdateReportPrivateNoteParams, UpdateReportWriteCapabilityParams, - UpdateWelcomeMessageParams, + UpdateRoomDescriptionParams, } from '@libs/API/parameters'; import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as CollectionUtils from '@libs/CollectionUtils'; @@ -1486,9 +1486,9 @@ function updateDescription(reportID: string, previousValue: string, newValue: st }, ]; - const parameters: UpdateWelcomeMessageParams = {reportID, description: parsedDescription}; + const parameters: UpdateRoomDescriptionParams = {reportID, description: parsedDescription}; - API.write(WRITE_COMMANDS.UPDATE_WELCOME_MESSAGE, parameters, {optimisticData, failureData}); + API.write(WRITE_COMMANDS.UPDATE_ROOM_DESCRIPTION, parameters, {optimisticData, failureData}); Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID)); } From f8a926b872c189024ff7a47c50972d456d91c390 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 30 Jan 2024 14:19:22 +0100 Subject: [PATCH 0887/1250] Fix goBack when workspace with given policyID does not exist --- src/pages/workspace/WorkspacePageWithSections.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 3e6e47000a4a..b4aaaf76446c 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -104,7 +104,12 @@ function WorkspacePageWithSections({ const {isSmallScreenWidth} = useWindowDimensions(); const firstRender = useRef(true); - const goBack = () => Navigation.goBack(ROUTES.SETTINGS_WORKSPACES); + const goBack = () => { + Navigation.goBack(ROUTES.SETTINGS_WORKSPACES); + + // Needed when workspace with given policyID does not exist + Navigation.navigateWithSwitchPolicyID({route: ROUTES.ALL_SETTINGS}); + }; useEffect(() => { // Because isLoading is false before merging in Onyx, we need firstRender ref to display loading page as well before isLoading is change to true From 76cbb772fe5e4370374d57cd19a27ee3695d3d75 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Tue, 30 Jan 2024 13:23:07 +0000 Subject: [PATCH 0888/1250] Revert testing change --- src/components/MoneyReportHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 470d03e639ad..4b4e3915f969 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -78,7 +78,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; - const shouldShowNextStep = !!nextStep?.message?.length; + const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport.currency); From e63e68c1f9945355a078dbc594395b90dbed3329 Mon Sep 17 00:00:00 2001 From: mkhutornyi Date: Tue, 30 Jan 2024 14:53:17 +0100 Subject: [PATCH 0889/1250] remove unused popover ref callbacks --- src/components/PopoverProvider/index.tsx | 6 ------ src/components/PopoverProvider/types.ts | 2 -- 2 files changed, 8 deletions(-) diff --git a/src/components/PopoverProvider/index.tsx b/src/components/PopoverProvider/index.tsx index a738d1f9798a..69728d7be126 100644 --- a/src/components/PopoverProvider/index.tsx +++ b/src/components/PopoverProvider/index.tsx @@ -27,9 +27,6 @@ function PopoverContextProvider(props: PopoverContextProps) { } activePopoverRef.current.close(); - if (activePopoverRef.current.onCloseCallback) { - activePopoverRef.current.onCloseCallback(); - } activePopoverRef.current = null; setIsOpen(false); }, []); @@ -107,9 +104,6 @@ function PopoverContextProvider(props: PopoverContextProps) { closePopover(activePopoverRef.current.anchorRef); } activePopoverRef.current = popoverParams; - if (popoverParams?.onOpenCallback) { - popoverParams.onOpenCallback(); - } setIsOpen(true); }, [closePopover], diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index 49705d7ea7a8..2a366ae2a712 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -16,8 +16,6 @@ type AnchorRef = { ref: RefObject; close: (anchorRef?: RefObject) => void; anchorRef: RefObject; - onOpenCallback?: () => void; - onCloseCallback?: () => void; }; export type {PopoverContextProps, PopoverContextValue, AnchorRef}; From 93dd12ac1376f002abc1a7a1f88507f62079e763 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 30 Jan 2024 14:58:17 +0100 Subject: [PATCH 0890/1250] TS fixes after merging main --- src/libs/ReportActionsUtils.ts | 6 ++-- src/libs/actions/IOU.ts | 32 ++++++++++--------- .../IntroSchoolPrincipalPage.tsx | 1 - 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 79de9f7785af..94a929ec8b94 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -438,9 +438,9 @@ function replaceBaseURL(reportAction: ReportAction): ReportAction { /** */ -function getLastVisibleAction(reportID: string, actionsToMerge: ReportActions = {}): OnyxEntry { - const reportActions = Object.values(fastMerge(allReportActions?.[reportID] ?? {}, actionsToMerge, true)); - const visibleReportActions = Object.values(reportActions ?? {}).filter((action) => shouldReportActionBeVisibleAsLastAction(action)); +function getLastVisibleAction(reportID: string, actionsToMerge: OnyxCollection = {}): OnyxEntry { + const reportActions = Object.values(fastMerge(allReportActions?.[reportID] ?? {}, actionsToMerge ?? {}, true)); + const visibleReportActions = Object.values(reportActions ?? {}).filter((action): action is ReportAction => shouldReportActionBeVisibleAsLastAction(action)); const sortedReportActions = getSortedReportActions(visibleReportActions, true); if (sortedReportActions.length === 0) { return null; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0963a6d2c027..f1e147db2af9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4,7 +4,6 @@ import fastMerge from 'expensify-common/lib/fastMerge'; import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; -import OnyxUtils from 'react-native-onyx/lib/utils'; import type {ValueOf} from 'type-fest'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; import * as API from '@libs/API'; @@ -60,7 +59,10 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; -type MoneyRequestRoute = StackScreenProps['route']; +type MoneyRequestRoute = StackScreenProps< + MoneyRequestNavigatorParamList, + typeof SCREENS.MONEY_REQUEST.CATEGORY | typeof SCREENS.MONEY_REQUEST.TAG | typeof SCREENS.MONEY_REQUEST.CONFIRMATION +>['route']; type IOURequestType = ValueOf; @@ -794,7 +796,7 @@ function getMoneyRequestInformation( // to remind me to do this. const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]; if (existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { - optimisticTransaction = fastMerge(existingTransaction, optimisticTransaction); + optimisticTransaction = fastMerge(existingTransaction, optimisticTransaction, false); } // STEP 4: Build optimistic reportActions. We need: @@ -1441,7 +1443,7 @@ function createSplitsAndOnyxData( value: {pendingAction: null}, }, { - onyxMethod: Onyx.METHOD.SET, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, value: null, }, @@ -1464,7 +1466,7 @@ function createSplitsAndOnyxData( }, }, { - onyxMethod: Onyx.METHOD.SET, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, value: null, }, @@ -2278,7 +2280,7 @@ function setDraftSplitTransaction(transactionID: string, transactionChanges: Tra const updatedTransaction = draftSplitTransaction ? TransactionUtils.getUpdatedTransaction(draftSplitTransaction, transactionChanges, false, false) : null; - Onyx.merge(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, updatedTransaction ?? {}); + Onyx.merge(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, updatedTransaction); } function editRegularMoneyRequest(transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges) { @@ -2348,7 +2350,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: updatedTransaction ?? {}, + value: updatedTransaction, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2482,7 +2484,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, - value: chatReport ?? {}, + value: chatReport, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2659,7 +2661,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - value: updatedIOUReport ?? {}, + value: updatedIOUReport, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2772,7 +2774,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor : { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - value: iouReport ?? {}, + value: iouReport, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -3016,8 +3018,10 @@ function getSendMoneyParams( }, }; - // Add an optimistic created action to the optimistic chat reportActions data - optimisticChatReportActionsData.value[optimisticCreatedAction.reportActionID] = optimisticCreatedAction; + if (optimisticChatReportActionsData.value) { + // Add an optimistic created action to the optimistic chat reportActions data + optimisticChatReportActionsData.value[optimisticCreatedAction.reportActionID] = optimisticCreatedAction; + } } else { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -3436,7 +3440,7 @@ function detachReceipt(transactionID: string) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: transaction ?? {}, + value: transaction, }, ]; @@ -3582,7 +3586,6 @@ function navigateToNextPage(iou: OnyxEntry, iouType: string, repo // If we're adding a receipt, that means the user came from the confirmation page and we need to navigate back to it. if (path.slice(1) === ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType, report?.reportID)) { - // @ts-expect-error TODO: Remove this once NewRequestAmountPage (https://github.com/Expensify/App/issues/34614) and NewDistanceRequestPage (https://github.com/Expensify/App/issues/34610) are removed. Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report?.reportID)); return; } @@ -3601,7 +3604,6 @@ function navigateToNextPage(iou: OnyxEntry, iouType: string, repo resetMoneyRequestCategory(); resetMoneyRequestTag(); } - // @ts-expect-error TODO: Remove this once NewRequestAmountPage (https://github.com/Expensify/App/issues/34614) and NewDistanceRequestPage (https://github.com/Expensify/App/issues/34610) are removed. Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); return; } diff --git a/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx b/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx index 5f5ca39c638a..d5900330e4ba 100644 --- a/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx +++ b/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx @@ -22,7 +22,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {LoginList} from '@src/types/onyx'; -import type {Errors} from '@src/types/onyx/OnyxCommon'; type IntroSchoolPrincipalPageOnyxProps = { loginList: OnyxEntry; From cad6895caae16fe44aa5882db63942d5034f4ec8 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 30 Jan 2024 15:13:54 +0100 Subject: [PATCH 0891/1250] test --- tests/perf-test/SignInPage.perf-test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/perf-test/SignInPage.perf-test.tsx b/tests/perf-test/SignInPage.perf-test.tsx index 80964c3c49cd..7bc08c92b115 100644 --- a/tests/perf-test/SignInPage.perf-test.tsx +++ b/tests/perf-test/SignInPage.perf-test.tsx @@ -29,6 +29,16 @@ jest.mock('../../src/libs/Navigation/Navigation', () => { } as typeof Navigation; }); +jest.mock('../../src/ROUTES', () => { + const actualRoutes = jest.requireActual('../../src/ROUTES'); + return { + ...actualRoutes, + HYBRID_APP_ROUTES: { + MONEY_REQUEST_CREATE: '/request/new/scan', + }, + }; +}); + const mockedNavigate = jest.fn(); jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); From 67cd4c468bd94c99a61e611607d4010c70e1e6e7 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Mon, 29 Jan 2024 16:30:25 +0000 Subject: [PATCH 0892/1250] [TS migration] Migrate rn-fetch-block mock --- __mocks__/rn-fetch-blob.js | 4 ---- __mocks__/rn-fetch-blob.ts | 11 +++++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) delete mode 100644 __mocks__/rn-fetch-blob.js create mode 100644 __mocks__/rn-fetch-blob.ts diff --git a/__mocks__/rn-fetch-blob.js b/__mocks__/rn-fetch-blob.js deleted file mode 100644 index 4d179e730903..000000000000 --- a/__mocks__/rn-fetch-blob.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - DocumentDir: jest.fn(), - ImageCache: jest.fn(), -}; diff --git a/__mocks__/rn-fetch-blob.ts b/__mocks__/rn-fetch-blob.ts new file mode 100644 index 000000000000..80d827de32ea --- /dev/null +++ b/__mocks__/rn-fetch-blob.ts @@ -0,0 +1,11 @@ +type RnFetchBlob = { + DocumentDir: jest.Mock + ImageCache: jest.Mock +} + +const RnFetchBlobMock: RnFetchBlob = { + DocumentDir: jest.fn(), + ImageCache: jest.fn(), +} + +export default RnFetchBlobMock From a49414c8836e7df9df9ef3954449f4cf4b5aa35a Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Mon, 29 Jan 2024 17:01:30 +0000 Subject: [PATCH 0893/1250] [TS migration][rn-fetch-blob] Prettify and lint --- __mocks__/rn-fetch-blob.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/__mocks__/rn-fetch-blob.ts b/__mocks__/rn-fetch-blob.ts index 80d827de32ea..fb4cdf6bf958 100644 --- a/__mocks__/rn-fetch-blob.ts +++ b/__mocks__/rn-fetch-blob.ts @@ -1,11 +1,11 @@ type RnFetchBlob = { - DocumentDir: jest.Mock - ImageCache: jest.Mock -} + DocumentDir: jest.Mock; + ImageCache: jest.Mock; +}; const RnFetchBlobMock: RnFetchBlob = { DocumentDir: jest.fn(), ImageCache: jest.fn(), -} +}; -export default RnFetchBlobMock +export default RnFetchBlobMock; From f78414e9139d7250fa4a691e1d0c95e2b1523007 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 30 Jan 2024 15:56:22 +0100 Subject: [PATCH 0894/1250] Minor code improvement --- src/libs/ValidationUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 41054f47a27c..b8bc5046b57d 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -73,7 +73,7 @@ function isValidPastDate(date: string | Date): boolean { /** * Used to validate a value that is "required". */ -function isRequiredFulfilled(value: string | number | Date | unknown[] | Record | null): boolean { +function isRequiredFulfilled(value: string | Date | unknown[] | Record | null): boolean { if (typeof value === 'string') { return !StringUtils.isEmptyString(value); } From 383d224410b5af27e9b1d1a71e44f2171fcd21ba Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 30 Jan 2024 16:03:01 +0100 Subject: [PATCH 0895/1250] Add BrickRoadIndicatorIconProps in WorkspacesListRow --- src/pages/workspace/WorkspacesListRow.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index 6449d1f78396..2f74d265c004 100644 --- a/src/pages/workspace/WorkspacesListRow.tsx +++ b/src/pages/workspace/WorkspacesListRow.tsx @@ -51,6 +51,10 @@ type WorkspacesListRowProps = WithCurrentUserPersonalDetailsProps & { brickRoadIndicator?: ValueOf; }; +type BrickRoadIndicatorIconProps = { + brickRoadIndicator?: ValueOf; +}; + const workspaceTypeIcon = (workspaceType: WorkspacesListRowProps['workspaceType']): IconAsset => { switch (workspaceType) { case CONST.POLICY.TYPE.FREE: @@ -64,7 +68,7 @@ const workspaceTypeIcon = (workspaceType: WorkspacesListRowProps['workspaceType' } }; -function BrickRoadIndicatorIcon({brickRoadIndicator}: {brickRoadIndicator?: ValueOf}) { +function BrickRoadIndicatorIcon({brickRoadIndicator}: BrickRoadIndicatorIconProps) { const theme = useTheme(); return brickRoadIndicator ? ( From 4c681ef95d6703fd66b58b6489389708c20e16c6 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 30 Jan 2024 16:55:23 +0100 Subject: [PATCH 0896/1250] test --- tests/perf-test/SignInPage.perf-test.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/perf-test/SignInPage.perf-test.tsx b/tests/perf-test/SignInPage.perf-test.tsx index 7bc08c92b115..94f535ef2afc 100644 --- a/tests/perf-test/SignInPage.perf-test.tsx +++ b/tests/perf-test/SignInPage.perf-test.tsx @@ -29,15 +29,9 @@ jest.mock('../../src/libs/Navigation/Navigation', () => { } as typeof Navigation; }); -jest.mock('../../src/ROUTES', () => { - const actualRoutes = jest.requireActual('../../src/ROUTES'); - return { - ...actualRoutes, - HYBRID_APP_ROUTES: { - MONEY_REQUEST_CREATE: '/request/new/scan', - }, - }; -}); +const HYBRID_APP_ROUTES = { + MONEY_REQUEST_CREATE: '/request/new/scan', +} as const; const mockedNavigate = jest.fn(); jest.mock('@react-navigation/native', () => { From b20246681b7d2ea44a4deb1bc3dba693208f9f7c Mon Sep 17 00:00:00 2001 From: Github Date: Tue, 30 Jan 2024 17:11:52 +0100 Subject: [PATCH 0897/1250] test --- src/pages/home/ReportScreen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 4d043f12351e..7219b730dac3 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -468,10 +468,10 @@ function ReportScreen({ [report, reportMetadata, isLoading, shouldHideReport, isOptimisticDelete, userLeavingStatus], ); - const actionListValue = useMemo(() => ({flatListRef, scrollPosition, setScrollPosition}), [flatListRef, scrollPosition, setScrollPosition]); + const actionListValues = useMemo(() => ({flatListRef, scrollPosition, setScrollPosition}), [flatListRef, scrollPosition, setScrollPosition]); return ( - + Date: Tue, 30 Jan 2024 17:16:48 +0100 Subject: [PATCH 0898/1250] test --- tests/perf-test/SignInPage.perf-test.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/perf-test/SignInPage.perf-test.tsx b/tests/perf-test/SignInPage.perf-test.tsx index 94f535ef2afc..80964c3c49cd 100644 --- a/tests/perf-test/SignInPage.perf-test.tsx +++ b/tests/perf-test/SignInPage.perf-test.tsx @@ -29,10 +29,6 @@ jest.mock('../../src/libs/Navigation/Navigation', () => { } as typeof Navigation; }); -const HYBRID_APP_ROUTES = { - MONEY_REQUEST_CREATE: '/request/new/scan', -} as const; - const mockedNavigate = jest.fn(); jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); From e5ebfbb476c36857dbd3b17e2a5afc52f00a936c Mon Sep 17 00:00:00 2001 From: Github Date: Tue, 30 Jan 2024 17:21:41 +0100 Subject: [PATCH 0899/1250] Revert "test" This reverts commit b20246681b7d2ea44a4deb1bc3dba693208f9f7c. --- src/pages/home/ReportScreen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 7219b730dac3..4d043f12351e 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -468,10 +468,10 @@ function ReportScreen({ [report, reportMetadata, isLoading, shouldHideReport, isOptimisticDelete, userLeavingStatus], ); - const actionListValues = useMemo(() => ({flatListRef, scrollPosition, setScrollPosition}), [flatListRef, scrollPosition, setScrollPosition]); + const actionListValue = useMemo(() => ({flatListRef, scrollPosition, setScrollPosition}), [flatListRef, scrollPosition, setScrollPosition]); return ( - + Date: Tue, 30 Jan 2024 21:55:46 +0530 Subject: [PATCH 0900/1250] Limit characters to 1024 --- src/CONST.ts | 4 ++++ src/pages/RoomDescriptionPage.tsx | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index 02b8717d9644..cfab40253380 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -99,6 +99,10 @@ const CONST = { MAX_LENGTH: 40, }, + REPORT_DESCRIPTION: { + MAX_LENGTH: 1024, + }, + PULL_REQUEST_NUMBER, MERCHANT_NAME_MAX_LENGTH: 255, diff --git a/src/pages/RoomDescriptionPage.tsx b/src/pages/RoomDescriptionPage.tsx index c9615be6d77e..ac56076c809d 100644 --- a/src/pages/RoomDescriptionPage.tsx +++ b/src/pages/RoomDescriptionPage.tsx @@ -6,6 +6,7 @@ import type {OnyxCollection} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; +import type {OnyxFormValuesFields} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -21,6 +22,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; type RoomDescriptionPageProps = { /** Policy for the current report */ @@ -61,6 +63,14 @@ function RoomDescriptionPage({report, policies}: RoomDescriptionPageProps) { }, []), ); + const limitCharacters = (values: OnyxFormValuesFields) => { + const errors: Errors = {}; + if (String(values.reportDescription).length > CONST.REPORT_DESCRIPTION.MAX_LENGTH) { + errors['reportDescription'] = translate('common.error.characterLimit', {limit: CONST.REPORT_DESCRIPTION.MAX_LENGTH}); + } + return errors; + }; + return ( {translate('reportDescriptionPage.explainerText')} From 2efe2db14d72427c7d594843b434ae3f6f23b0d5 Mon Sep 17 00:00:00 2001 From: someone-here Date: Tue, 30 Jan 2024 22:02:41 +0530 Subject: [PATCH 0901/1250] Fix lint --- src/pages/RoomDescriptionPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/RoomDescriptionPage.tsx b/src/pages/RoomDescriptionPage.tsx index ac56076c809d..969d19f58a67 100644 --- a/src/pages/RoomDescriptionPage.tsx +++ b/src/pages/RoomDescriptionPage.tsx @@ -66,7 +66,7 @@ function RoomDescriptionPage({report, policies}: RoomDescriptionPageProps) { const limitCharacters = (values: OnyxFormValuesFields) => { const errors: Errors = {}; if (String(values.reportDescription).length > CONST.REPORT_DESCRIPTION.MAX_LENGTH) { - errors['reportDescription'] = translate('common.error.characterLimit', {limit: CONST.REPORT_DESCRIPTION.MAX_LENGTH}); + errors.reportDescription = translate('common.error.characterLimit', {limit: CONST.REPORT_DESCRIPTION.MAX_LENGTH}); } return errors; }; From 71dda56c541df3b029175f7d9644826f318b2e34 Mon Sep 17 00:00:00 2001 From: someone-here Date: Tue, 30 Jan 2024 23:23:03 +0530 Subject: [PATCH 0902/1250] Improvement of header for small screens --- .../DisplayNames/DisplayNamesWithTooltip.tsx | 3 +- .../DisplayNamesWithoutTooltip.tsx | 7 +++- src/components/DisplayNames/index.tsx | 4 +- src/components/DisplayNames/types.ts | 4 ++ src/pages/home/HeaderView.js | 40 +++++++------------ 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx index ce0ae7ddcf4f..77396b4d8999 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx @@ -11,7 +11,7 @@ import type DisplayNamesProps from './types'; type HTMLElementWithText = HTMLElement & RNText; -function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWithTooltips, textStyles = [], numberOfLines = 1}: DisplayNamesProps) { +function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWithTooltips, textStyles = [], numberOfLines = 1, AdditionalComponent}: DisplayNamesProps) { const styles = useThemeStyles(); const containerRef = useRef(null); const childRefs = useRef([]); @@ -72,6 +72,7 @@ function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWit {index < displayNamesWithTooltips.length - 1 && } ))} + {AdditionalComponent && } {Boolean(isEllipsisActive) && ( diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx index 177bdb6a9fc4..e3a2dc503666 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type {ComponentType} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -12,9 +13,12 @@ type DisplayNamesWithoutTooltipProps = { /** Number of lines before wrapping */ numberOfLines?: number; + + /** Additional component to render after the text */ + AdditionalComponent?: ComponentType; }; -function DisplayNamesWithoutTooltip({textStyles = [], numberOfLines = 1, fullTitle = ''}: DisplayNamesWithoutTooltipProps) { +function DisplayNamesWithoutTooltip({textStyles = [], numberOfLines = 1, fullTitle = '', AdditionalComponent}: DisplayNamesWithoutTooltipProps) { const styles = useThemeStyles(); return ( {fullTitle} + {AdditionalComponent && } ); } diff --git a/src/components/DisplayNames/index.tsx b/src/components/DisplayNames/index.tsx index 155193368cc5..73439ea02396 100644 --- a/src/components/DisplayNames/index.tsx +++ b/src/components/DisplayNames/index.tsx @@ -4,7 +4,7 @@ import DisplayNamesWithoutTooltip from './DisplayNamesWithoutTooltip'; import DisplayNamesWithToolTip from './DisplayNamesWithTooltip'; import type DisplayNamesProps from './types'; -function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, shouldUseFullTitle, displayNamesWithTooltips}: DisplayNamesProps) { +function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, shouldUseFullTitle, displayNamesWithTooltips, AdditionalComponent}: DisplayNamesProps) { const {translate} = useLocalize(); const title = fullTitle || translate('common.hidden'); @@ -14,6 +14,7 @@ function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, sho textStyles={textStyles} numberOfLines={numberOfLines} fullTitle={title} + AdditionalComponent={AdditionalComponent} /> ); } @@ -25,6 +26,7 @@ function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, sho displayNamesWithTooltips={displayNamesWithTooltips} textStyles={textStyles} numberOfLines={numberOfLines} + AdditionalComponent={AdditionalComponent} /> ); } diff --git a/src/components/DisplayNames/types.ts b/src/components/DisplayNames/types.ts index 2e6f36d5cc07..26bd263bdbd8 100644 --- a/src/components/DisplayNames/types.ts +++ b/src/components/DisplayNames/types.ts @@ -1,3 +1,4 @@ +import type {ComponentType} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import type {AvatarSource} from '@libs/UserUtils'; @@ -39,6 +40,9 @@ type DisplayNamesProps = { /** If the full title needs to be displayed */ shouldUseFullTitle?: boolean; + + /** Additional component to display after the display names */ + AdditionalComponent?: ComponentType; }; export default DisplayNamesProps; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 611eab5375bd..bc7ec0abc19d 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -278,33 +278,23 @@ function HeaderView(props) { /> )} - - - {!_.isEmpty(policyName) && - !_.isEmpty(reportDescription) && ( // code for the policyName display + + !_.isEmpty(policyName) && + !_.isEmpty(reportDescription) && ( <> - - {translate('threads.in')} - - - {policyName} - + {translate('threads.in')} + {policyName} - )} - + ) + } + /> {!_.isEmpty(parentNavigationSubtitleData) && ( Date: Tue, 30 Jan 2024 23:33:22 +0530 Subject: [PATCH 0903/1250] Add offline pattern --- src/libs/actions/Report.ts | 13 ++++++++++--- src/pages/ReportDetailsPage.js | 19 +++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index dd8d6f4ed186..ccc4c6ebc065 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1475,20 +1475,27 @@ function updateDescription(reportID: string, previousValue: string, newValue: st { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: {description: parsedDescription}, + value: {description: parsedDescription, pendingFields: {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}, }, ]; const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: {description: previousValue}, + value: {description: previousValue, pendingFields: {description: null}}, + }, + ]; + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: {pendingFields: {description: null}}, }, ]; const parameters: UpdateRoomDescriptionParams = {reportID, description: parsedDescription}; - API.write(WRITE_COMMANDS.UPDATE_ROOM_DESCRIPTION, parameters, {optimisticData, failureData}); + API.write(WRITE_COMMANDS.UPDATE_ROOM_DESCRIPTION, parameters, {optimisticData, failureData, successData}); Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID)); } diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 55ab85e8c3e5..effc51a9a2ec 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -10,6 +10,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import MultipleAvatars from '@components/MultipleAvatars'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {withNetwork} from '@components/OnyxProvider'; import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle'; import participantPropTypes from '@components/participantPropTypes'; @@ -242,14 +243,16 @@ function ReportDetailsPage(props) { {shouldShowReportDescription && ( - Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(props.report.reportID))} - /> + + Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(props.report.reportID))} + /> + )} {_.map(menuItems, (item) => { const brickRoadIndicator = From e9c7742aac218884caaf5e20a1035883b27fb562 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 30 Jan 2024 11:23:00 -0700 Subject: [PATCH 0904/1250] Add GPS coordinates to smartscanned images --- src/libs/actions/IOU.js | 5 ++++ .../step/IOURequestStepConfirmation.js | 24 +++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 3d6664099866..3890d1d3262a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1289,6 +1289,7 @@ function updateDistanceRequest(transactionID, transactionThreadReportID, transac * @param {Object} [policy] * @param {Object} [policyTags] * @param {Object} [policyCategories] + * @param {Object} [gpsPoints] */ function requestMoney( report, @@ -1309,6 +1310,7 @@ function requestMoney( policy = undefined, policyTags = undefined, policyCategories = undefined, + gpsPoints = undefined, ) { // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); @@ -1360,6 +1362,9 @@ function requestMoney( taxCode, taxAmount, billable, + + // This needs to be a string of JSON because of limitations with the fetch() API and nested objects + gpsPoints: gpsPoints ? JSON.stringify(gpsPoints) : undefined, }, onyxData, ); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 6028a735d132..154ad0198a08 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -18,6 +18,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import getCurrentPosition from '@libs/getCurrentPosition'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; @@ -160,7 +161,7 @@ function IOURequestStepConfirmation({ * @param {File} [receiptObj] */ const requestMoney = useCallback( - (selectedParticipants, trimmedComment, receiptObj) => { + (selectedParticipants, trimmedComment, receiptObj, gpsPoints) => { IOU.requestMoney( report, transaction.amount, @@ -180,6 +181,7 @@ function IOURequestStepConfirmation({ policy, policyTags, policyCategories, + gpsPoints, ); }, [report, transaction, transactionTaxCode, transactionTaxAmount, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, policy, policyTags, policyCategories], @@ -265,7 +267,25 @@ function IOURequestStepConfirmation({ } if (receiptFile) { - requestMoney(selectedParticipants, trimmedComment, receiptFile); + getCurrentPosition( + (successData) => { + requestMoney(selectedParticipants, trimmedComment, receiptFile, { + lat: successData.coords.latitude, + long: successData.coords.longitude, + }); + }, + () => { + // When there is an error, the money can still be requested, it just won't include the GPS coordinates + requestMoney(selectedParticipants, trimmedComment, receiptFile); + }, + { + // It's OK to get a cached location that is up to an hour old because the only accuracy needed is the country the user is in + maximumAge: 1000 * 60 * 60, + + // 15 seconds, don't way too long because the server can always fall back to using the IP address + timeout: 15000, + }, + ); return; } From 0a1acb8dea39b3ef3166d4773e4c17a870e7bcfe Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Tue, 30 Jan 2024 19:09:38 +0000 Subject: [PATCH 0905/1250] Add new harvesting policy key --- src/types/onyx/Policy.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index eca7e9d1ee06..63319a1c67bc 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -88,10 +88,15 @@ type Policy = { /** The scheduled submit frequency set up on the this policy */ autoReportingFrequency?: ValueOf; - /** Whether the scheduled submit is enabled */ + /** @deprecated Whether the scheduled submit is enabled */ isHarvestingEnabled?: boolean; /** Whether the scheduled submit is enabled */ + harvesting?: { + enabled: boolean; + } + + /** Whether the self approval or submitting is enabled */ isPreventSelfApprovalEnabled?: boolean; /** When the monthly scheduled submit should happen */ From 65f9daddbd1f6b83fdc2facb254b6a49202641a6 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Tue, 30 Jan 2024 19:22:38 +0000 Subject: [PATCH 0906/1250] Clean up other usages of isHarvestingEnabled --- src/components/MoneyReportHeader.tsx | 9 ++++++--- src/components/ReportActionItem/ReportPreview.tsx | 9 ++++++--- src/libs/actions/IOU.js | 2 +- tests/utils/LHNTestUtils.js | 4 +++- tests/utils/collections/policies.ts | 4 +++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 4b4e3915f969..2f578c625cae 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -76,7 +76,10 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt return isManager && !isDraft && !isApproved && !isSettled; }, [isPaidGroupPolicy, isManager, isDraft, isApproved, isSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; + const shouldShowSubmitButton = useMemo( + () => isDraft && reimbursableSpend !== 0, + [isDraft, reimbursableSpend], + ); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; @@ -86,8 +89,8 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( - () => chatReport?.isOwnPolicyExpenseChat && !policy.isHarvestingEnabled, - [chatReport?.isOwnPolicyExpenseChat, policy.isHarvestingEnabled], + () => chatReport?.isOwnPolicyExpenseChat && !(policy.harvesting?.enabled ?? policy.isHarvestingEnabled), + [chatReport?.isOwnPolicyExpenseChat, policy.harvesting?.enabled, policy.isHarvestingEnabled], ); const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)]; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index b2fece085f57..b2714cc58a4a 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -155,12 +155,15 @@ function ReportPreview({ scanningReceipts: numberOfScanningReceipts, }); - const shouldShowSubmitButton = isDraftExpenseReport && reimbursableSpend !== 0; + const shouldShowSubmitButton = useMemo( + () => isDraftExpenseReport && reimbursableSpend !== 0, + [isDraftExpenseReport, reimbursableSpend], + ); // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( - () => chatReport?.isOwnPolicyExpenseChat && !policy?.isHarvestingEnabled, - [chatReport?.isOwnPolicyExpenseChat, policy?.isHarvestingEnabled], + () => chatReport?.isOwnPolicyExpenseChat && !(policy?.harvesting?.enabled ?? policy?.isHarvestingEnabled), + [chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled, policy?.isHarvestingEnabled], ); const getDisplayAmount = (): string => { diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index a9e1b09ed984..0e9fd4bcb94a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -719,7 +719,7 @@ function getMoneyRequestInformation( isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN - needsToBeManuallySubmitted = isFromPaidPolicy && !(policy.isHarvestingEnabled || false); + needsToBeManuallySubmitted = isFromPaidPolicy && !(lodashGet(policy, 'harvesting.enabled', policy.isHarvestingEnabled) || false); // If the linked expense report on paid policy is not draft, we need to create a new draft expense report if (iouReport && isFromPaidPolicy && !ReportUtils.isDraftExpenseReport(iouReport)) { diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index 6c72558e5df3..04246c1c438a 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -256,7 +256,9 @@ function getFakePolicy(id = 1, name = 'Workspace-Test-001') { lastModified: 1697323926777105, autoReporting: true, autoReportingFrequency: 'immediate', - isHarvestingEnabled: true, + harvesting: { + enabled: true, + }, autoReportingOffset: 1, isPreventSelfApprovalEnabled: true, submitsTo: 123456, diff --git a/tests/utils/collections/policies.ts b/tests/utils/collections/policies.ts index 8547c171c7a7..4223c7e41941 100644 --- a/tests/utils/collections/policies.ts +++ b/tests/utils/collections/policies.ts @@ -11,7 +11,9 @@ export default function createRandomPolicy(index: number): Policy { autoReporting: randBoolean(), isPolicyExpenseChatEnabled: randBoolean(), autoReportingFrequency: rand(Object.values(CONST.POLICY.AUTO_REPORTING_FREQUENCIES)), - isHarvestingEnabled: randBoolean(), + harvesting: { + enabled: randBoolean(), + }, autoReportingOffset: 1, isPreventSelfApprovalEnabled: randBoolean(), submitsTo: index, From d0ab3e5bb6d3c068b8af6ad029211545c2c7e208 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Tue, 30 Jan 2024 19:25:27 +0000 Subject: [PATCH 0907/1250] Remove submit memo change --- src/components/MoneyReportHeader.tsx | 5 +---- src/components/ReportActionItem/ReportPreview.tsx | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 2f578c625cae..c2e6ff341416 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -76,10 +76,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt return isManager && !isDraft && !isApproved && !isSettled; }, [isPaidGroupPolicy, isManager, isDraft, isApproved, isSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldShowSubmitButton = useMemo( - () => isDraft && reimbursableSpend !== 0, - [isDraft, reimbursableSpend], - ); + const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index b2714cc58a4a..52e9d94eaefd 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -155,10 +155,7 @@ function ReportPreview({ scanningReceipts: numberOfScanningReceipts, }); - const shouldShowSubmitButton = useMemo( - () => isDraftExpenseReport && reimbursableSpend !== 0, - [isDraftExpenseReport, reimbursableSpend], - ); + const shouldShowSubmitButton = isDraftExpenseReport && reimbursableSpend !== 0; // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( From 90583bccd62032aa13b8b0c41e517eb1ebd74468 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Tue, 30 Jan 2024 20:34:33 +0000 Subject: [PATCH 0908/1250] Fix style --- src/types/onyx/Policy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 63319a1c67bc..719e0ba1fb9d 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -94,9 +94,9 @@ type Policy = { /** Whether the scheduled submit is enabled */ harvesting?: { enabled: boolean; - } + }; - /** Whether the self approval or submitting is enabled */ + /** Whether the self approval or submitting is enabled */ isPreventSelfApprovalEnabled?: boolean; /** When the monthly scheduled submit should happen */ From b33dae63c9c4233b24f9f76f6431a775d7546b8c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 30 Jan 2024 22:32:12 +0100 Subject: [PATCH 0909/1250] Fix TS issues after merging main --- src/components/AttachmentModal.tsx | 2 +- src/libs/actions/IOU.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index f3e8ed316c52..88427afcb73e 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -279,7 +279,7 @@ function AttachmentModal({ * Detach the receipt and close the modal. */ const deleteAndCloseModal = useCallback(() => { - IOU.detachReceipt(transaction?.transactionID); + IOU.detachReceipt(transaction?.transactionID ?? ''); setIsDeleteReceiptConfirmModalVisible(false); Navigation.dismissModal(report?.reportID); }, [transaction, report]); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 10cf7b852b80..238db9eaa509 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3450,14 +3450,14 @@ function detachReceipt(transactionID: string) { } function replaceReceipt(transactionID: string, file: File, source: string) { - const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {}; + const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; const oldReceipt = transaction?.receipt ?? {}; const receiptOptimistic = { source, state: CONST.IOU.RECEIPT_STATE.OPEN, }; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, From 141cf89428a21ff85e7ee643d017afa3d831e1b9 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 30 Jan 2024 14:56:09 -0700 Subject: [PATCH 0910/1250] Add error logging --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 154ad0198a08..edd788f51e90 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -20,6 +20,7 @@ import compose from '@libs/compose'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getCurrentPosition from '@libs/getCurrentPosition'; import * as IOUUtils from '@libs/IOUUtils'; +import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -274,7 +275,8 @@ function IOURequestStepConfirmation({ long: successData.coords.longitude, }); }, - () => { + (errorData) => { + Log.info('[IOURequestStepConfirmation] getCurrentPosition failed', false, errorData); // When there is an error, the money can still be requested, it just won't include the GPS coordinates requestMoney(selectedParticipants, trimmedComment, receiptFile); }, From 022efe32a9c8dad2c62a43d646fe3e1dbbab21ce Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Wed, 31 Jan 2024 04:22:43 +0500 Subject: [PATCH 0911/1250] fix: minor refactors to function names --- .../ReportActionItem/MoneyReportView.tsx | 9 +++----- .../parameters/SetPolicyReportNameParams.ts | 6 ------ .../API/parameters/SetReportNameParams.ts | 6 ++++++ src/libs/API/parameters/index.ts | 2 +- src/libs/API/types.ts | 4 ++-- src/libs/ReportUtils.ts | 21 +++++++++++++++++++ src/libs/actions/Report.ts | 10 ++++++--- src/pages/EditReportFieldPage.tsx | 4 ++-- 8 files changed, 42 insertions(+), 20 deletions(-) delete mode 100644 src/libs/API/parameters/SetPolicyReportNameParams.ts create mode 100644 src/libs/API/parameters/SetReportNameParams.ts diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx index 5651d0b943ab..4af5ee7f86fa 100644 --- a/src/components/ReportActionItem/MoneyReportView.tsx +++ b/src/components/ReportActionItem/MoneyReportView.tsx @@ -59,12 +59,9 @@ function MoneyReportView({report, policy, policyReportFields, shouldShowHorizont ]; const sortedPolicyReportFields = useMemo((): PolicyReportField[] => { - const reportFields = Object.values(report.reportFields ?? {}); - const mergedFieldIds = Array.from(new Set([...policyReportFields.map(({fieldID}) => fieldID), ...reportFields.map(({fieldID}) => fieldID)])); - const mergedFields = mergedFieldIds.map((id) => report?.reportFields?.[id] ?? policyReportFields.find(({fieldID}) => fieldID === id)) as PolicyReportField[]; - const allReportFields = isSettled ? reportFields : mergedFields; - return allReportFields.sort(({orderWeight: firstOrderWeight}, {orderWeight: secondOrderWeight}) => firstOrderWeight - secondOrderWeight); - }, [policyReportFields, report.reportFields, isSettled]); + const fields = ReportUtils.getAvailableReportFields(report, policyReportFields); + return fields.sort(({orderWeight: firstOrderWeight}, {orderWeight: secondOrderWeight}) => firstOrderWeight - secondOrderWeight); + }, [policyReportFields, report]); return ( diff --git a/src/libs/API/parameters/SetPolicyReportNameParams.ts b/src/libs/API/parameters/SetPolicyReportNameParams.ts deleted file mode 100644 index d6310ee18115..000000000000 --- a/src/libs/API/parameters/SetPolicyReportNameParams.ts +++ /dev/null @@ -1,6 +0,0 @@ -type SetPolicyReportNameParams = { - reportID: string; - reportName: string; -}; - -export default SetPolicyReportNameParams; diff --git a/src/libs/API/parameters/SetReportNameParams.ts b/src/libs/API/parameters/SetReportNameParams.ts new file mode 100644 index 000000000000..784674e1486e --- /dev/null +++ b/src/libs/API/parameters/SetReportNameParams.ts @@ -0,0 +1,6 @@ +type SetReportNameParams = { + reportID: string; + reportName: string; +}; + +export default SetReportNameParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 635887b33e4b..b2a68fca8562 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -123,4 +123,4 @@ export type {default as CompleteTaskParams} from './CompleteTaskParams'; export type {default as CompleteEngagementModalParams} from './CompleteEngagementModalParams'; export type {default as SetNameValuePairParams} from './SetNameValuePairParams'; export type {default as SetPolicyReportFieldParams} from './SetPolicyReportFieldParams'; -export type {default as SetPolicyReportNameParams} from './SetPolicyReportNameParams'; +export type {default as SetReportNameParams} from './SetReportNameParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index ca13e828f1bd..83f30700a2c6 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -114,7 +114,7 @@ const WRITE_COMMANDS = { COMPLETE_ENGAGEMENT_MODAL: 'CompleteEngagementModal', SET_NAME_VALUE_PAIR: 'SetNameValuePair', SET_POLICY_REPORT_FIELD: 'Report_SetFields', - SET_POLICY_REPORT_NAME: 'RenameReport', + SET_REPORT_NAME: 'RenameReport', } as const; type WriteCommand = ValueOf; @@ -226,7 +226,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.COMPLETE_ENGAGEMENT_MODAL]: Parameters.CompleteEngagementModalParams; [WRITE_COMMANDS.SET_NAME_VALUE_PAIR]: Parameters.SetNameValuePairParams; [WRITE_COMMANDS.SET_POLICY_REPORT_FIELD]: Parameters.SetPolicyReportFieldParams; - [WRITE_COMMANDS.SET_POLICY_REPORT_NAME]: Parameters.SetPolicyReportNameParams; + [WRITE_COMMANDS.SET_REPORT_NAME]: Parameters.SetReportNameParams; }; const READ_COMMANDS = { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9f3d52eecd48..0f656b05f252 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1913,6 +1913,26 @@ function getReportFieldsByPolicyID(policyID: string) { return Object.entries(allPolicyReportFields ?? {}).find(([key]) => key.replace(ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS, '') === policyID)?.[1]; } +/** + * Get the report fields that we should display a MoneyReportView gets opened + */ + +function getAvailableReportFields(report: Report, policyReportFields: PolicyReportField[]): PolicyReportField[] { + // Get the report fields that are attached to a report. These will persist even if a field is deleted from the policy. + const reportFields = Object.values(report.reportFields ?? {}); + const reportIsSettled = isSettled(report.reportID); + + // If the report is settled, we don't want to show any new field that gets added to the policy. + if (reportIsSettled) { + return reportFields; + } + + // If the report is unsettled, we want to merge the new fields that get added to the policy with the fields that + // are attached to the report. + const mergedFieldIds = Array.from(new Set([...policyReportFields.map(({fieldID}) => fieldID), ...reportFields.map(({fieldID}) => fieldID)])); + return mergedFieldIds.map((id) => report?.reportFields?.[id] ?? policyReportFields.find(({fieldID}) => fieldID === id)) as PolicyReportField[]; +} + /** * Get the title for an IOU or expense chat which will be showing the payer and the amount */ @@ -4843,6 +4863,7 @@ export { isValidReport, isReportFieldOfTypeTitle, isReportFieldDisabled, + getAvailableReportFields, }; export type { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3dad99f59df1..f60fb0a62b1f 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1468,7 +1468,7 @@ function toggleSubscribeToChildReport(childReportID = '0', parentReportAction: P } } -function updatePolicyReportName(reportID: string, value: string) { +function updateReportName(reportID: string, value: string, previousValue: string) { const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1486,6 +1486,7 @@ function updatePolicyReportName(reportID: string, value: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { + reportName: previousValue, pendingFields: { reportName: null, }, @@ -1519,7 +1520,7 @@ function updatePolicyReportName(reportID: string, value: string) { API.write('RenameReport', parameters, {optimisticData, failureData, successData}); } -function updatePolicyReportField(reportID: string, reportField: PolicyReportField) { +function updatePolicyReportField(reportID: string, reportField: PolicyReportField, previousReportField: PolicyReportField) { const recentlyUsedValues = allRecentlyUsedReportFields?.[reportField.fieldID] ?? []; const optimisticData: OnyxUpdate[] = [ @@ -1552,6 +1553,9 @@ function updatePolicyReportField(reportID: string, reportField: PolicyReportFiel onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { + reportFields: { + [reportField.fieldID]: previousReportField, + }, pendingFields: { [reportField.fieldID]: null, }, @@ -2855,6 +2859,6 @@ export { updateLastVisitTime, clearNewRoomFormError, updatePolicyReportField, - updatePolicyReportName, + updateReportName, resolveActionableMentionWhisper, }; diff --git a/src/pages/EditReportFieldPage.tsx b/src/pages/EditReportFieldPage.tsx index 24ade45993f6..b2d04e714d27 100644 --- a/src/pages/EditReportFieldPage.tsx +++ b/src/pages/EditReportFieldPage.tsx @@ -66,9 +66,9 @@ function EditReportFieldPage({route, policy, report, policyReportFields}: EditRe const handleReportFieldChange = (form: OnyxFormValuesFields) => { const value = form[reportField.fieldID].toString() || ''; if (isReportFieldTitle) { - ReportActions.updatePolicyReportName(report.reportID, value); + ReportActions.updateReportName(report.reportID, value, report.reportName ?? ''); } else { - ReportActions.updatePolicyReportField(report.reportID, {...reportField, value}); + ReportActions.updatePolicyReportField(report.reportID, {...reportField, value}, reportField); } Navigation.dismissModal(report?.reportID); From db3738de68e25edb3bc5c7eb59b79d06329b62a3 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Wed, 31 Jan 2024 04:32:01 +0500 Subject: [PATCH 0912/1250] fix: lint issues --- src/libs/API/parameters/SetPolicyReportFieldParams.ts | 6 ------ src/libs/API/parameters/SetReportFieldParams.ts | 6 ++++++ src/libs/API/parameters/index.ts | 2 +- src/libs/API/types.ts | 4 ++-- src/libs/actions/Report.ts | 8 ++++---- src/pages/EditReportFieldPage.tsx | 2 +- src/pages/EditReportFieldTextPage.tsx | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 src/libs/API/parameters/SetPolicyReportFieldParams.ts create mode 100644 src/libs/API/parameters/SetReportFieldParams.ts diff --git a/src/libs/API/parameters/SetPolicyReportFieldParams.ts b/src/libs/API/parameters/SetPolicyReportFieldParams.ts deleted file mode 100644 index 94f059057b58..000000000000 --- a/src/libs/API/parameters/SetPolicyReportFieldParams.ts +++ /dev/null @@ -1,6 +0,0 @@ -type SetPolicyReportFieldParams = { - reportID: string; - reportFields: string; -}; - -export default SetPolicyReportFieldParams; diff --git a/src/libs/API/parameters/SetReportFieldParams.ts b/src/libs/API/parameters/SetReportFieldParams.ts new file mode 100644 index 000000000000..8b6c8682d657 --- /dev/null +++ b/src/libs/API/parameters/SetReportFieldParams.ts @@ -0,0 +1,6 @@ +type SetReportFieldParams = { + reportID: string; + reportFields: string; +}; + +export default SetReportFieldParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index b2a68fca8562..8c0c2fde17cf 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -122,5 +122,5 @@ export type {default as ReopenTaskParams} from './ReopenTaskParams'; export type {default as CompleteTaskParams} from './CompleteTaskParams'; export type {default as CompleteEngagementModalParams} from './CompleteEngagementModalParams'; export type {default as SetNameValuePairParams} from './SetNameValuePairParams'; -export type {default as SetPolicyReportFieldParams} from './SetPolicyReportFieldParams'; +export type {default as SetReportFieldParams} from './SetReportFieldParams'; export type {default as SetReportNameParams} from './SetReportNameParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 83f30700a2c6..05b658ee0702 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -113,7 +113,7 @@ const WRITE_COMMANDS = { COMPLETE_TASK: 'CompleteTask', COMPLETE_ENGAGEMENT_MODAL: 'CompleteEngagementModal', SET_NAME_VALUE_PAIR: 'SetNameValuePair', - SET_POLICY_REPORT_FIELD: 'Report_SetFields', + SET_REPORT_FIELD: 'Report_SetFields', SET_REPORT_NAME: 'RenameReport', } as const; @@ -225,7 +225,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.COMPLETE_TASK]: Parameters.CompleteTaskParams; [WRITE_COMMANDS.COMPLETE_ENGAGEMENT_MODAL]: Parameters.CompleteEngagementModalParams; [WRITE_COMMANDS.SET_NAME_VALUE_PAIR]: Parameters.SetNameValuePairParams; - [WRITE_COMMANDS.SET_POLICY_REPORT_FIELD]: Parameters.SetPolicyReportFieldParams; + [WRITE_COMMANDS.SET_REPORT_FIELD]: Parameters.SetReportFieldParams; [WRITE_COMMANDS.SET_REPORT_NAME]: Parameters.SetReportNameParams; }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f60fb0a62b1f..38a73c1d3055 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1517,10 +1517,10 @@ function updateReportName(reportID: string, value: string, previousValue: string reportName: value, }; - API.write('RenameReport', parameters, {optimisticData, failureData, successData}); + API.write(WRITE_COMMANDS.SET_REPORT_NAME, parameters, {optimisticData, failureData, successData}); } -function updatePolicyReportField(reportID: string, reportField: PolicyReportField, previousReportField: PolicyReportField) { +function updateReportField(reportID: string, reportField: PolicyReportField, previousReportField: PolicyReportField) { const recentlyUsedValues = allRecentlyUsedReportFields?.[reportField.fieldID] ?? []; const optimisticData: OnyxUpdate[] = [ @@ -1596,7 +1596,7 @@ function updatePolicyReportField(reportID: string, reportField: PolicyReportFiel reportFields: JSON.stringify({[reportField.fieldID]: {fieldID: reportField.fieldID, value: reportField.value, type: reportField.type, name: reportField.name}}), }; - API.write('Report_SetFields', parameters, {optimisticData, failureData, successData}); + API.write(WRITE_COMMANDS.SET_REPORT_FIELD, parameters, {optimisticData, failureData, successData}); } function updateWelcomeMessage(reportID: string, previousValue: string, newValue: string) { @@ -2858,7 +2858,7 @@ export { getDraftPrivateNote, updateLastVisitTime, clearNewRoomFormError, - updatePolicyReportField, + updateReportField, updateReportName, resolveActionableMentionWhisper, }; diff --git a/src/pages/EditReportFieldPage.tsx b/src/pages/EditReportFieldPage.tsx index b2d04e714d27..602f2eed161e 100644 --- a/src/pages/EditReportFieldPage.tsx +++ b/src/pages/EditReportFieldPage.tsx @@ -68,7 +68,7 @@ function EditReportFieldPage({route, policy, report, policyReportFields}: EditRe if (isReportFieldTitle) { ReportActions.updateReportName(report.reportID, value, report.reportName ?? ''); } else { - ReportActions.updatePolicyReportField(report.reportID, {...reportField, value}, reportField); + ReportActions.updateReportField(report.reportID, {...reportField, value}, reportField); } Navigation.dismissModal(report?.reportID); diff --git a/src/pages/EditReportFieldTextPage.tsx b/src/pages/EditReportFieldTextPage.tsx index 096967cf4003..bb1a42103da1 100644 --- a/src/pages/EditReportFieldTextPage.tsx +++ b/src/pages/EditReportFieldTextPage.tsx @@ -38,7 +38,7 @@ function EditReportFieldTextPage({fieldName, onSubmit, fieldValue, isRequired, f const validate = useCallback( (values: OnyxFormValuesFields) => { const errors: Errors = {}; - if (isRequired && values[fieldID].toString().trim() === '') { + if (isRequired && (values[fieldID] as string).trim() === '') { errors[fieldID] = 'common.error.fieldRequired'; } return errors; From d0db92fda86a6f7aec6736dd21e882c11e4d4af6 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Wed, 31 Jan 2024 04:46:27 +0500 Subject: [PATCH 0913/1250] fix: use ucfirst str lib function and handle more lint issues --- src/ONYXKEYS.ts | 8 ++++---- src/components/ReportActionItem/MoneyReportView.tsx | 3 ++- src/pages/EditReportFieldDatePage.tsx | 8 ++++---- src/pages/EditReportFieldPage.tsx | 11 ++++++----- src/pages/EditReportFieldTextPage.tsx | 8 ++++---- src/types/onyx/Form.ts | 4 +++- src/types/onyx/index.ts | 3 ++- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index e3c42238318d..b4ca0c898b0b 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -358,8 +358,8 @@ const ONYXKEYS = { REPORT_VIRTUAL_CARD_FRAUD_DRAFT: 'reportVirtualCardFraudFormDraft', GET_PHYSICAL_CARD_FORM: 'getPhysicalCardForm', GET_PHYSICAL_CARD_FORM_DRAFT: 'getPhysicalCardFormDraft', - POLICY_REPORT_FIELD_EDIT_FORM: 'policyReportFieldEditForm', - POLICY_REPORT_FIELD_EDIT_FORM_DRAFT: 'policyReportFieldEditFormDraft', + REPORT_FIELD_EDIT_FORM: 'reportFieldEditForm', + REPORT_FIELD_EDIT_FORM_DRAFT: 'reportFieldEditFormDraft', REIMBURSEMENT_ACCOUNT_FORM: 'reimbursementAccount', REIMBURSEMENT_ACCOUNT_FORM_DRAFT: 'reimbursementAccountDraft', }, @@ -542,8 +542,8 @@ type OnyxValues = { [ONYXKEYS.FORMS.REPORT_PHYSICAL_CARD_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.POLICY_REPORT_FIELD_EDIT_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.POLICY_REPORT_FIELD_EDIT_FORM_DRAFT]: OnyxTypes.Form; + [ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM]: OnyxTypes.ReportFieldEditForm; + [ONYXKEYS.FORMS.REPORT_FIELD_EDIT_FORM_DRAFT]: OnyxTypes.Form; // @ts-expect-error Different values are defined under the same key: ReimbursementAccount and ReimbursementAccountForm [ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT]: OnyxTypes.Form; diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx index 4af5ee7f86fa..3c0e50b2c940 100644 --- a/src/components/ReportActionItem/MoneyReportView.tsx +++ b/src/components/ReportActionItem/MoneyReportView.tsx @@ -1,3 +1,4 @@ +import Str from 'expensify-common/lib/str'; import React, {useMemo} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import {View} from 'react-native'; @@ -81,7 +82,7 @@ function MoneyReportView({report, policy, policyReportFields, shouldShowHorizont key={`menuItem-${reportField.fieldID}`} > Navigation.navigate(ROUTES.EDIT_REPORT_FIELD_REQUEST.getRoute(report.reportID, report.policyID ?? '', reportField.fieldID))} shouldShowRightIcon diff --git a/src/pages/EditReportFieldDatePage.tsx b/src/pages/EditReportFieldDatePage.tsx index 2496e5b23bf2..82659eca62c2 100644 --- a/src/pages/EditReportFieldDatePage.tsx +++ b/src/pages/EditReportFieldDatePage.tsx @@ -27,7 +27,7 @@ type EditReportFieldDatePageProps = { isRequired: boolean; /** Callback to fire when the Save button is pressed */ - onSubmit: (form: OnyxFormValuesFields) => void; + onSubmit: (form: OnyxFormValuesFields) => void; }; function EditReportFieldDatePage({fieldName, isRequired, onSubmit, fieldValue, fieldID}: EditReportFieldDatePageProps) { @@ -36,9 +36,9 @@ function EditReportFieldDatePage({fieldName, isRequired, onSubmit, fieldValue, f const inputRef = useRef(null); const validate = useCallback( - (value: OnyxFormValuesFields) => { + (value: OnyxFormValuesFields) => { const errors: Errors = {}; - if (isRequired && value[fieldID].toString().trim() === '') { + if (isRequired && value[fieldID].trim() === '') { errors[fieldID] = 'common.error.fieldRequired'; } return errors; @@ -58,7 +58,7 @@ function EditReportFieldDatePage({fieldName, isRequired, onSubmit, fieldValue, f ) => { - const value = form[reportField.fieldID].toString() || ''; + const handleReportFieldChange = (form: OnyxFormValuesFields) => { + const value = form[reportField.fieldID] || ''; if (isReportFieldTitle) { ReportActions.updateReportName(report.reportID, value, report.reportName ?? ''); } else { @@ -79,7 +80,7 @@ function EditReportFieldPage({route, policy, report, policyReportFields}: EditRe if (reportField.type === 'text' || isReportFieldTitle) { return ( ) => void; + onSubmit: (form: OnyxFormValuesFields) => void; }; function EditReportFieldTextPage({fieldName, onSubmit, fieldValue, isRequired, fieldID}: EditReportFieldTextPageProps) { @@ -36,9 +36,9 @@ function EditReportFieldTextPage({fieldName, onSubmit, fieldValue, isRequired, f const inputRef = useRef(null); const validate = useCallback( - (values: OnyxFormValuesFields) => { + (values: OnyxFormValuesFields) => { const errors: Errors = {}; - if (isRequired && (values[fieldID] as string).trim() === '') { + if (isRequired && values[fieldID].trim() === '') { errors[fieldID] = 'common.error.fieldRequired'; } return errors; @@ -58,7 +58,7 @@ function EditReportFieldTextPage({fieldName, onSubmit, fieldValue, isRequired, f ; +type ReportFieldEditForm = Form>; + export default Form; -export type {AddDebitCardForm, DateOfBirthForm, PrivateNotesForm, DisplayNameForm, FormValueType, NewRoomForm, BaseForm, IKnowATeacherForm, IntroSchoolPrincipalForm}; +export type {AddDebitCardForm, DateOfBirthForm, PrivateNotesForm, DisplayNameForm, FormValueType, NewRoomForm, BaseForm, IKnowATeacherForm, IntroSchoolPrincipalForm, ReportFieldEditForm}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 5b04cae58671..64eec736b5bf 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -9,7 +9,7 @@ import type Credentials from './Credentials'; import type Currency from './Currency'; import type CustomStatusDraft from './CustomStatusDraft'; import type Download from './Download'; -import type {AddDebitCardForm, DateOfBirthForm, DisplayNameForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, PrivateNotesForm} from './Form'; +import type {AddDebitCardForm, DateOfBirthForm, DisplayNameForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, PrivateNotesForm, ReportFieldEditForm} from './Form'; import type Form from './Form'; import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; @@ -151,4 +151,5 @@ export type { IKnowATeacherForm, IntroSchoolPrincipalForm, PrivateNotesForm, + ReportFieldEditForm, }; From 0c9939ac37fc152c2fb7f3f73d12a86cfc77d933 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Wed, 31 Jan 2024 04:51:32 +0500 Subject: [PATCH 0914/1250] fix: include the whole report field object in the set report field call --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 38a73c1d3055..221c7f8d4f88 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1593,7 +1593,7 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre const parameters = { reportID, - reportFields: JSON.stringify({[reportField.fieldID]: {fieldID: reportField.fieldID, value: reportField.value, type: reportField.type, name: reportField.name}}), + reportFields: JSON.stringify({[reportField.fieldID]: reportField}), }; API.write(WRITE_COMMANDS.SET_REPORT_FIELD, parameters, {optimisticData, failureData, successData}); From cfd4286c528a290c55971d72de83542aa590a921 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 31 Jan 2024 07:40:23 +0700 Subject: [PATCH 0915/1250] move transitionDuration to propTypes, remove default value of transitionDuration, fix incorrect const name --- .../SignInPageLayout/BackgroundImage/index.android.js | 10 +--------- .../SignInPageLayout/BackgroundImage/index.ios.js | 4 ---- .../signin/SignInPageLayout/BackgroundImage/index.js | 4 ---- .../SignInPageLayout/BackgroundImage/propTypes.js | 3 +++ src/pages/signin/SignInPageLayout/index.js | 2 +- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js b/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js index a3ddc3383bc4..06784ddef007 100644 --- a/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js +++ b/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js @@ -9,13 +9,6 @@ const defaultProps = { transitionDuration: 0, }; -const propTypes = { - /** Transition duration in milliseconds */ - transitionDuration: PropTypes.number, - - ...defaultPropTypes, -}; - function BackgroundImage(props) { const styles = useThemeStyles(); return ( @@ -29,7 +22,6 @@ function BackgroundImage(props) { } BackgroundImage.displayName = 'BackgroundImage'; -BackgroundImage.propTypes = propTypes; -BackgroundImage.defaultProps = defaultProps; +BackgroundImage.propTypes = defaultPropTypes; export default BackgroundImage; diff --git a/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.js b/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.js index 20d5a75a8838..b426a888e8b9 100644 --- a/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.js +++ b/src/pages/signin/SignInPageLayout/BackgroundImage/index.ios.js @@ -9,16 +9,12 @@ import defaultPropTypes from './propTypes'; const defaultProps = { isSmallScreen: false, - transitionDuration: 0, }; const propTypes = { /** Is the window width narrow, like on a mobile device */ isSmallScreen: PropTypes.bool, - /** Transition duration in milliseconds */ - transitionDuration: PropTypes.number, - ...defaultPropTypes, }; function BackgroundImage(props) { diff --git a/src/pages/signin/SignInPageLayout/BackgroundImage/index.js b/src/pages/signin/SignInPageLayout/BackgroundImage/index.js index 19504852d541..96406d6f3d31 100644 --- a/src/pages/signin/SignInPageLayout/BackgroundImage/index.js +++ b/src/pages/signin/SignInPageLayout/BackgroundImage/index.js @@ -8,16 +8,12 @@ import defaultPropTypes from './propTypes'; const defaultProps = { isSmallScreen: false, - transitionDuration: 0, }; const propTypes = { /** Is the window width narrow, like on a mobile device */ isSmallScreen: PropTypes.bool, - /** Tranistion duration in milisecond */ - transitionDuration: PropTypes.number, - ...defaultPropTypes, }; function BackgroundImage(props) { diff --git a/src/pages/signin/SignInPageLayout/BackgroundImage/propTypes.js b/src/pages/signin/SignInPageLayout/BackgroundImage/propTypes.js index 533d22ad12c5..d966b196b725 100644 --- a/src/pages/signin/SignInPageLayout/BackgroundImage/propTypes.js +++ b/src/pages/signin/SignInPageLayout/BackgroundImage/propTypes.js @@ -6,6 +6,9 @@ const propTypes = { /** The width of the image. */ width: PropTypes.number.isRequired, + + /** Transition duration in milliseconds */ + transitionDuration: PropTypes.number, }; export default propTypes; diff --git a/src/pages/signin/SignInPageLayout/index.js b/src/pages/signin/SignInPageLayout/index.js index 635f500c4aa3..fdb7221cbf15 100644 --- a/src/pages/signin/SignInPageLayout/index.js +++ b/src/pages/signin/SignInPageLayout/index.js @@ -128,7 +128,7 @@ function SignInPageLayout(props) { isSmallScreen={false} pointerEvents="none" width={variables.signInHeroBackgroundWidth} - transitionDuration={CONST.BACKGROUND_IMAGE_TRANSITION} + transitionDuration={CONST.BACKGROUND_IMAGE_TRANSITION_DURATION} /> From 3a0f56f557df430809912a2f51e34d169a90ad73 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 31 Jan 2024 08:04:35 +0700 Subject: [PATCH 0916/1250] fix incorrect const call --- nextjs-starter-medusa-billplz | 1 + src/pages/signin/SignInPageLayout/index.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 nextjs-starter-medusa-billplz diff --git a/nextjs-starter-medusa-billplz b/nextjs-starter-medusa-billplz new file mode 160000 index 000000000000..af3c68c1557f --- /dev/null +++ b/nextjs-starter-medusa-billplz @@ -0,0 +1 @@ +Subproject commit af3c68c1557f7091cb7d9b8aeeb5e5a9b8e8ba89 diff --git a/src/pages/signin/SignInPageLayout/index.js b/src/pages/signin/SignInPageLayout/index.js index fdb7221cbf15..5465a3189818 100644 --- a/src/pages/signin/SignInPageLayout/index.js +++ b/src/pages/signin/SignInPageLayout/index.js @@ -168,7 +168,7 @@ function SignInPageLayout(props) { isSmallScreen pointerEvents="none" width={variables.signInHeroBackgroundWidthMobile} - transitionDuration={CONST.BACKGROUND_IMAGE_TRANSITION} + transitionDuration={CONST.BACKGROUND_IMAGE_TRANSITION_DURATION} /> Date: Wed, 31 Jan 2024 08:22:33 +0700 Subject: [PATCH 0917/1250] remove unrelated file --- nextjs-starter-medusa-billplz | 1 - 1 file changed, 1 deletion(-) delete mode 160000 nextjs-starter-medusa-billplz diff --git a/nextjs-starter-medusa-billplz b/nextjs-starter-medusa-billplz deleted file mode 160000 index af3c68c1557f..000000000000 --- a/nextjs-starter-medusa-billplz +++ /dev/null @@ -1 +0,0 @@ -Subproject commit af3c68c1557f7091cb7d9b8aeeb5e5a9b8e8ba89 From 77df109cdabe30afc19c9e510d326fd6a9b78eda Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 31 Jan 2024 08:27:26 +0700 Subject: [PATCH 0918/1250] remove unused import and var --- .../signin/SignInPageLayout/BackgroundImage/index.android.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js b/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js index 06784ddef007..8d261134baba 100644 --- a/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js +++ b/src/pages/signin/SignInPageLayout/BackgroundImage/index.android.js @@ -1,14 +1,9 @@ import {Image} from 'expo-image'; -import PropTypes from 'prop-types'; import React from 'react'; import AndroidBackgroundImage from '@assets/images/home-background--android.svg'; import useThemeStyles from '@hooks/useThemeStyles'; import defaultPropTypes from './propTypes'; -const defaultProps = { - transitionDuration: 0, -}; - function BackgroundImage(props) { const styles = useThemeStyles(); return ( From 40c979c99065e128832e5bd383555719e9fc72de Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Wed, 31 Jan 2024 03:07:32 +0100 Subject: [PATCH 0919/1250] fix --- src/components/ReportActionItem/ReportPreview.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index b2fece085f57..712dd6e56760 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -229,7 +229,10 @@ function ReportPreview({ }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; return ( - + { From a4e5fbaf085f9dfa406748d6e5a4ea17db89fc6b Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 31 Jan 2024 09:38:14 +0700 Subject: [PATCH 0920/1250] fix lint --- src/components/ReportActionItem/TaskView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/TaskView.tsx b/src/components/ReportActionItem/TaskView.tsx index 414bd70155f0..bdcc4d01b1a8 100644 --- a/src/components/ReportActionItem/TaskView.tsx +++ b/src/components/ReportActionItem/TaskView.tsx @@ -28,7 +28,7 @@ import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {PersonalDetailsList, Policy, Report} from '@src/types/onyx'; +import type {PersonalDetailsList, Report} from '@src/types/onyx'; type TaskViewOnyxProps = { /** All of the personal details for everyone */ From c8679c727db9755404599fa61943d3a36e1a1f68 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 31 Jan 2024 09:50:45 +0700 Subject: [PATCH 0921/1250] fix logic show new user engagement modal --- .../PurposeForUsingExpensifyModal.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/PurposeForUsingExpensifyModal.tsx b/src/components/PurposeForUsingExpensifyModal.tsx index c02815d74153..d464296edd84 100644 --- a/src/components/PurposeForUsingExpensifyModal.tsx +++ b/src/components/PurposeForUsingExpensifyModal.tsx @@ -1,6 +1,8 @@ import {useNavigation} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {ScrollView, View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -11,6 +13,7 @@ import * as Report from '@userActions/Report'; import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; +import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import HeaderWithBackButton from './HeaderWithBackButton'; import * as Expensicons from './Icon/Expensicons'; @@ -78,8 +81,12 @@ const menuIcons = { [CONST.INTRO_CHOICES.MANAGE_TEAM]: Expensicons.MoneyBag, [CONST.INTRO_CHOICES.CHAT_SPLIT]: Expensicons.Briefcase, }; +type PurposeForUsingExpensifyModalOnyxProps = { + isLoadingApp: OnyxEntry; +}; +type PurposeForUsingExpensifyModalProps = PurposeForUsingExpensifyModalOnyxProps; -function PurposeForUsingExpensifyModal() { +function PurposeForUsingExpensifyModal({isLoadingApp = false}: PurposeForUsingExpensifyModalProps) { const {translate} = useLocalize(); const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); @@ -98,7 +105,7 @@ function PurposeForUsingExpensifyModal() { Welcome.show(routes, () => setIsModalOpen(true)); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [isLoadingApp]); const closeModal = useCallback(() => { Report.dismissEngagementModal(); @@ -174,5 +181,8 @@ function PurposeForUsingExpensifyModal() { } PurposeForUsingExpensifyModal.displayName = 'PurposeForUsingExpensifyModal'; - -export default PurposeForUsingExpensifyModal; +export default withOnyx({ + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, +})(PurposeForUsingExpensifyModal); From 414136a8d9e7299b3cc6217893bc98e37307f2a5 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 31 Jan 2024 11:15:51 +0700 Subject: [PATCH 0922/1250] 34612 --- ...oraryForRefactorRequestConfirmationList.js | 47 +++++++++---------- .../iou/request/step/IOURequestStepTag.js | 4 +- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index bdd3fa98672f..98be34c41bf9 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -810,30 +810,29 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ rightLabel={canUseViolations && Boolean(policy.requiresCategory) ? translate('common.required') : ''} /> )} - {shouldShowTags || - (true && ( - - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( - CONST.IOU.ACTION.CREATE, - iouType, - transaction.transactionID, - reportID, - Navigation.getActiveRouteWithoutParams(), - ), - ) - } - style={[styles.moneyRequestMenuItem]} - disabled={didConfirm} - interactive={!isReadOnly} - rightLabel={canUseViolations && Boolean(policy.requiresTag) ? translate('common.required') : ''} - /> - ))} + {shouldShowTags && ( + + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction.transactionID, + reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) + } + style={[styles.moneyRequestMenuItem]} + disabled={didConfirm} + interactive={!isReadOnly} + rightLabel={canUseViolations && lodashGet(policy, 'requiresTag', false) ? translate('common.required') : ''} + /> + )} {shouldShowTax && ( { Navigation.goBack(backTo || ROUTES.HOME); @@ -68,7 +68,7 @@ function IOURequestStepTag({ const updateTag = (selectedTag) => { const isSelectedTag = selectedTag.searchText === tag; const updatedTag = !isSelectedTag ? selectedTag.searchText : ''; - if (isEditting) { + if (isEditing) { IOU.updateMoneyRequestTag(transactionID, report.reportID, updatedTag); Navigation.dismissModal(); return; From da41c762df457e061f90c33c2badddb1f2e9aa9f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 31 Jan 2024 12:25:23 +0800 Subject: [PATCH 0923/1250] don't go to sign in modal if the route is empty --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1f6905cfb8e0..b9eb1aecb412 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1984,7 +1984,7 @@ function openReportFromDeepLink(url: string, isAuthenticated: boolean) { navigateToConciergeChat(true); return; } - if (Session.isAnonymousUser() && !Session.canAccessRouteByAnonymousUser(route)) { + if (route && Session.isAnonymousUser() && !Session.canAccessRouteByAnonymousUser(route)) { Session.signOutAndRedirectToSignIn(true); return; } From a0763958c623991b8ac85531e1bcb26f1551352f Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Wed, 31 Jan 2024 07:20:04 +0100 Subject: [PATCH 0924/1250] prevent crash on Backspace press when magic input is not focused --- src/components/MagicCodeInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MagicCodeInput.tsx b/src/components/MagicCodeInput.tsx index 4a6d87b48e38..9af07bef6af1 100644 --- a/src/components/MagicCodeInput.tsx +++ b/src/components/MagicCodeInput.tsx @@ -278,7 +278,7 @@ function MagicCodeInput( const indexToFocus = numbers[editIndex] === CONST.MAGIC_CODE_EMPTY_CHAR ? indexBeforeLastEditIndex : editIndex; const formElement = inputRefs.current as HTMLFormElement | null; - (formElement?.[indexToFocus] as HTMLInputElement).focus(); + (formElement?.[indexToFocus] as HTMLInputElement)?.focus(); onChangeTextProp(value.substring(0, indexToFocus)); return; From 61267ec0fd44cabd884405711e100ef9f1ed5cb4 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:08:18 +0100 Subject: [PATCH 0925/1250] fix not always showing announce rooms --- src/pages/workspace/WorkspacesListPage.tsx | 58 ++++++++++++---------- src/types/onyx/Policy.ts | 4 ++ 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 4001e34a3529..4bd86de4001b 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -13,6 +13,7 @@ import LottieAnimations from '@components/LottieAnimations'; import type {MenuItemProps} from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import type {OfflineWithFeedbackProps} from '@components/OfflineWithFeedback'; +import type {PopoverMenuItem} from '@components/PopoverMenu'; import {PressableWithoutFeedback} from '@components/Pressable'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; @@ -131,32 +132,39 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r */ const getMenuItem = useCallback( ({item, index}: GetMenuItem) => { - const threeDotsMenuItems = [ - // Check if the user is an admin of the workspace - ...(item.role === CONST.POLICY.ROLE.ADMIN - ? [ - { - icon: Expensicons.Trashcan, - text: translate('workspace.common.delete'), - onSelected: () => { - setPolicyIDToDelete(item.policyID ?? ''); - setPolicyNameToDelete(item.title); - setIsDeleteModalOpen(true); - }, - }, - { - icon: Expensicons.Hashtag, - text: translate('workspace.common.goToRoom', {roomName: CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS}), - onSelected: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.adminRoom ?? '')), - }, - ] - : []), - { + const isAdmin = item.role === CONST.POLICY.ROLE.ADMIN; + // Menu options to navigate to the chat report of #admins and #announce room. + // For navigation, the chat report ids may be unavailable due to the missing chat reports in Onyx. + // In such cases, let us use the available chat report ids from the policy. + const threeDotsMenuItems: PopoverMenuItem[] = []; + + if (isAdmin) { + threeDotsMenuItems.push({ + icon: Expensicons.Trashcan, + text: translate('workspace.common.delete'), + onSelected: () => { + setPolicyIDToDelete(item.policyID ?? ''); + setPolicyNameToDelete(item.title); + setIsDeleteModalOpen(true); + }, + }); + } + + if (isAdmin && item.adminRoom) { + threeDotsMenuItems.push({ + icon: Expensicons.Hashtag, + text: translate('workspace.common.goToRoom', {roomName: CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS}), + onSelected: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.adminRoom ?? '')), + }); + } + + if (item.announceRoom) { + threeDotsMenuItems.push({ icon: Expensicons.Hashtag, text: translate('workspace.common.goToRoom', {roomName: CONST.REPORT.WORKSPACE_CHAT_ROOMS.ANNOUNCE}), onSelected: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.announceRoom ?? '')), - }, - ]; + }); + } return ( Date: Wed, 31 Jan 2024 08:13:49 +0100 Subject: [PATCH 0926/1250] fix padding (PR 35126) --- src/components/HeaderWithBackButton/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 9a50b61cb1c3..6c89221d9217 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -71,7 +71,7 @@ function HeaderWithBackButton({ // Hover on some part of close icons will not work on Electron if dragArea is true // https://github.com/Expensify/App/issues/29598 dataSet={{dragArea: false}} - style={[styles.headerBar, shouldShowBorderBottom && styles.borderBottom, shouldShowBackButton ? styles.pl2 : styles.pl5, shouldOverlay && StyleSheet.absoluteFillObject]} + style={[styles.headerBar, shouldShowBorderBottom && styles.borderBottom, shouldShowBackButton ? styles.pl0 : styles.pl5, shouldOverlay && StyleSheet.absoluteFillObject]} > {shouldShowBackButton && ( From 17f3d4844c2db4ca8a52d8e8bc990be39aaba1e1 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 31 Jan 2024 14:21:08 +0700 Subject: [PATCH 0927/1250] fix lint --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 98be34c41bf9..122001861827 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -818,13 +818,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( - CONST.IOU.ACTION.CREATE, - iouType, - transaction.transactionID, - reportID, - Navigation.getActiveRouteWithoutParams(), - ), + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), ) } style={[styles.moneyRequestMenuItem]} From 5ea2e8c458a71e4d644d476307b95cf9fd442fcf Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 31 Jan 2024 14:24:51 +0700 Subject: [PATCH 0928/1250] fix lint --- src/components/AttachmentModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 6176746b2758..1b986098657f 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -131,7 +131,7 @@ type AttachmentModalProps = AttachmentModalOnyxProps & { isWorkspaceAvatar?: boolean; /** Denotes whether it can be an icon (ex: SVG) */ - maybeIcon?: boolean, + maybeIcon?: boolean; /** Whether it is a receipt attachment or not */ isReceiptAttachment?: boolean; From a5344d7ced07f765c98a4b9dd416ab624f56e0f9 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:27:05 +0100 Subject: [PATCH 0929/1250] center vertically HeaderSkeletonView --- src/components/ReportHeaderSkeletonView.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx index 8b8bb721a5dc..2113abd85e88 100644 --- a/src/components/ReportHeaderSkeletonView.tsx +++ b/src/components/ReportHeaderSkeletonView.tsx @@ -25,7 +25,7 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = () return ( - + {isSmallScreenWidth && ( From da78bf0f5dc6d4df15833b65e7044a64914c6058 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 31 Jan 2024 12:40:28 +0500 Subject: [PATCH 0930/1250] fix: crash and profile not updating --- src/libs/ReportUtils.ts | 2 +- .../ReportActionCompose/ReportActionCompose.js | 2 ++ src/pages/home/report/ReportActionItemCreated.tsx | 14 +++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8f0e6b7eee57..228db29aea6c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1439,7 +1439,7 @@ function getWorkspaceIcon(report: OnyxEntry, policy: OnyxEntry = */ function getIcons( report: OnyxEntry, - personalDetails: OnyxCollection = allPersonalDetails, + personalDetails: OnyxCollection, defaultIcon: UserUtils.AvatarSource | null = null, defaultName = '', defaultAccountID = -1, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index af871c42a439..cc07716209a2 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -341,6 +341,8 @@ function ReportActionCompose({ const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; const handleSendMessage = useCallback(() => { + 'worklet'; + if (isSendDisabled || !isReportReadyForDisplay) { return; } diff --git a/src/pages/home/report/ReportActionItemCreated.tsx b/src/pages/home/report/ReportActionItemCreated.tsx index a7877914c7f4..f8345ca7d2d0 100644 --- a/src/pages/home/report/ReportActionItemCreated.tsx +++ b/src/pages/home/report/ReportActionItemCreated.tsx @@ -15,7 +15,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, Report} from '@src/types/onyx'; +import type {PersonalDetailsList, Policy, Report} from '@src/types/onyx'; import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground'; type ReportActionItemCreatedOnyxProps = { @@ -24,6 +24,9 @@ type ReportActionItemCreatedOnyxProps = { /** The policy object for the current route */ policy: OnyxEntry; + + /** Personal details of all the users */ + personalDetails: OnyxEntry; }; type ReportActionItemCreatedProps = ReportActionItemCreatedOnyxProps & { @@ -45,7 +48,7 @@ function ReportActionItemCreated(props: ReportActionItemCreatedProps) { return null; } - const icons = ReportUtils.getIcons(props.report); + const icons = ReportUtils.getIcons(props.report, props.personalDetails); const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(props.report); return ( @@ -100,6 +103,10 @@ export default withOnyx `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, + + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, })( memo( ReportActionItemCreated, @@ -108,6 +115,7 @@ export default withOnyx Date: Wed, 31 Jan 2024 09:25:57 +0100 Subject: [PATCH 0931/1250] fix sharecode width --- src/pages/ShareCodePage.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index 1f5dab566e2c..8c84313a7f68 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -91,12 +91,7 @@ class ShareCodePage extends React.Component { /> - + Date: Wed, 31 Jan 2024 09:38:53 +0100 Subject: [PATCH 0932/1250] fix types --- src/components/AttachmentModal.tsx | 2 +- src/libs/Navigation/Navigation.ts | 6 +++++- src/libs/actions/Report.ts | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 5d826da71e90..45f8aa4de0e5 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -281,7 +281,7 @@ function AttachmentModal({ const deleteAndCloseModal = useCallback(() => { IOU.detachReceipt(transaction?.transactionID); setIsDeleteReceiptConfirmModalVisible(false); - Navigation.dismissModalWithReportID(report?.reportID ?? ''); + Navigation.dismissModalWithReportID(report?.reportID); }, [transaction, report]); const isValidFile = useCallback((fileObject: FileObject) => { diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index f293ac1a9127..c0afc7f6c798 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -58,7 +58,11 @@ const dismissModal = (ref = navigationRef) => originalDismissModal(ref); // Then we can pass the report as a param without getting it from the Onyx. const dismissModalWithReport = (report: Report | EmptyObject, ref = navigationRef) => originalDismissModalWithReport(report, ref); -const dismissModalWithReportID = (reportID: string, ref = navigationRef) => { +const dismissModalWithReportID = (reportID?: string, ref = navigationRef) => { + if (!reportID) { + dismissModal(ref); + return; + } const report = getReport(reportID); originalDismissModalWithReport({reportID, ...report}, ref); }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 0370a4bff97a..b1596cf43461 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1553,7 +1553,7 @@ function navigateToConciergeChat(ignoreConciergeReportID = false, shouldDismissM navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], shouldDismissModal); }); } else if (shouldDismissModal) { - Navigation.dismissModal(conciergeChatReportID); + Navigation.dismissModalWithReportID(conciergeChatReportID); } else { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeChatReportID)); } From 4c3945f0095da3db4cc6b9fc8971d52965951792 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 31 Jan 2024 10:19:31 +0100 Subject: [PATCH 0933/1250] Fix TS on WorkspacesListPage --- src/pages/workspace/WorkspacesListPage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 4bd86de4001b..9442ffafa862 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -25,6 +25,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import type {AvatarSource} from '@libs/UserUtils'; import * as App from '@userActions/App'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -38,10 +39,11 @@ import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import WorkspacesListRow from './WorkspacesListRow'; -type WorkspaceItem = Required> & +type WorkspaceItem = Required> & Pick & Pick & Pick & { + icon: AvatarSource; action: () => void; dismissError: () => void; iconType?: ValueOf; From bb2886307eb4f2276df271b246a1fabd52bcd04a Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 31 Jan 2024 16:38:02 +0700 Subject: [PATCH 0934/1250] fix: 34961 --- .../SelectionList/RadioListItem.tsx | 31 ++++---------- src/components/SelectionList/UserListItem.tsx | 31 ++++---------- .../TextWithTooltip/index.native.tsx | 18 ++++++++ src/components/TextWithTooltip/index.tsx | 41 +++++++++++++++++++ src/components/TextWithTooltip/types.ts | 9 ++++ 5 files changed, 86 insertions(+), 44 deletions(-) create mode 100644 src/components/TextWithTooltip/index.native.tsx create mode 100644 src/components/TextWithTooltip/index.tsx create mode 100644 src/components/TextWithTooltip/types.ts diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index 769eaa80df4b..ca0ae859c3ad 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {View} from 'react-native'; -import Text from '@components/Text'; -import Tooltip from '@components/Tooltip'; +import TextWithTooltip from '@components/TextWithTooltip'; import useThemeStyles from '@hooks/useThemeStyles'; import type {RadioListItemProps} from './types'; @@ -10,30 +9,18 @@ function RadioListItem({item, showTooltip, textStyles, alternateTextStyles}: Rad return ( - - - {item.text} - - + textStyles={textStyles} + /> {!!item.alternateText && ( - - - {item.alternateText} - - + textStyles={alternateTextStyles} + /> )} ); diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 3c973ad0bbea..11ef03bec33d 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -1,8 +1,7 @@ import React from 'react'; import {View} from 'react-native'; import SubscriptAvatar from '@components/SubscriptAvatar'; -import Text from '@components/Text'; -import Tooltip from '@components/Tooltip'; +import TextWithTooltip from '@components/TextWithTooltip'; import useThemeStyles from '@hooks/useThemeStyles'; import type {UserListItemProps} from './types'; @@ -18,29 +17,17 @@ function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style /> )} - - - {item.text} - - + textStyles={[textStyles, style]} + /> {!!item.alternateText && ( - - - {item.alternateText} - - + textStyles={[alternateTextStyles, style]} + /> )} {!!item.rightElement && item.rightElement} diff --git a/src/components/TextWithTooltip/index.native.tsx b/src/components/TextWithTooltip/index.native.tsx new file mode 100644 index 000000000000..f8013ae00e4c --- /dev/null +++ b/src/components/TextWithTooltip/index.native.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import Text from '@components/Text'; +import type TextWithTooltipProps from './types'; + +function TextWithTooltip({text, textStyles}: TextWithTooltipProps) { + return ( + + {text} + + ); +} + +TextWithTooltip.displayName = 'TextWithTooltip'; + +export default TextWithTooltip; diff --git a/src/components/TextWithTooltip/index.tsx b/src/components/TextWithTooltip/index.tsx new file mode 100644 index 000000000000..fd202db8de64 --- /dev/null +++ b/src/components/TextWithTooltip/index.tsx @@ -0,0 +1,41 @@ +import React, {useState} from 'react'; +import Text from '@components/Text'; +import Tooltip from '@components/Tooltip'; +import type TextWithTooltipProps from './types'; + +type LayoutChangeEvent = { + target: HTMLElement; +}; + +function TextWithTooltip({text, shouldShowTooltip, textStyles}: TextWithTooltipProps) { + const [showTooltip, setShowTooltip] = useState(false); + + return ( + + { + const target = (e.nativeEvent as unknown as LayoutChangeEvent).target; + if (!shouldShowTooltip) { + return; + } + if (target.scrollWidth > target.offsetWidth) { + setShowTooltip(true); + return; + } + setShowTooltip(false); + }} + > + {text} + + + ); +} + +TextWithTooltip.displayName = 'TextWithTooltip'; + +export default TextWithTooltip; diff --git a/src/components/TextWithTooltip/types.ts b/src/components/TextWithTooltip/types.ts new file mode 100644 index 000000000000..80517b0b2acf --- /dev/null +++ b/src/components/TextWithTooltip/types.ts @@ -0,0 +1,9 @@ +import type {StyleProp, TextStyle} from 'react-native'; + +type TextWithTooltipProps = { + text: string; + shouldShowTooltip: boolean; + textStyles?: StyleProp; +}; + +export default TextWithTooltipProps; From 5b42bdf2aa0c06f83d8965bc168556716861621f Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Wed, 31 Jan 2024 15:15:48 +0530 Subject: [PATCH 0935/1250] update: callback function to hook. Signed-off-by: Krishna Gupta --- src/hooks/useSearchTermAndSearch.ts | 22 +++++++++++++++++++ src/pages/NewChatPage.js | 14 +++--------- ...yForRefactorRequestParticipantsSelector.js | 14 ++---------- .../MoneyRequestParticipantsSelector.js | 18 ++++----------- 4 files changed, 31 insertions(+), 37 deletions(-) create mode 100644 src/hooks/useSearchTermAndSearch.ts diff --git a/src/hooks/useSearchTermAndSearch.ts b/src/hooks/useSearchTermAndSearch.ts new file mode 100644 index 000000000000..827b6c6d8bd1 --- /dev/null +++ b/src/hooks/useSearchTermAndSearch.ts @@ -0,0 +1,22 @@ +import type {Dispatch} from 'react'; +import {useCallback} from 'react'; +import * as Report from '@userActions/Report'; + +/** + * Hook for fetching reports when user updated search term and hasn't selected max number of participants + */ +const useSearchTermAndSearch = (setSearchTerm: Dispatch>, maxParticipantsReached: boolean) => { + const setSearchTermAndSearchInServer = useCallback( + (text = '') => { + if (text && !maxParticipantsReached) { + Report.searchInServer(text); + } + setSearchTerm(text); + }, + [maxParticipantsReached, setSearchTerm], + ); + + return setSearchTermAndSearchInServer; +}; + +export default useSearchTermAndSearch; diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index 9624967a89a3..7dd328e6a0bd 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -11,6 +11,7 @@ import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useNetwork from '@hooks/useNetwork'; +import useSearchTermAndSearch from '@hooks/useSearchTermAndSearch'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; @@ -64,6 +65,8 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; + const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); + const headerMessage = OptionsListUtils.getHeaderMessage( filteredPersonalDetails.length + filteredRecentReports.length !== 0, Boolean(filteredUserToInvite), @@ -229,17 +232,6 @@ function NewChatPage({betas, isGroupChat, personalDetails, reports, translate, i updateOptions(); }, [didScreenTransitionEnd, updateOptions]); - // When search term updates & user hasn't selected max number of participants we will fetch any reports - const setSearchTermAndSearchInServer = useCallback( - (text = '') => { - if (text && !maxParticipantsReached) { - Report.searchInServer(text); - } - setSearchTerm(text); - }, - [maxParticipantsReached], - ); - const {inputCallbackRef} = useAutoFocusInput(); return ( diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index b25b545a5382..e47b0b8198ea 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -13,8 +13,8 @@ import SelectCircle from '@components/SelectCircle'; import SelectionList from '@components/SelectionList'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import useSearchTermAndSearch from '@hooks/useSearchTermAndSearch'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Report from '@libs/actions/Report'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -88,6 +88,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ const offlineMessage = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; + const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); /** * Returns the sections needed for the OptionsSelector @@ -244,17 +245,6 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ [maxParticipantsReached, newChatOptions, participants, searchTerm], ); - // When search term updates & user hasn't selected max number of participants we will fetch any reports - const setSearchTermAndSearchInServer = useCallback( - (text = '') => { - if (text && !maxParticipantsReached) { - Report.searchInServer(text); - } - setSearchTerm(text); - }, - [maxParticipantsReached], - ); - // Right now you can't split a request with a workspace and other additional participants // This is getting properly fixed in https://github.com/Expensify/App/issues/27508, but as a stop-gap to prevent // the app from crashing on native when you try to do this, we'll going to hide the button if you have a workspace and other participants diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index e62d77f1a52a..daaa63aae147 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -13,8 +13,8 @@ import SelectCircle from '@components/SelectCircle'; import SelectionList from '@components/SelectionList'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import useSearchTermAndSearch from '@hooks/useSearchTermAndSearch'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Report from '@libs/actions/Report'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import reportPropTypes from '@pages/reportPropTypes'; @@ -89,6 +89,9 @@ function MoneyRequestParticipantsSelector({ const {isOffline} = useNetwork(); const personalDetails = usePersonalDetails(); + const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; + const setSearchTermAndSearchInServer = useSearchTermAndSearch(setSearchTerm, maxParticipantsReached); + const offlineMessage = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; const newChatOptions = useMemo(() => { @@ -124,8 +127,6 @@ function MoneyRequestParticipantsSelector({ }; }, [betas, reports, participants, personalDetails, searchTerm, iouType, isDistanceRequest]); - const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; - /** * Returns the sections needed for the OptionsSelector * @@ -260,17 +261,6 @@ function MoneyRequestParticipantsSelector({ [maxParticipantsReached, newChatOptions.personalDetails.length, newChatOptions.recentReports.length, newChatOptions.userToInvite, participants, searchTerm], ); - // When search term updates & user hasn't selected max number of participants we will fetch any reports - const setSearchTermAndSearchInServer = useCallback( - (text = '') => { - if (text && !maxParticipantsReached) { - Report.searchInServer(text); - } - setSearchTerm(text); - }, - [maxParticipantsReached], - ); - // Right now you can't split a request with a workspace and other additional participants // This is getting properly fixed in https://github.com/Expensify/App/issues/27508, but as a stop-gap to prevent // the app from crashing on native when you try to do this, we'll going to show error message if you have a workspace and other participants From 1424356bf4928202676746dd0fe0ad0424cb75bf Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 31 Jan 2024 11:00:08 +0100 Subject: [PATCH 0936/1250] Fix showing not-found when the new workspace is created (215) --- src/pages/workspace/WorkspacePageWithSections.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 3f9205fb3eb8..605eed9f64b5 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -2,8 +2,8 @@ import type {RouteProp} from '@react-navigation/native'; import type {ReactNode} from 'react'; import React, {useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -83,6 +83,7 @@ function WorkspacePageWithSections({ guidesCallTaskID = '', headerText, policy, + policyDraft, reimbursementAccount = ReimbursementAccountProps.reimbursementAccountDefaultProps, route, shouldUseScrollView = false, @@ -95,7 +96,6 @@ function WorkspacePageWithSections({ useNetwork({onReconnect: () => fetchData(shouldSkipVBBACall)}); const isLoading = reimbursementAccount?.isLoading ?? true; - const isOverview = route.name; const achState = reimbursementAccount?.achData?.state ?? ''; const isUsingECard = user?.isUsingExpensifyCard ?? false; const policyID = route.params.policyID; @@ -121,13 +121,13 @@ function WorkspacePageWithSections({ }, [shouldSkipVBBACall]); const shouldShow = useMemo(() => { - if (isEmptyObject(policy)) { + if (isEmptyObject(policy) && isEmptyObject(policyDraft)) { return true; } - // TODO - check is the value of isOveriew is correct - return !PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPendingDeletePolicy(policy) || !isOverview; - }, [isOverview, policy]); + return (!isEmptyObject(policy) && !PolicyUtils.isPolicyAdmin(policy)) || PolicyUtils.isPendingDeletePolicy(policy); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [policy]); return ( Date: Wed, 31 Jan 2024 10:12:25 +0100 Subject: [PATCH 0937/1250] cleanup --- src/libs/Navigation/Navigation.ts | 4 ++-- src/libs/Navigation/switchPolicyID.ts | 11 ++++++----- src/libs/Navigation/types.ts | 4 ++-- src/pages/ShareCodePage.js | 4 ++-- src/pages/WorkspaceSwitcherPage.js | 12 ++++-------- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index c0afc7f6c798..31c40759802a 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -18,7 +18,7 @@ import linkingConfig from './linkingConfig'; import linkTo from './linkTo'; import navigationRef from './navigationRef'; import switchPolicyID from './switchPolicyID'; -import type {State, StateOrRoute, switchPolicyIDParams} from './types'; +import type {State, StateOrRoute, SwitchPolicyIDParams} from './types'; let resolveNavigationIsReadyPromise: () => void; const navigationIsReadyPromise = new Promise((resolve) => { @@ -327,7 +327,7 @@ function waitForProtectedRoutes() { }); } -function navigateWithSwitchPolicyID(params: switchPolicyIDParams) { +function navigateWithSwitchPolicyID(params: SwitchPolicyIDParams) { if (!canNavigate('navigateWithSwitchPolicyID')) { return; } diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 521045ddb89f..2130fdfb6f99 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -1,7 +1,7 @@ import {getActionFromState} from '@react-navigation/core'; import type {NavigationAction, NavigationContainerRef, NavigationState, PartialState} from '@react-navigation/native'; import {getPathFromState} from '@react-navigation/native'; -import type {Writable} from 'type-fest'; +import type {ValueOf, Writable} from 'type-fest'; import getIsSmallScreenWidth from '@libs/getIsSmallScreenWidth'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; @@ -11,7 +11,7 @@ import getStateFromPath from './getStateFromPath'; import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; import linkingConfig from './linkingConfig'; import TAB_TO_CENTRAL_PANE_MAPPING from './linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING'; -import type {NavigationRoot, RootStackParamList, StackNavigationAction, State, switchPolicyIDParams} from './types'; +import type {CentralPaneNavigatorParamList, NavigationRoot, RootStackParamList, StackNavigationAction, State, SwitchPolicyIDParams} from './types'; type ActionPayloadParams = { screen?: string; @@ -60,7 +60,7 @@ function getActionForBottomTabNavigator(action: StackNavigationAction, state: Na }; } -export default function switchPolicyID(navigation: NavigationContainerRef | null, {policyID, route, isPolicyAdmin = true}: switchPolicyIDParams) { +export default function switchPolicyID(navigation: NavigationContainerRef | null, {policyID, route, isPolicyAdmin = false}: SwitchPolicyIDParams) { if (!navigation) { throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?"); } @@ -110,17 +110,18 @@ export default function switchPolicyID(navigation: NavigationContainerRef); // Only workspace settings screens have to store the policyID in the params. // In other case, the policyID is read from the BottomTab params. - if (!screen?.startsWith('Workspace_')) { + if (!isWorkspaceScreen) { delete params.policyID; } else { params.policyID = policyID; } // We need to redirect non admin users to overview screen, when switching workspace. - if (!isPolicyAdmin && screen !== SCREENS.WORKSPACE.OVERVIEW) { + if (!isPolicyAdmin && isWorkspaceScreen && screen !== SCREENS.WORKSPACE.OVERVIEW) { screen = SCREENS.WORKSPACE.OVERVIEW; } diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 2ccecc709bca..02d0af545710 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -470,7 +470,7 @@ type CentralPaneName = keyof CentralPaneNavigatorParamList; type FullScreenName = keyof SettingsCentralPaneNavigatorParamList; -type switchPolicyIDParams = { +type SwitchPolicyIDParams = { policyID?: string; route?: Routes; isPolicyAdmin?: boolean; @@ -520,5 +520,5 @@ export type { ReimbursementAccountNavigatorParamList, State, WorkspaceSwitcherNavigatorParamList, - switchPolicyIDParams, + SwitchPolicyIDParams, }; diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js index 8c84313a7f68..3f0ef6ca138e 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -90,8 +90,8 @@ class ShareCodePage extends React.Component { shouldShowBackButton={isReport || this.props.isSmallScreenWidth} /> - - + + { + const selectPolicy = (option) => { const {policyID, isPolicyAdmin} = option; if (policyID) { @@ -123,11 +123,7 @@ function WorkspaceSwitcherPage({policies}) { if (policyID !== activeWorkspaceID) { Navigation.navigateWithSwitchPolicyID({policyID, isPolicyAdmin}); } - }, []); - - const onChangeText = useCallback((newSearchTerm) => { - setSearchTerm(newSearchTerm); - }, []); + }; const usersWorkspaces = useMemo( () => @@ -248,7 +244,7 @@ function WorkspaceSwitcherPage({policies}) { sections={[usersWorkspacesSectionData]} value={searchTerm} shouldShowTextInput={usersWorkspaces.length >= CONST.WORKSPACE_SWITCHER.MINIMUM_WORKSPACES_TO_SHOW_SEARCH} - onChangeText={onChangeText} + onChangeText={(newSearchTerm) => setSearchTerm(newSearchTerm)} selectedOptions={selectedOption ? [selectedOption] : []} onSelectRow={selectPolicy} shouldPreventDefaultFocusOnSelectRow @@ -270,7 +266,7 @@ function WorkspaceSwitcherPage({policies}) { )} ), - [inputCallbackRef, onChangeText, searchTerm, selectPolicy, selectedOption, styles, theme.textSupporting, translate, usersWorkspaces.length, usersWorkspacesSectionData], + [inputCallbackRef, setSearchTerm, searchTerm, selectPolicy, selectedOption, styles, theme.textSupporting, translate, usersWorkspaces.length, usersWorkspacesSectionData], ); useEffect(() => { From 6ee3a1dd5f66691456c9e0340da81ecb282a022c Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 31 Jan 2024 11:18:25 +0100 Subject: [PATCH 0938/1250] Fix active state (212,211) --- src/pages/WorkspaceSwitcherPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/WorkspaceSwitcherPage.js b/src/pages/WorkspaceSwitcherPage.js index 05edef8fc2bd..e6cd8f5ff9a7 100644 --- a/src/pages/WorkspaceSwitcherPage.js +++ b/src/pages/WorkspaceSwitcherPage.js @@ -261,8 +261,8 @@ function WorkspaceSwitcherPage({policies}) { showTitleTooltip={false} contentContainerStyles={[styles.pt0, styles.mt0]} textIconLeft={MagnifyingGlass} - // It has to be set to null or -1 to avoid focus on any element at the beggining - initiallyFocusedOptionKey={null} + // Null is to avoid selecting unfocused option when Global selected, undefined is to focus selected workspace + initiallyFocusedOptionKey={!activeWorkspaceID ? null : undefined} /> ) : ( From 16cac424959a2da4a9161819793a453dfa209c72 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:23:51 +0100 Subject: [PATCH 0939/1250] center search bar text --- src/styles/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 0050692559bc..f895352d6d68 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3197,6 +3197,7 @@ const styles = (theme: ThemeColors) => paddingHorizontal: 24, backgroundColor: theme.hoverComponentBG, borderRadius: variables.componentBorderRadiusRounded, + justifyContent: 'center', }, searchContainerHovered: { @@ -3703,10 +3704,6 @@ const styles = (theme: ThemeColors) => ...flex.alignItemsCenter, }, - shareCodePage: { - paddingHorizontal: 38.5, - }, - shareCodeContainer: { width: '100%', alignItems: 'center', From 4a01664b8147857e878222b3f93073181afe3117 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 31 Jan 2024 11:38:09 +0100 Subject: [PATCH 0940/1250] add temporary fix for splash screen --- src/Expensify.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index 407e5ae09c2d..be01159c95d7 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -4,6 +4,7 @@ import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useStat import {AppState, Linking} from 'react-native'; import Onyx, {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; @@ -23,7 +24,7 @@ import compose from './libs/compose'; import * as Growl from './libs/Growl'; import Log from './libs/Log'; import migrateOnyx from './libs/migrateOnyx'; -import Navigation from './libs/Navigation/Navigation'; +import Navigation, {navigationRef} from './libs/Navigation/Navigation'; import NavigationRoot from './libs/Navigation/NavigationRoot'; import NetworkConnection from './libs/NetworkConnection'; import PushNotification from './libs/Notification/PushNotification'; @@ -37,6 +38,7 @@ import Visibility from './libs/Visibility'; import ONYXKEYS from './ONYXKEYS'; import PopoverReportActionContextMenu from './pages/home/report/ContextMenu/PopoverReportActionContextMenu'; import * as ReportActionContextMenu from './pages/home/report/ContextMenu/ReportActionContextMenu'; +import SCREENS from './SCREENS'; Onyx.registerLogger(({level, message}) => { if (level === 'alert') { @@ -130,7 +132,16 @@ function Expensify(props) { [isSplashHidden], ); - const shouldInit = isNavigationReady && (!isAuthenticated || props.isSidebarLoaded) && hasAttemptedToOpenPublicRoom; + // This is a temporary fix to handle more that one possible screen in the sidebar. + const isSidebarLoaded = useMemo(() => { + if (!isNavigationReady) { + return false; + } + + return getTopmostBottomTabRoute(navigationRef.getState()).name === SCREENS.HOME ? props.isSidebarLoaded : true; + }, [isNavigationReady, props.isSidebarLoaded]); + + const shouldInit = isNavigationReady && (!isAuthenticated || isSidebarLoaded) && hasAttemptedToOpenPublicRoom; const shouldHideSplash = shouldInit && !isSplashHidden; const initializeClient = () => { From 16f8040da7b6f37c01fc63b914aed53059734dea Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:39:14 +0100 Subject: [PATCH 0941/1250] fix settings status button --- .../home/sidebar/AvatarWithOptionalStatus.js | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/pages/home/sidebar/AvatarWithOptionalStatus.js b/src/pages/home/sidebar/AvatarWithOptionalStatus.js index e1ff3982a0cc..80130d50555c 100644 --- a/src/pages/home/sidebar/AvatarWithOptionalStatus.js +++ b/src/pages/home/sidebar/AvatarWithOptionalStatus.js @@ -29,14 +29,13 @@ function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const showStatusPage = useCallback(() => { + const showSettingsPage = useCallback(() => { if (isCreateMenuOpen) { // Prevent opening Settings page when click profile avatar quickly after clicking FAB icon return; } - Navigation.setShouldPopAllStateOnUP(); - Navigation.navigate(ROUTES.SETTINGS_STATUS); + Navigation.navigate(ROUTES.SETTINGS); }, [isCreateMenuOpen]); return ( @@ -45,17 +44,15 @@ function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) { - - - {emojiStatus} - - + + {emojiStatus} + ); From 7291cdfe6158d0508227a7f99e6933d87089b66c Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:44:14 +0100 Subject: [PATCH 0942/1250] refactor dismissModal function --- src/components/AttachmentModal.tsx | 2 +- src/libs/Navigation/Navigation.ts | 18 ++++++++---------- src/libs/actions/IOU.js | 12 ++++++------ src/libs/actions/Report.ts | 4 ++-- src/libs/actions/Task.ts | 2 +- src/libs/actions/TeachersUnite.ts | 4 ++-- src/pages/AddPersonalBankAccountPage.tsx | 2 +- src/pages/EditRequestDistancePage.js | 6 +++--- src/pages/SearchPage.js | 2 +- src/pages/SearchPage/index.js | 2 +- src/pages/settings/Preferences/ThemePage.js | 2 +- src/pages/tasks/TaskAssigneeSelectorModal.js | 2 +- src/pages/tasks/TaskDescriptionPage.js | 4 ++-- src/pages/tasks/TaskTitlePage.js | 4 ++-- src/pages/workspace/WorkspaceNewRoomPage.js | 2 +- tests/actions/IOUTest.js | 2 +- 16 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 45f8aa4de0e5..f3e8ed316c52 100755 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -281,7 +281,7 @@ function AttachmentModal({ const deleteAndCloseModal = useCallback(() => { IOU.detachReceipt(transaction?.transactionID); setIsDeleteReceiptConfirmModalVisible(false); - Navigation.dismissModalWithReportID(report?.reportID); + Navigation.dismissModal(report?.reportID); }, [transaction, report]); const isValidFile = useCallback((fileObject: FileObject) => { diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 31c40759802a..188d8b5f337f 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -51,21 +51,20 @@ const getTopmostReportId = (state = navigationRef.getState()) => originalGetTopm const getTopmostReportActionId = (state = navigationRef.getState()) => originalGetTopmostReportActionId(state); // Re-exporting the dismissModal here to fill in default value for navigationRef. The dismissModal isn't defined in this file to avoid cyclic dependencies. -const dismissModal = (ref = navigationRef) => originalDismissModal(ref); - -// Re-exporting the dismissModalWithReport here to fill in default value for navigationRef. The dismissModalWithReport isn't defined in this file to avoid cyclic dependencies. -// This method is needed because it allows to dismiss the modal and then open the report. Within this method is checked whether the report belongs to a specific workspace. Sometimes the report we want to check, hasn't been added to the Onyx yet. -// Then we can pass the report as a param without getting it from the Onyx. -const dismissModalWithReport = (report: Report | EmptyObject, ref = navigationRef) => originalDismissModalWithReport(report, ref); - -const dismissModalWithReportID = (reportID?: string, ref = navigationRef) => { +const dismissModal = (reportID?: string, ref = navigationRef) => { if (!reportID) { - dismissModal(ref); + originalDismissModal(ref); return; } const report = getReport(reportID); originalDismissModalWithReport({reportID, ...report}, ref); }; + +// Re-exporting the dismissModalWithReport here to fill in default value for navigationRef. The dismissModalWithReport isn't defined in this file to avoid cyclic dependencies. +// This method is needed because it allows to dismiss the modal and then open the report. Within this method is checked whether the report belongs to a specific workspace. Sometimes the report we want to check, hasn't been added to the Onyx yet. +// Then we can pass the report as a param without getting it from the Onyx. +const dismissModalWithReport = (report: Report | EmptyObject, ref = navigationRef) => originalDismissModalWithReport(report, ref); + /** Method for finding on which index in stack we are. */ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number | undefined { if ('routes' in stateOrRoute && stateOrRoute.routes) { @@ -341,7 +340,6 @@ export default { setParams, dismissModal, dismissModalWithReport, - dismissModalWithReportID, isActiveRoute, getActiveRoute, getActiveRouteWithoutParams, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d7857954ebe5..5e7396e5cd8f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -936,7 +936,7 @@ function createDistanceRequest(report, participant, comment, created, category, }, onyxData, ); - Navigation.dismissModalWithReportID(isMoneyRequestReport ? report.reportID : chatReport.reportID); + Navigation.dismissModal(isMoneyRequestReport ? report.reportID : chatReport.reportID); Report.notifyNewAction(chatReport.reportID, userAccountID); } @@ -1364,7 +1364,7 @@ function requestMoney( onyxData, ); resetMoneyRequestInfo(); - Navigation.dismissModalWithReportID(activeReportID); + Navigation.dismissModal(activeReportID); Report.notifyNewAction(activeReportID, payeeAccountID); } @@ -1806,7 +1806,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou ); resetMoneyRequestInfo(); - Navigation.dismissModalWithReportID(splitData.chatReportID); + Navigation.dismissModal(splitData.chatReportID); Report.notifyNewAction(splitData.chatReportID, currentUserAccountID); } @@ -2288,7 +2288,7 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi }, {optimisticData, successData, failureData}, ); - Navigation.dismissModalWithReportID(chatReportID); + Navigation.dismissModal(chatReportID); Report.notifyNewAction(chatReportID, sessionAccountID); } @@ -3244,7 +3244,7 @@ function sendMoneyElsewhere(report, amount, currency, comment, managerID, recipi API.write('SendMoneyElsewhere', params, {optimisticData, successData, failureData}); resetMoneyRequestInfo(); - Navigation.dismissModalWithReportID(params.chatReportID); + Navigation.dismissModal(params.chatReportID); Report.notifyNewAction(params.chatReportID, managerID); } @@ -3262,7 +3262,7 @@ function sendMoneyWithWallet(report, amount, currency, comment, managerID, recip API.write('SendMoneyWithWallet', params, {optimisticData, successData, failureData}); resetMoneyRequestInfo(); - Navigation.dismissModalWithReportID(params.chatReportID); + Navigation.dismissModal(params.chatReportID); Report.notifyNewAction(params.chatReportID, managerID); } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b1596cf43461..028c409ee8fa 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1553,7 +1553,7 @@ function navigateToConciergeChat(ignoreConciergeReportID = false, shouldDismissM navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], shouldDismissModal); }); } else if (shouldDismissModal) { - Navigation.dismissModalWithReportID(conciergeChatReportID); + Navigation.dismissModal(conciergeChatReportID); } else { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeChatReportID)); } @@ -1641,7 +1641,7 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { }; API.write(WRITE_COMMANDS.ADD_WORKSPACE_ROOM, parameters, {optimisticData, successData, failureData}); - Navigation.dismissModalWithReportID(policyReport.reportID); + Navigation.dismissModal(policyReport.reportID); } /** Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. */ diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 45c036f9edb9..a7aab98f02c6 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -244,7 +244,7 @@ function createTaskAndNavigate( API.write(WRITE_COMMANDS.CREATE_TASK, parameters, {optimisticData, successData, failureData}); - Navigation.dismissModalWithReportID(parentReportID); + Navigation.dismissModal(parentReportID); } /** diff --git a/src/libs/actions/TeachersUnite.ts b/src/libs/actions/TeachersUnite.ts index c0259de50f60..055d1f2b53a2 100644 --- a/src/libs/actions/TeachersUnite.ts +++ b/src/libs/actions/TeachersUnite.ts @@ -61,7 +61,7 @@ function referTeachersUniteVolunteer(partnerUserID: string, firstName: string, l }; API.write(WRITE_COMMANDS.REFER_TEACHERS_UNITE_VOLUNTEER, parameters, {optimisticData}); - Navigation.dismissModalWithReportID(publicRoomReportID); + Navigation.dismissModal(publicRoomReportID); } /** @@ -181,7 +181,7 @@ function addSchoolPrincipal(firstName: string, partnerUserID: string, lastName: }; API.write(WRITE_COMMANDS.ADD_SCHOOL_PRINCIPAL, parameters, {optimisticData, successData, failureData}); - Navigation.dismissModalWithReportID(expenseChatReportID); + Navigation.dismissModal(expenseChatReportID); } export default {referTeachersUniteVolunteer, addSchoolPrincipal}; diff --git a/src/pages/AddPersonalBankAccountPage.tsx b/src/pages/AddPersonalBankAccountPage.tsx index f7963976dc05..1876992f9ced 100644 --- a/src/pages/AddPersonalBankAccountPage.tsx +++ b/src/pages/AddPersonalBankAccountPage.tsx @@ -47,7 +47,7 @@ function AddPersonalBankAccountPage({personalBankAccount, plaidData}: AddPersona const onSuccessFallbackRoute = personalBankAccount?.onSuccessFallbackRoute ?? ''; if (exitReportID) { - Navigation.dismissModalWithReportID(exitReportID); + Navigation.dismissModal(exitReportID); } else if (shouldContinue && onSuccessFallbackRoute) { PaymentMethods.continueSetup(onSuccessFallbackRoute); } else { diff --git a/src/pages/EditRequestDistancePage.js b/src/pages/EditRequestDistancePage.js index f4c5fbe0446f..f3ea76a3390a 100644 --- a/src/pages/EditRequestDistancePage.js +++ b/src/pages/EditRequestDistancePage.js @@ -60,7 +60,7 @@ function EditRequestDistancePage({report, route, transaction, transactionBackup} // When the loading goes from true to false, then we know the transaction has just been // saved to the server. Check for errors. If there are no errors, then the modal can be closed. if (prevIsLoading && !transaction.isLoading && !hasWaypointError.current) { - Navigation.dismissModalWithReportID(report.reportID); + Navigation.dismissModal(report.reportID); } }, [transaction, prevIsLoading, report]); @@ -75,7 +75,7 @@ function EditRequestDistancePage({report, route, transaction, transactionBackup} const oldAddresses = _.mapObject(oldWaypoints, (waypoint) => _.pick(waypoint, 'address')); const addresses = _.mapObject(waypoints, (waypoint) => _.pick(waypoint, 'address')); if (_.isEqual(oldAddresses, addresses)) { - Navigation.dismissModalWithReportID(report.reportID); + Navigation.dismissModal(report.reportID); return; } @@ -84,7 +84,7 @@ function EditRequestDistancePage({report, route, transaction, transactionBackup} // If the client is offline, then the modal can be closed as well (because there are no errors or other feedback to show them // until they come online again and sync with the server). if (isOffline) { - Navigation.dismissModalWithReportID(report.reportID); + Navigation.dismissModal(report.reportID); } }; diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 76931d73e18f..d0d2a23bb563 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -155,7 +155,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports, nav return; } if (option.reportID) { - Navigation.dismissModalWithReportID(option.reportID); + Navigation.dismissModal(option.reportID); } else { Report.navigateToAndOpenReport([option.login]); } diff --git a/src/pages/SearchPage/index.js b/src/pages/SearchPage/index.js index 3fd28a39f64b..211f3622e06c 100644 --- a/src/pages/SearchPage/index.js +++ b/src/pages/SearchPage/index.js @@ -123,7 +123,7 @@ function SearchPage({betas, reports}) { if (option.reportID) { setSearchValue(''); - Navigation.dismissModalWithReportID(option.reportID); + Navigation.dismissModal(option.reportID); } else { Report.navigateToAndOpenReport([option.login]); } diff --git a/src/pages/settings/Preferences/ThemePage.js b/src/pages/settings/Preferences/ThemePage.js index 95b280f76076..4907056be761 100644 --- a/src/pages/settings/Preferences/ThemePage.js +++ b/src/pages/settings/Preferences/ThemePage.js @@ -41,7 +41,7 @@ function ThemePage(props) { title={translate('themePage.theme')} shouldShowBackButton onBackButtonPress={() => Navigation.goBack()} - onCloseButtonPress={() => Navigation.dismissModal(true)} + onCloseButtonPress={() => Navigation.dismissModal()} /> {translate('themePage.chooseThemeBelowOrSync')} diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.js b/src/pages/tasks/TaskAssigneeSelectorModal.js index 6ab37b1196b9..f3627a9bd04d 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.js +++ b/src/pages/tasks/TaskAssigneeSelectorModal.js @@ -139,7 +139,7 @@ function TaskAssigneeSelectorModal(props) { if (report && !ReportUtils.isTaskReport(report)) { Navigation.isNavigationReady().then(() => { - Navigation.dismissModalWithReportID(report.reportID); + Navigation.dismissModal(report.reportID); }); } diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index bc2ade3aad0a..3a6999d4408a 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -60,14 +60,14 @@ function TaskDescriptionPage(props) { Task.editTask(props.report, {description: values.description}); } - Navigation.dismissModalWithReportID(props.report.reportID); + Navigation.dismissModal(props.report.reportID); }, [props], ); if (!ReportUtils.isTaskReport(props.report)) { Navigation.isNavigationReady().then(() => { - Navigation.dismissModalWithReportID(props.report.reportID); + Navigation.dismissModal(props.report.reportID); }); } const inputRef = useRef(null); diff --git a/src/pages/tasks/TaskTitlePage.js b/src/pages/tasks/TaskTitlePage.js index 351540b64e31..9b393a8a2374 100644 --- a/src/pages/tasks/TaskTitlePage.js +++ b/src/pages/tasks/TaskTitlePage.js @@ -66,14 +66,14 @@ function TaskTitlePage(props) { Task.editTask(props.report, {title: values.title}); } - Navigation.dismissModalWithReportID(props.report.reportID); + Navigation.dismissModal(props.report.reportID); }, [props], ); if (!ReportUtils.isTaskReport(props.report)) { Navigation.isNavigationReady().then(() => { - Navigation.dismissModalWithReportID(props.report.reportID); + Navigation.dismissModal(props.report.reportID); }); } diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index e3068ffb1de8..b616b519ff32 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -175,7 +175,7 @@ function WorkspaceNewRoomPage(props) { if (!(((wasLoading && !props.formState.isLoading) || (isOffline && props.formState.isLoading)) && _.isEmpty(props.formState.errorFields))) { return; } - Navigation.dismissModalWithReportID(newRoomReportID); + Navigation.dismissModal(newRoomReportID); // eslint-disable-next-line react-hooks/exhaustive-deps -- we just want this to update on changing the form State }, [props.formState]); diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js index cc5088ebd4c9..9c3c5a756faf 100644 --- a/tests/actions/IOUTest.js +++ b/tests/actions/IOUTest.js @@ -24,7 +24,7 @@ jest.mock('../../src/libs/Navigation/Navigation', () => ({ navigate: jest.fn(), dismissModal: jest.fn(), dismissModalWithReport: jest.fn(), - dismissModalWithReportID: jest.fn(), + dismissModal: jest.fn(), goBack: jest.fn(), })); From a47c9b2644e3c497656e040720f5ec4969344416 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 31 Jan 2024 11:50:46 +0100 Subject: [PATCH 0943/1250] fix lint --- src/Expensify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Expensify.js b/src/Expensify.js index be01159c95d7..d9282229b749 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -4,7 +4,6 @@ import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useStat import {AppState, Linking} from 'react-native'; import Onyx, {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; @@ -24,6 +23,7 @@ import compose from './libs/compose'; import * as Growl from './libs/Growl'; import Log from './libs/Log'; import migrateOnyx from './libs/migrateOnyx'; +import getTopmostBottomTabRoute from './libs/Navigation/getTopmostBottomTabRoute'; import Navigation, {navigationRef} from './libs/Navigation/Navigation'; import NavigationRoot from './libs/Navigation/NavigationRoot'; import NetworkConnection from './libs/NetworkConnection'; From b33e251bd0876f3a04f036658680664c653d0e61 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:24:30 +0100 Subject: [PATCH 0944/1250] modify margins for workspace switcher (200) --- src/pages/WorkspaceSwitcherPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/WorkspaceSwitcherPage.js b/src/pages/WorkspaceSwitcherPage.js index 05edef8fc2bd..23d79898e58c 100644 --- a/src/pages/WorkspaceSwitcherPage.js +++ b/src/pages/WorkspaceSwitcherPage.js @@ -213,7 +213,7 @@ function WorkspaceSwitcherPage({policies}) { const workspacesSection = useMemo( () => ( <> - 0 ? [styles.mb1] : [styles.mb3])]}> + 0 ? [styles.mb0] : [styles.mb3])]}> Date: Wed, 31 Jan 2024 12:31:04 +0100 Subject: [PATCH 0945/1250] Revert optional status changes --- .../home/sidebar/AvatarWithOptionalStatus.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/pages/home/sidebar/AvatarWithOptionalStatus.js b/src/pages/home/sidebar/AvatarWithOptionalStatus.js index 80130d50555c..e1ff3982a0cc 100644 --- a/src/pages/home/sidebar/AvatarWithOptionalStatus.js +++ b/src/pages/home/sidebar/AvatarWithOptionalStatus.js @@ -29,13 +29,14 @@ function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const showSettingsPage = useCallback(() => { + const showStatusPage = useCallback(() => { if (isCreateMenuOpen) { // Prevent opening Settings page when click profile avatar quickly after clicking FAB icon return; } - Navigation.navigate(ROUTES.SETTINGS); + Navigation.setShouldPopAllStateOnUP(); + Navigation.navigate(ROUTES.SETTINGS_STATUS); }, [isCreateMenuOpen]); return ( @@ -44,15 +45,17 @@ function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) { - - {emojiStatus} - + + + {emojiStatus} + + ); From 6dbc81eb6db46db01a1251838042d1b412a61592 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Wed, 31 Jan 2024 13:33:31 +0200 Subject: [PATCH 0946/1250] Add newest auto reporting frequency --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 1ccdfd9a82a8..f434aa281866 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1301,6 +1301,7 @@ const CONST = { USER: 'user', }, AUTO_REPORTING_FREQUENCIES: { + INSTANT: 'instant', IMMEDIATE: 'immediate', WEEKLY: 'weekly', SEMI_MONTHLY: 'semimonthly', From e6e22bf0796f57af56ba1168c0d4b47df7f69704 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:33:48 +0100 Subject: [PATCH 0947/1250] fix lint --- src/libs/Navigation/switchPolicyID.ts | 2 +- tests/actions/IOUTest.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 2130fdfb6f99..0cbd4874e7ae 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -11,7 +11,7 @@ import getStateFromPath from './getStateFromPath'; import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; import linkingConfig from './linkingConfig'; import TAB_TO_CENTRAL_PANE_MAPPING from './linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING'; -import type {CentralPaneNavigatorParamList, NavigationRoot, RootStackParamList, StackNavigationAction, State, SwitchPolicyIDParams} from './types'; +import type {NavigationRoot, RootStackParamList, StackNavigationAction, State, SwitchPolicyIDParams} from './types'; type ActionPayloadParams = { screen?: string; diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js index 9c3c5a756faf..92b39fc3ac50 100644 --- a/tests/actions/IOUTest.js +++ b/tests/actions/IOUTest.js @@ -24,7 +24,6 @@ jest.mock('../../src/libs/Navigation/Navigation', () => ({ navigate: jest.fn(), dismissModal: jest.fn(), dismissModalWithReport: jest.fn(), - dismissModal: jest.fn(), goBack: jest.fn(), })); From c9113e75123723f3a225234ed2d60431472f96fd Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Wed, 31 Jan 2024 13:33:48 +0200 Subject: [PATCH 0948/1250] Fix comment typo --- src/types/onyx/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index eca7e9d1ee06..2a3477ba7c2c 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -85,7 +85,7 @@ type Policy = { /** Whether the auto reporting is enabled */ autoReporting?: boolean; - /** The scheduled submit frequency set up on the this policy */ + /** The scheduled submit frequency set up on this policy */ autoReportingFrequency?: ValueOf; /** Whether the scheduled submit is enabled */ From 7e6042dbc70a468a9fdf19bd467500f5a40bdb13 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 31 Jan 2024 12:37:01 +0100 Subject: [PATCH 0949/1250] rename const money_report --- src/CONST.ts | 2 +- src/styles/utils/index.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 86be7997b25c..0bb24c68928e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -147,7 +147,7 @@ const CONST = { CONTAINER_MINHEIGHT: 500, VIEW_HEIGHT: 275, }, - MONEY_REPORT: { + MONEY_OR_TASK_REPORT: { SMALL_SCREEN: { IMAGE_HEIGHT: 300, CONTAINER_MINHEIGHT: 280, diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index a0f8f52927b9..79e3809108b4 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -699,8 +699,8 @@ function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAv /** * Gets the correct size for the empty state background image based on screen dimensions */ -function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ImageStyle { - const emptyStateBackground = isMoneyReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT : CONST.EMPTY_STATE_BACKGROUND; +function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMoneyOrTaskReport = false): ImageStyle { + const emptyStateBackground = isMoneyOrTaskReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_OR_TASK_REPORT : CONST.EMPTY_STATE_BACKGROUND; if (isSmallScreenWidth) { return { @@ -720,8 +720,8 @@ function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean, isMon /** * Gets the correct top margin size for the chat welcome message based on screen dimensions */ -function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ViewStyle { - const emptyStateBackground = isMoneyReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT : CONST.EMPTY_STATE_BACKGROUND; +function getReportWelcomeTopMarginStyle(isSmallScreenWidth: boolean, isMoneyOrTaskReport = false): ViewStyle { + const emptyStateBackground = isMoneyOrTaskReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_OR_TASK_REPORT : CONST.EMPTY_STATE_BACKGROUND; if (isSmallScreenWidth) { return { marginTop: emptyStateBackground.SMALL_SCREEN.VIEW_HEIGHT, @@ -754,8 +754,8 @@ function getLineHeightStyle(lineHeight: number): TextStyle { /** * Gets the correct size for the empty state container based on screen dimensions */ -function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean, isMoneyReport = false): ViewStyle { - const emptyStateBackground = isMoneyReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT : CONST.EMPTY_STATE_BACKGROUND; +function getReportWelcomeContainerStyle(isSmallScreenWidth: boolean, isMoneyOrTaskReport = false): ViewStyle { + const emptyStateBackground = isMoneyOrTaskReport ? CONST.EMPTY_STATE_BACKGROUND.MONEY_OR_TASK_REPORT : CONST.EMPTY_STATE_BACKGROUND; if (isSmallScreenWidth) { return { minHeight: emptyStateBackground.SMALL_SCREEN.CONTAINER_MINHEIGHT, From e7ff2176847948ce3c0a5d5121393c08bbabd5e0 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 31 Jan 2024 12:39:06 +0100 Subject: [PATCH 0950/1250] Fix TS issues after merging main --- src/components/DotIndicatorMessage.tsx | 5 ++--- src/components/MessagesRow.tsx | 3 ++- src/components/OfflineWithFeedback.tsx | 5 +++-- src/components/ReportActionItem/MoneyRequestView.tsx | 6 +++++- src/libs/Localize/index.ts | 6 +++++- src/libs/ReceiptUtils.ts | 2 +- src/types/onyx/Transaction.ts | 2 +- 7 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/components/DotIndicatorMessage.tsx b/src/components/DotIndicatorMessage.tsx index d18704fdfb05..d2143f5b48da 100644 --- a/src/components/DotIndicatorMessage.tsx +++ b/src/components/DotIndicatorMessage.tsx @@ -8,13 +8,12 @@ import useThemeStyles from '@hooks/useThemeStyles'; import fileDownload from '@libs/fileDownload'; import * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; +import type {ReceiptError} from '@src/types/onyx/Transaction'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import {PressableWithoutFeedback} from './Pressable'; import Text from './Text'; -type ReceiptError = {error?: string; source: string; filename: string}; - type DotIndicatorMessageProps = { /** * In most cases this should just be errors from onxyData @@ -23,7 +22,7 @@ type DotIndicatorMessageProps = { * timestamp: 'message', * } */ - messages: Record; + messages: Record; /** The type of message, 'error' shows a red dot, 'success' shows a green dot */ type: 'error' | 'success'; diff --git a/src/components/MessagesRow.tsx b/src/components/MessagesRow.tsx index cfec6fd292e9..7c764ec94fcd 100644 --- a/src/components/MessagesRow.tsx +++ b/src/components/MessagesRow.tsx @@ -6,6 +6,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import type * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; +import type {ReceiptError} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import DotIndicatorMessage from './DotIndicatorMessage'; import Icon from './Icon'; @@ -15,7 +16,7 @@ import Tooltip from './Tooltip'; type MessagesRowProps = { /** The messages to display */ - messages: Record; + messages: Record; /** The type of message, 'error' shows a red dot, 'success' shows a green dot */ type: 'error' | 'success'; diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index 1a8f313af267..2fad21fb54ef 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -8,6 +8,7 @@ import mapChildrenFlat from '@libs/mapChildrenFlat'; import shouldRenderOffscreen from '@libs/shouldRenderOffscreen'; import CONST from '@src/CONST'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import MessagesRow from './MessagesRow'; @@ -26,7 +27,7 @@ type OfflineWithFeedbackProps = ChildrenProps & { shouldHideOnDelete?: boolean; /** The errors to display */ - errors?: OnyxCommon.Errors | null; + errors?: OnyxCommon.Errors | ReceiptErrors | null; /** Whether we should show the error messages */ shouldShowErrorMessages?: boolean; @@ -84,7 +85,7 @@ function OfflineWithFeedback({ const hasErrors = !isEmptyObject(errors ?? {}); // Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages. - const errorMessages = omitBy(errors, (e) => e === null); + const errorMessages = omitBy(errors, (e: string | ReceiptError) => e === null); const hasErrorMessages = !isEmptyObject(errorMessages); const isOfflinePendingAction = !!isOffline && !!pendingAction; const isUpdateOrDeleteError = hasErrors && (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 6e814811f858..ee727ad81518 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -205,9 +205,13 @@ function MoneyRequestView({ {hasReceipt && ( { + if (!transaction?.transactionID) { + return; + } + Transaction.clearError(transaction.transactionID); }} > diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index bc40f93dd13b..46ca550eaa1a 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -7,6 +7,7 @@ import CONST from '@src/CONST'; import translations from '@src/languages/translations'; import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {ReceiptError} from '@src/types/onyx/Transaction'; import LocaleListener from './LocaleListener'; import BaseLocaleListener from './LocaleListener/BaseLocaleListener'; @@ -102,7 +103,10 @@ type MaybePhraseKey = string | [string, Record & {isTranslated? /** * Return translated string for given error. */ -function translateIfPhraseKey(message: MaybePhraseKey): string { +function translateIfPhraseKey(message: MaybePhraseKey): string; +function translateIfPhraseKey(message: ReceiptError): ReceiptError; +function translateIfPhraseKey(message: MaybePhraseKey | ReceiptError): string | ReceiptError; +function translateIfPhraseKey(message: MaybePhraseKey | ReceiptError): string | ReceiptError { if (!message || (Array.isArray(message) && message.length === 0)) { return ''; } diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index cbffb5b454b3..444b9b0f3954 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -37,7 +37,7 @@ function getThumbnailAndImageURIs(transaction: OnyxEntry, receiptPa } // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg // If there're errors, we need to display them in preview. We can store many files in errors, but we just need to get the last one - const errors = _.findLast(transaction.errors) as ReceiptError | undefined; + const errors = _.findLast(transaction?.errors) as ReceiptError | undefined; const path = errors?.source ?? transaction?.receipt?.source ?? receiptPath ?? ''; // filename of uploaded image or last part of remote URI const filename = errors?.filename ?? transaction?.filename ?? receiptFileName ?? ''; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 8f2b9d03dc9e..fe7ca7436a81 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -103,4 +103,4 @@ type Transaction = { }; export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint, ReceiptError, TransactionPendingFieldsKey}; +export type {WaypointCollection, Comment, Receipt, Waypoint, ReceiptError, ReceiptErrors, TransactionPendingFieldsKey}; From 73c6c75ee0d010ddde9e8f7034c75a9fede32286 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:42:44 +0100 Subject: [PATCH 0951/1250] fix loading indicator in the header --- src/pages/home/HeaderView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 1e3d34af64e7..c2d1ecffc2a7 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -227,7 +227,7 @@ function HeaderView(props) { dataSet={{dragArea: true}} > - + {isLoading ? ( ) : ( From c2ad5db8ab4eeff30cb0569d68dd905138ad0864 Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 17:25:21 +0530 Subject: [PATCH 0952/1250] Fix new task description --- src/libs/Navigation/linkingConfig.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index d6e0aeabf5d6..df0ee800ec38 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -337,6 +337,7 @@ const linkingConfig: LinkingOptions = { [SCREENS.NEW_TASK.TASK_SHARE_DESTINATION_SELECTOR]: ROUTES.NEW_TASK_SHARE_DESTINATION, [SCREENS.NEW_TASK.DETAILS]: ROUTES.NEW_TASK_DETAILS, [SCREENS.NEW_TASK.TITLE]: ROUTES.NEW_TASK_TITLE, + [SCREENS.NEW_TASK.DESCRIPTION]: ROUTES.NEW_TASK_DESCRIPTION, }, }, [SCREENS.RIGHT_MODAL.TEACHERS_UNITE]: { From 52db819f43e7e1ecc5eda1a574d9d52d7ae20849 Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 17:29:54 +0530 Subject: [PATCH 0953/1250] hide description menuItem for tasks --- src/pages/ReportDetailsPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index effc51a9a2ec..6db4cf4cf3b7 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -68,11 +68,12 @@ function ReportDetailsPage(props) { const shouldUseFullTitle = useMemo(() => ReportUtils.shouldUseFullTitleToDisplay(props.report), [props.report]); const isChatRoom = useMemo(() => ReportUtils.isChatRoom(props.report), [props.report]); const isThread = useMemo(() => ReportUtils.isChatThread(props.report), [props.report]); + const isTask = useMemo(() => ReportUtils.isTaskReport(props.report), [props.report]); const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(props.report), [props.report]); const canEditReportDescription = useMemo(() => ReportUtils.canEditReportDescription(props.report, policy), [props.report, policy]); - const shouldShowReportDescription = canEditReportDescription || !_.isEmpty(props.report.description); + const shouldShowReportDescription = !isTask && (canEditReportDescription || !_.isEmpty(props.report.description)); // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); From 652f54f83f7afcd22a53072b877623d12e0f4947 Mon Sep 17 00:00:00 2001 From: Conor Pendergrast Date: Thu, 1 Feb 2024 01:11:32 +1300 Subject: [PATCH 0954/1250] Update Preferences.md --- .../articles/new-expensify/account-settings/Preferences.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/articles/new-expensify/account-settings/Preferences.md b/docs/articles/new-expensify/account-settings/Preferences.md index a31be00a02bb..16d001d2b835 100644 --- a/docs/articles/new-expensify/account-settings/Preferences.md +++ b/docs/articles/new-expensify/account-settings/Preferences.md @@ -3,7 +3,8 @@ title: Preferences description: How to manage your Expensify Preferences --- # Overview -Your Preferences in Expensify allow you to customise how you interact with New Expensify. +Your Preferences in Expensify allow you to customise how you use New Expensify. + - Set your theme preference # How to set your theme preference in New Expensify @@ -15,6 +16,6 @@ To set or update your theme preference in New Expensify: _Use Device Settings_ is the default setting. -Selecting _Use Device Settings_ will use your device's theme settings. For example, if you have your device set to switch to light theme during daylight hours, and dark theme during after sunset, we'll match that. +Selecting _Use Device Settings_ will use your device's theme settings. For example, if your device is set to adjust the appearance from light to dark during the day, we'll match that. -Your theme preference will sync across all of the ways you use New Expensify (mobile app, web app or OSX desktop app). +Your theme preference will sync across all your New Expensify apps (mobile, web, or OSX desktop apps). From 0dffd611153236052abf098521b8bc8c0c344f98 Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 17:47:18 +0530 Subject: [PATCH 0955/1250] rename AdditionalComponent -> renderAdditionalText --- .../DisplayNames/DisplayNamesWithTooltip.tsx | 4 ++-- .../DisplayNamesWithoutTooltip.tsx | 8 +++---- src/components/DisplayNames/index.tsx | 6 ++--- src/components/DisplayNames/types.ts | 4 ++-- src/pages/home/HeaderView.js | 22 +++++++++++-------- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx index 77396b4d8999..6efb256123a4 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx @@ -11,7 +11,7 @@ import type DisplayNamesProps from './types'; type HTMLElementWithText = HTMLElement & RNText; -function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWithTooltips, textStyles = [], numberOfLines = 1, AdditionalComponent}: DisplayNamesProps) { +function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWithTooltips, textStyles = [], numberOfLines = 1, renderAdditionalText}: DisplayNamesProps) { const styles = useThemeStyles(); const containerRef = useRef(null); const childRefs = useRef([]); @@ -72,7 +72,7 @@ function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWit {index < displayNamesWithTooltips.length - 1 && } ))} - {AdditionalComponent && } + {renderAdditionalText?.()} {Boolean(isEllipsisActive) && ( diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx index e3a2dc503666..a636df025523 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx @@ -14,11 +14,11 @@ type DisplayNamesWithoutTooltipProps = { /** Number of lines before wrapping */ numberOfLines?: number; - /** Additional component to render after the text */ - AdditionalComponent?: ComponentType; + /** Additional Text component to render after the displayNames */ + renderAdditionalText?: () => React.ReactNode; }; -function DisplayNamesWithoutTooltip({textStyles = [], numberOfLines = 1, fullTitle = '', AdditionalComponent}: DisplayNamesWithoutTooltipProps) { +function DisplayNamesWithoutTooltip({textStyles = [], numberOfLines = 1, fullTitle = '', renderAdditionalText}: DisplayNamesWithoutTooltipProps) { const styles = useThemeStyles(); return ( {fullTitle} - {AdditionalComponent && } + {renderAdditionalText?.()} ); } diff --git a/src/components/DisplayNames/index.tsx b/src/components/DisplayNames/index.tsx index 73439ea02396..e85e068976f9 100644 --- a/src/components/DisplayNames/index.tsx +++ b/src/components/DisplayNames/index.tsx @@ -4,7 +4,7 @@ import DisplayNamesWithoutTooltip from './DisplayNamesWithoutTooltip'; import DisplayNamesWithToolTip from './DisplayNamesWithTooltip'; import type DisplayNamesProps from './types'; -function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, shouldUseFullTitle, displayNamesWithTooltips, AdditionalComponent}: DisplayNamesProps) { +function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, shouldUseFullTitle, displayNamesWithTooltips, renderAdditionalText}: DisplayNamesProps) { const {translate} = useLocalize(); const title = fullTitle || translate('common.hidden'); @@ -14,7 +14,7 @@ function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, sho textStyles={textStyles} numberOfLines={numberOfLines} fullTitle={title} - AdditionalComponent={AdditionalComponent} + renderAdditionalText={renderAdditionalText} /> ); } @@ -26,7 +26,7 @@ function DisplayNames({fullTitle, tooltipEnabled, textStyles, numberOfLines, sho displayNamesWithTooltips={displayNamesWithTooltips} textStyles={textStyles} numberOfLines={numberOfLines} - AdditionalComponent={AdditionalComponent} + renderAdditionalText={renderAdditionalText} /> ); } diff --git a/src/components/DisplayNames/types.ts b/src/components/DisplayNames/types.ts index 26bd263bdbd8..69be2f01a530 100644 --- a/src/components/DisplayNames/types.ts +++ b/src/components/DisplayNames/types.ts @@ -41,8 +41,8 @@ type DisplayNamesProps = { /** If the full title needs to be displayed */ shouldUseFullTitle?: boolean; - /** Additional component to display after the display names */ - AdditionalComponent?: ComponentType; + /** Additional Text component to render after the displayNames */ + renderAdditionalText?: () => React.ReactNode; }; export default DisplayNamesProps; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index bc7ec0abc19d..56839abb3e14 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -187,6 +187,18 @@ function HeaderView(props) { /> ); + const renderAdditionalText = () => { + if (_.isEmpty(policyName) || _.isEmpty(reportDescription)) { + return null; + } + return ( + <> + {translate('threads.in')} + {policyName} + + ); + }; + threeDotMenuItems.push(HeaderUtils.getPinMenuItem(props.report)); if (isConcierge && props.guideCalendarLink) { @@ -285,15 +297,7 @@ function HeaderView(props) { numberOfLines={1} textStyles={[styles.headerText, styles.pre]} shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isChatThread || isTaskReport} - AdditionalComponent={() => - !_.isEmpty(policyName) && - !_.isEmpty(reportDescription) && ( - <> - {translate('threads.in')} - {policyName} - - ) - } + renderAdditionalText={renderAdditionalText} /> {!_.isEmpty(parentNavigationSubtitleData) && ( Date: Wed, 31 Jan 2024 17:48:37 +0530 Subject: [PATCH 0956/1250] Fix lint --- src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx | 1 - src/components/DisplayNames/types.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx index a636df025523..c66d1698bbd6 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import type {ComponentType} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import useThemeStyles from '@hooks/useThemeStyles'; diff --git a/src/components/DisplayNames/types.ts b/src/components/DisplayNames/types.ts index 69be2f01a530..d76c8b671975 100644 --- a/src/components/DisplayNames/types.ts +++ b/src/components/DisplayNames/types.ts @@ -1,4 +1,3 @@ -import type {ComponentType} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import type {AvatarSource} from '@libs/UserUtils'; From 8ed596858c400051ab3e3d2899141f3082416ed0 Mon Sep 17 00:00:00 2001 From: caitlinwhite1 Date: Wed, 31 Jan 2024 06:19:49 -0600 Subject: [PATCH 0957/1250] Update redirects.csv URL was missing for global reimbursmenets --- docs/redirects.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/redirects.csv b/docs/redirects.csv index 4881e3f50a31..1f90b1565771 100644 --- a/docs/redirects.csv +++ b/docs/redirects.csv @@ -30,7 +30,7 @@ https://community.expensify.com/discussion/5124/how-to-add-your-name-and-photo-t https://community.expensify.com/discussion/5149/how-to-manage-your-devices-in-expensify,https://help.expensify.com/articles/expensify-classic/account-settings/Account-Details https://community.expensify.com/discussion/4432/how-to-add-a-secondary-login,https://help.expensify.com/articles/expensify-classic/account-settings/Account-Details https://community.expensify.com/discussion/6794/how-to-change-your-email-in-expensify,https://help.expensify.com/articles/expensify-classic/account-settings/Account-Details -Reimbursements,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/Global-Reimbursements +https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/International-Reimbursements,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/Global-Reimbursements https://community.expensify.com/discussion/4452/how-to-merge-accounts,https://help.expensify.com/articles/expensify-classic/account-settings/Merge-Accounts#gsc.tab=0 https://community.expensify.com/discussion/4783/how-to-add-or-remove-a-copilot#latest,https://help.expensify.com/articles/expensify-classic/account-settings/Copilot#gsc.tab=0 https://community.expensify.com/discussion/4343/expensify-anz-partnership-announcement,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-ANZ From c1f8ca634217bc9d012b5a1a34dda9c1edf56dc8 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Wed, 31 Jan 2024 13:33:23 +0100 Subject: [PATCH 0958/1250] Fix policyID={false} warning --- src/pages/iou/SplitBillDetailsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js index 94c2f1c31242..885d0487edcb 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -146,7 +146,7 @@ function SplitBillDetailsPage(props) { transaction={isEditingSplitBill ? props.draftTransaction || props.transaction : props.transaction} onConfirm={onConfirm} isPolicyExpenseChat={ReportUtils.isPolicyExpenseChat(props.report)} - policyID={ReportUtils.isPolicyExpenseChat(props.report) && props.report.policyID} + policyID={ReportUtils.isPolicyExpenseChat(props.report) ? props.report.policyID : null} /> )} From d4f9e1ec591b9ee4ab68bc9caf29643fcbb00eee Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 18:10:32 +0530 Subject: [PATCH 0959/1250] Fix header --- src/pages/home/HeaderView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 56839abb3e14..ab7021542f14 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -188,7 +188,7 @@ function HeaderView(props) { ); const renderAdditionalText = () => { - if (_.isEmpty(policyName) || _.isEmpty(reportDescription)) { + if (_.isEmpty(policyName) || _.isEmpty(reportDescription) || !_.isEmpty(parentNavigationSubtitleData)) { return null; } return ( From 2a7f09799678ba6f9d6f7b4b54011bfb64e8b856 Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 18:31:09 +0530 Subject: [PATCH 0960/1250] Make in lowercase --- src/languages/en.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 73be93af5a93..e8fdd4689b31 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1950,7 +1950,7 @@ export default { replies: 'Replies', reply: 'Reply', from: 'From', - in: 'In', + in: 'in', parentNavigationSummary: ({rootReportName, workspaceName}: ParentNavigationSummaryParams) => `From ${rootReportName}${workspaceName ? ` in ${workspaceName}` : ''}`, }, qrCodes: { From dd95e9b93d4594d25b1b38e0e164d94bd226a8b0 Mon Sep 17 00:00:00 2001 From: Github Date: Wed, 31 Jan 2024 13:39:50 +0100 Subject: [PATCH 0961/1250] Change navigation mock in perf tests for SignInPage --- tests/perf-test/SignInPage.perf-test.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/perf-test/SignInPage.perf-test.tsx b/tests/perf-test/SignInPage.perf-test.tsx index 80964c3c49cd..dde8596fb2ae 100644 --- a/tests/perf-test/SignInPage.perf-test.tsx +++ b/tests/perf-test/SignInPage.perf-test.tsx @@ -18,17 +18,6 @@ import * as TestHelper from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; -jest.mock('../../src/libs/Navigation/Navigation', () => { - const actualNav = jest.requireActual('../../src/libs/Navigation/Navigation'); - return { - ...actualNav, - navigationRef: { - addListener: () => jest.fn(), - removeListener: () => jest.fn(), - }, - } as typeof Navigation; -}); - const mockedNavigate = jest.fn(); jest.mock('@react-navigation/native', () => { const actualNav = jest.requireActual('@react-navigation/native'); @@ -43,7 +32,10 @@ jest.mock('@react-navigation/native', () => { navigate: jest.fn(), addListener: () => jest.fn(), }), - createNavigationContainerRef: jest.fn(), + createNavigationContainerRef: () => ({ + addListener: () => jest.fn(), + removeListener: () => jest.fn(), + }), } as typeof NativeNavigation; }); From c614c34627680953444272448282666fdaacd23d Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 19:05:46 +0530 Subject: [PATCH 0962/1250] Fixed 'in' bold issue --- src/components/MenuItem.tsx | 2 +- src/pages/home/HeaderView.js | 2 +- src/styles/index.ts | 4 ++++ src/styles/utils/FontUtils/index.ts | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index e53c7915da8a..23ce3017522a 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -378,7 +378,7 @@ function MenuItem( {(isHovered) => ( shouldBlockSelection && isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={ControlSelection.unblock} onSecondaryInteraction={onSecondaryInteraction} diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index ab7021542f14..90b12b9ff969 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -193,7 +193,7 @@ function HeaderView(props) { } return ( <> - {translate('threads.in')} + {translate('threads.in')} {policyName} ); diff --git a/src/styles/index.ts b/src/styles/index.ts index d52e876d2e8e..6bc80e847626 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -415,6 +415,10 @@ const styles = (theme: ThemeColors) => fontWeight: FontUtils.fontWeight.bold, }, + fontWeightNormal: { + fontWeight: FontUtils.fontWeight.normal, + }, + textHeadline: { ...headlineFont, ...whiteSpace.preWrap, diff --git a/src/styles/utils/FontUtils/index.ts b/src/styles/utils/FontUtils/index.ts index ac07fdbf026e..b93655fdf63d 100644 --- a/src/styles/utils/FontUtils/index.ts +++ b/src/styles/utils/FontUtils/index.ts @@ -11,6 +11,7 @@ const FontUtils = { }, fontWeight: { bold: fontWeightBold, + normal: '400', }, }; From 8e0b7dc6164fca0e42041ca283b6ffd855d9f14f Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 19:58:52 +0530 Subject: [PATCH 0963/1250] Change condition --- src/pages/ReportDetailsPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 6db4cf4cf3b7..4f880c29c2b4 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -68,12 +68,11 @@ function ReportDetailsPage(props) { const shouldUseFullTitle = useMemo(() => ReportUtils.shouldUseFullTitleToDisplay(props.report), [props.report]); const isChatRoom = useMemo(() => ReportUtils.isChatRoom(props.report), [props.report]); const isThread = useMemo(() => ReportUtils.isChatThread(props.report), [props.report]); - const isTask = useMemo(() => ReportUtils.isTaskReport(props.report), [props.report]); const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(props.report), [props.report]); const canEditReportDescription = useMemo(() => ReportUtils.canEditReportDescription(props.report, policy), [props.report, policy]); - const shouldShowReportDescription = !isTask && (canEditReportDescription || !_.isEmpty(props.report.description)); + const shouldShowReportDescription = isChatRoom && (canEditReportDescription || !_.isEmpty(props.report.description)); // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); From 7ce42f46f13c59ed13ae2a738d7ae7ab26ca0f1d Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 31 Jan 2024 15:29:38 +0100 Subject: [PATCH 0964/1250] Fix NewExpensify icon color --- assets/images/new-expensify.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/images/new-expensify.svg b/assets/images/new-expensify.svg index 264821d4f86e..89102ecbc5e4 100644 --- a/assets/images/new-expensify.svg +++ b/assets/images/new-expensify.svg @@ -1 +1 @@ - + From 75d6d88ef7d7bd392967984fe0644da3a1d9c6bd Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 31 Jan 2024 15:32:07 +0100 Subject: [PATCH 0965/1250] fix previous temporary fix --- src/Expensify.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index d9282229b749..dfb59a0f8848 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -23,8 +23,7 @@ import compose from './libs/compose'; import * as Growl from './libs/Growl'; import Log from './libs/Log'; import migrateOnyx from './libs/migrateOnyx'; -import getTopmostBottomTabRoute from './libs/Navigation/getTopmostBottomTabRoute'; -import Navigation, {navigationRef} from './libs/Navigation/Navigation'; +import Navigation from './libs/Navigation/Navigation'; import NavigationRoot from './libs/Navigation/NavigationRoot'; import NetworkConnection from './libs/NetworkConnection'; import PushNotification from './libs/Notification/PushNotification'; @@ -38,7 +37,6 @@ import Visibility from './libs/Visibility'; import ONYXKEYS from './ONYXKEYS'; import PopoverReportActionContextMenu from './pages/home/report/ContextMenu/PopoverReportActionContextMenu'; import * as ReportActionContextMenu from './pages/home/report/ContextMenu/ReportActionContextMenu'; -import SCREENS from './SCREENS'; Onyx.registerLogger(({level, message}) => { if (level === 'alert') { @@ -64,7 +62,8 @@ const propTypes = { /** Whether a new update is available and ready to install. */ updateAvailable: PropTypes.bool, - /** Tells us if the sidebar has rendered */ + /** Tells us if the sidebar has rendered - TODO: We don't use it as temporary solution to fix not hidding splashscreen */ + // eslint-disable-next-line react/no-unused-prop-types isSidebarLoaded: PropTypes.bool, /** Information about a screen share call requested by a GuidesPlus agent */ @@ -132,16 +131,7 @@ function Expensify(props) { [isSplashHidden], ); - // This is a temporary fix to handle more that one possible screen in the sidebar. - const isSidebarLoaded = useMemo(() => { - if (!isNavigationReady) { - return false; - } - - return getTopmostBottomTabRoute(navigationRef.getState()).name === SCREENS.HOME ? props.isSidebarLoaded : true; - }, [isNavigationReady, props.isSidebarLoaded]); - - const shouldInit = isNavigationReady && (!isAuthenticated || isSidebarLoaded) && hasAttemptedToOpenPublicRoom; + const shouldInit = isNavigationReady && hasAttemptedToOpenPublicRoom; const shouldHideSplash = shouldInit && !isSplashHidden; const initializeClient = () => { From 88d27a0e312ee856f54adb4d28a6acfbcbeaad6c Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 31 Jan 2024 17:32:20 +0300 Subject: [PATCH 0966/1250] Update docs/assets/js/main.js Co-authored-by: Nikki Wines --- docs/assets/js/main.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/assets/js/main.js b/docs/assets/js/main.js index 473bd544c7b8..6b3390148ff0 100644 --- a/docs/assets/js/main.js +++ b/docs/assets/js/main.js @@ -216,13 +216,11 @@ window.addEventListener('DOMContentLoaded', () => { const expensifyClassicContent = document.getElementById('expensify-classic'); const newExpensifyContent = document.getElementById('new-expensify'); - let contentSelector; + let contentSelector = '.article-toc-content'; if (expensifyClassicContent) { contentSelector = '#expensify-classic'; } else if (newExpensifyContent) { contentSelector = '#new-expensify'; - } else { - contentSelector = '.article-toc-content'; } if (window.tocbot) { From db2937ae9fd931949aaa5e8ddbecc6bb571bba43 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 31 Jan 2024 20:03:10 +0530 Subject: [PATCH 0967/1250] rm breaker --- docs/assets/js/platform-tabs.js | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/assets/js/platform-tabs.js b/docs/assets/js/platform-tabs.js index 18746efc5a84..e677e58b1e97 100644 --- a/docs/assets/js/platform-tabs.js +++ b/docs/assets/js/platform-tabs.js @@ -1,6 +1,5 @@ const expensifyClassicContent = document.getElementById('expensify-classic'); const newExpensifyContent = document.getElementById('new-expensify'); - const platformTabs = document.getElementById('platform-tabs'); if (expensifyClassicContent) { From 4241883500af7e601fae321df678af20323e513c Mon Sep 17 00:00:00 2001 From: Vit Horacek <36083550+mountiny@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:41:46 +0000 Subject: [PATCH 0968/1250] Update src/libs/actions/IOU.js Co-authored-by: Alex Beaman --- src/libs/actions/IOU.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 43db82dd3c22..3a67706627b7 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -651,7 +651,7 @@ function buildOnyxDataForMoneyRequest( * @param {Object} [policy] * @param {Object} [policyTags] * @param {Object} [policyCategories] - * @param {Number} [moneyRequestReportID] - If user requests money from a composer on some money request report, we always add a request to that specific report. + * @param {Number} [moneyRequestReportID] - If user requests money via the report composer on some money request report, we always add a request to that specific report. * @returns {Object} data * @returns {String} data.payerEmail * @returns {Object} data.iouReport From ecbfc7255c470c22aed915923de438c7748dc77b Mon Sep 17 00:00:00 2001 From: someone-here Date: Wed, 31 Jan 2024 20:49:13 +0530 Subject: [PATCH 0969/1250] Remove welcome message --- src/types/onyx/Form.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index c015627bfbc2..a8c837570142 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -33,7 +33,7 @@ type DisplayNameForm = Form<{ type NewRoomForm = Form<{ roomName?: string; - welcomeMessage?: string; + reportDescription?: string; policyID?: string; writeCapability?: string; visibility?: string; From 68070e8e3512ca50fe27fd5e6eb92757c8a02b04 Mon Sep 17 00:00:00 2001 From: Github Date: Wed, 31 Jan 2024 16:09:52 +0100 Subject: [PATCH 0970/1250] cr changes --- tests/perf-test/ReportScreen.perf-test.js | 2 +- tests/utils/collections/reportActions.ts | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/perf-test/ReportScreen.perf-test.js b/tests/perf-test/ReportScreen.perf-test.js index f5bc3f985227..dbc7775d066e 100644 --- a/tests/perf-test/ReportScreen.perf-test.js +++ b/tests/perf-test/ReportScreen.perf-test.js @@ -247,7 +247,7 @@ test('[ReportScreen] should press of the report item', () => { await act(triggerTransitionEnd); - // // Query for the report list + // Query for the report list await screen.findByTestId('report-actions-list'); const hintReportPreviewText = Localize.translateLocal('iou.viewDetails'); diff --git a/tests/utils/collections/reportActions.ts b/tests/utils/collections/reportActions.ts index 4efff8b3748a..bb14a2c7a41b 100644 --- a/tests/utils/collections/reportActions.ts +++ b/tests/utils/collections/reportActions.ts @@ -1,4 +1,5 @@ import {rand, randAggregation, randBoolean, randWord} from '@ngneat/falso'; +import {format} from 'date-fns'; import CONST from '@src/CONST'; import type {ReportAction} from '@src/types/onyx'; @@ -17,15 +18,11 @@ const flattenActionNamesValues = (actionNames: any) => { return result; }; -const addZero = (value: number): string | number => (value < 10 ? `0${value}` : value); - const getRandomDate = (): string => { const randomTimestamp = Math.random() * new Date().getTime(); const randomDate = new Date(randomTimestamp); - const formattedDate = `${randomDate.getFullYear()}-${addZero(randomDate.getMonth() + 1)}-${addZero(randomDate.getDate())} ${addZero(randomDate.getHours())}:${addZero( - randomDate.getMinutes(), - )}:${addZero(randomDate.getSeconds())}.${randomDate.getMilliseconds()}`; + const formattedDate = format(randomDate, CONST.DATE.FNS_DB_FORMAT_STRING); return formattedDate; }; From d336d58ef2a4a7f5ebfe2502b68415c06e06a694 Mon Sep 17 00:00:00 2001 From: Conor Pendergrast Date: Thu, 1 Feb 2024 04:22:06 +1300 Subject: [PATCH 0971/1250] Update docs/articles/new-expensify/account-settings/Preferences.md Co-authored-by: Amy Evans --- docs/articles/new-expensify/account-settings/Preferences.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/new-expensify/account-settings/Preferences.md b/docs/articles/new-expensify/account-settings/Preferences.md index 16d001d2b835..ae2efffc19b7 100644 --- a/docs/articles/new-expensify/account-settings/Preferences.md +++ b/docs/articles/new-expensify/account-settings/Preferences.md @@ -3,7 +3,7 @@ title: Preferences description: How to manage your Expensify Preferences --- # Overview -Your Preferences in Expensify allow you to customise how you use New Expensify. +Your Preferences in Expensify allow you to customize how you use New Expensify. - Set your theme preference From 9eb5967d2dc45823c69ab30f1f33e8d20fd8b77c Mon Sep 17 00:00:00 2001 From: Conor Pendergrast Date: Thu, 1 Feb 2024 04:23:15 +1300 Subject: [PATCH 0972/1250] Update Preferences.md --- docs/articles/new-expensify/account-settings/Preferences.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/articles/new-expensify/account-settings/Preferences.md b/docs/articles/new-expensify/account-settings/Preferences.md index ae2efffc19b7..5ec854e9bb81 100644 --- a/docs/articles/new-expensify/account-settings/Preferences.md +++ b/docs/articles/new-expensify/account-settings/Preferences.md @@ -14,8 +14,6 @@ To set or update your theme preference in New Expensify: 2. Tap on **Theme** 3. You can choose between the _Dark_ theme, the _Light_ theme, or _Use Device Settings_ (which is the default setting) -_Use Device Settings_ is the default setting. - Selecting _Use Device Settings_ will use your device's theme settings. For example, if your device is set to adjust the appearance from light to dark during the day, we'll match that. Your theme preference will sync across all your New Expensify apps (mobile, web, or OSX desktop apps). From c122efbb35301b737bdcdc6020f09a37ad34149b Mon Sep 17 00:00:00 2001 From: Conor Pendergrast Date: Thu, 1 Feb 2024 04:24:13 +1300 Subject: [PATCH 0973/1250] Update Preferences.md --- docs/articles/new-expensify/account-settings/Preferences.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/articles/new-expensify/account-settings/Preferences.md b/docs/articles/new-expensify/account-settings/Preferences.md index 5ec854e9bb81..b94c9d35c1a1 100644 --- a/docs/articles/new-expensify/account-settings/Preferences.md +++ b/docs/articles/new-expensify/account-settings/Preferences.md @@ -12,7 +12,9 @@ Your Preferences in Expensify allow you to customize how you use New Expensify. To set or update your theme preference in New Expensify: 1. Go to **Settings > Preferences** 2. Tap on **Theme** -3. You can choose between the _Dark_ theme, the _Light_ theme, or _Use Device Settings_ (which is the default setting) +3. You can choose between the _Dark_ theme, the _Light_ theme, or _Use Device Settings_ + +_Use Device Settings_ is the default setting. Selecting _Use Device Settings_ will use your device's theme settings. For example, if your device is set to adjust the appearance from light to dark during the day, we'll match that. From ed3b10b4fe00ee96e2bedc4c54831a63dd5b7aaa Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:50:56 +0100 Subject: [PATCH 0974/1250] Fix padding android --- src/styles/theme/themes/dark.ts | 4 ++++ src/styles/theme/themes/light.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index 4ab481d34e9b..0417f4a7920b 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -91,6 +91,10 @@ const darkTheme = { // The route urls from ROUTES.ts are only used for deep linking and configuring URLs on web. // The screen name (see SCREENS.ts) is the name of the screen as far as react-navigation is concerned, and the linkingConfig maps screen names to URLs PAGE_THEMES: { + [SCREENS.HOME]: { + backgroundColor: colors.productDark200, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, [SCREENS.REPORT]: { backgroundColor: colors.productDark100, statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index 643fdfa19460..7632562c47d7 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -91,6 +91,10 @@ const lightTheme = { // The route urls from ROUTES.ts are only used for deep linking and configuring URLs on web. // The screen name (see SCREENS.ts) is the name of the screen as far as react-navigation is concerned, and the linkingConfig maps screen names to URLs PAGE_THEMES: { + [SCREENS.HOME]: { + backgroundColor: colors.productLight200, + statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, + }, [SCREENS.REPORT]: { backgroundColor: colors.productLight100, statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, From 5b6cf79f404cb5d6a3677871622e62f7e0badd7a Mon Sep 17 00:00:00 2001 From: Antony Kithinzi Date: Wed, 31 Jan 2024 17:33:14 +0100 Subject: [PATCH 0975/1250] prettier --- src/components/ReportActionItem/ReportPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 712dd6e56760..d9146c25edcf 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -229,7 +229,7 @@ function ReportPreview({ }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; return ( - From 3a2108a4e9fda4ea682f36a58650ae20fd2b12d3 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Wed, 31 Jan 2024 17:35:34 +0100 Subject: [PATCH 0976/1250] Add hovered effect on the workspace switcher button (210) --- src/components/SubscriptAvatar.tsx | 5 ++++- src/components/WorkspaceSwitcherButton.tsx | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/SubscriptAvatar.tsx b/src/components/SubscriptAvatar.tsx index 71b63cb07ca1..c2de41d8b4db 100644 --- a/src/components/SubscriptAvatar.tsx +++ b/src/components/SubscriptAvatar.tsx @@ -20,6 +20,9 @@ type SubIcon = { /** Height of the icon */ height?: number; + + /** The fill color for the icon. Can be hex, rgb, rgba, or valid react-native named color such as 'red' or 'blue'. */ + fill?: string; }; type SubscriptAvatarProps = { @@ -120,7 +123,7 @@ function SubscriptAvatar({mainAvatar, secondaryAvatar, subscriptIcon, size = CON width={subscriptIcon.width} height={subscriptIcon.height} additionalStyles={styles.alignSelfCenter} - fill={theme.icon} + fill={subscriptIcon.fill ?? theme.icon} /> )} diff --git a/src/components/WorkspaceSwitcherButton.tsx b/src/components/WorkspaceSwitcherButton.tsx index b76503c0c6e9..b7485fbab7a8 100644 --- a/src/components/WorkspaceSwitcherButton.tsx +++ b/src/components/WorkspaceSwitcherButton.tsx @@ -2,6 +2,7 @@ import React, {useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import Navigation from '@libs/Navigation/Navigation'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; @@ -21,6 +22,7 @@ type WorkspaceSwitcherButtonProps = {activeWorkspaceID?: string} & WorkspaceSwit function WorkspaceSwitcherButton({activeWorkspaceID, policy}: WorkspaceSwitcherButtonProps) { const {translate} = useLocalize(); + const theme = useTheme(); const {source, name, type} = useMemo(() => { if (!activeWorkspaceID) { @@ -46,12 +48,19 @@ function WorkspaceSwitcherButton({activeWorkspaceID, policy}: WorkspaceSwitcherB }) } > - + {({hovered}) => ( + + )} ); } From 5595560e1b3faf78f9bec4498308e4b47d9565e8 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 31 Jan 2024 09:39:20 -0700 Subject: [PATCH 0977/1250] show card for corporate cards --- src/components/ReportActionItem/MoneyRequestPreview.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 7b38dcd34109..13f75b9869a0 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -165,7 +165,7 @@ function MoneyRequestPreview(props) { const isScanning = hasReceipt && TransactionUtils.isReceiptBeingScanned(props.transaction); const hasFieldErrors = TransactionUtils.hasMissingSmartscanFields(props.transaction); const isDistanceRequest = TransactionUtils.isDistanceRequest(props.transaction); - const isExpensifyCardTransaction = TransactionUtils.isExpensifyCardTransaction(props.transaction); + const isCardTransaction = TransactionUtils.isCardTransaction(props.transaction); const isSettled = ReportUtils.isSettled(props.iouReport.reportID); const isDeleted = lodashGet(props.action, 'pendingAction', null) === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; @@ -184,7 +184,7 @@ function MoneyRequestPreview(props) { const receiptImages = hasReceipt ? [ReceiptUtils.getThumbnailAndImageURIs(props.transaction)] : []; const getSettledMessage = () => { - if (isExpensifyCardTransaction) { + if (isCardTransaction) { return translate('common.done'); } return translate('iou.settledExpensify'); @@ -207,7 +207,7 @@ function MoneyRequestPreview(props) { return translate('iou.split'); } - if (isExpensifyCardTransaction) { + if (isCardTransaction) { let message = translate('iou.card'); if (TransactionUtils.isPending(props.transaction)) { message += ` • ${translate('iou.pending')}`; From 7860696cba7d465c404b30a17e0b9e8772e8eb4f Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 31 Jan 2024 18:01:18 +0100 Subject: [PATCH 0978/1250] fix for goBack on ProfilePage and WorkspacesListPage --- src/pages/ProfilePage.js | 2 +- src/pages/workspace/WorkspacesListPage.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index bf99b3ef0a45..11e2afeb6e95 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -133,7 +133,7 @@ function ProfilePage(props) { const hasStatus = !!statusEmojiCode; const statusContent = `${statusEmojiCode} ${statusText}`; - const navigateBackTo = lodashGet(props.route, 'params.backTo', ROUTES.HOME); + const navigateBackTo = lodashGet(props.route, 'params.backTo'); const shouldShowNotificationPreference = !_.isEmpty(props.report) && props.report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; const notificationPreference = shouldShowNotificationPreference ? props.translate(`notificationPreferencesPage.notificationPreferences.${props.report.notificationPreference}`) : ''; diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 9442ffafa862..5b499fe7a3ee 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -322,7 +322,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r Navigation.goBack(ROUTES.ALL_SETTINGS)} + onBackButtonPress={() => Navigation.goBack()} >