From 9bc2e6537f76807063e06741913dfc6c7d5ccd31 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 18 May 2024 17:31:44 +0100 Subject: [PATCH 01/29] simplify navigateToMostRecentReport --- src/libs/ReportUtils.ts | 34 ++++++++++++++----- src/libs/actions/Report.ts | 57 +++++++++----------------------- src/pages/NewChatConfirmPage.tsx | 2 +- 3 files changed, 42 insertions(+), 51 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b8e4c448fdc2..b46537e81142 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1079,7 +1079,7 @@ function doesReportBelongToWorkspace(report: OnyxEntry, policyMemberAcco /** * Given an array of reports, return them filtered by a policyID and policyMemberAccountIDs. */ -function filterReportsByPolicyIDAndMemberAccountIDs(reports: Report[], policyMemberAccountIDs: number[] = [], policyID?: string) { +function filterReportsByPolicyIDAndMemberAccountIDs(reports: Array>, policyMemberAccountIDs: number[] = [], policyID?: string) { return reports.filter((report) => !!report && doesReportBelongToWorkspace(report, policyMemberAccountIDs, policyID)); } @@ -1165,6 +1165,7 @@ function findLastAccessedReport( reportMetadata: OnyxCollection = {}, policyID?: string, policyMemberAccountIDs: number[] = [], + excludeReportID?: string, ): OnyxEntry { // If it's the user's first time using New Expensify, then they could either have: // - just a Concierge report, if so we'll return that @@ -1172,7 +1173,7 @@ function findLastAccessedReport( // If it's the latter, we'll use the deeplinked report over the Concierge report, // since the Concierge report would be incorrectly selected over the deep-linked report in the logic below. - let reportsValues = Object.values(reports ?? {}) as Report[]; + let reportsValues = Object.values(reports ?? {}); if (!!policyID || policyMemberAccountIDs.length > 0) { reportsValues = filterReportsByPolicyIDAndMemberAccountIDs(reportsValues, policyMemberAccountIDs, policyID); @@ -1188,13 +1189,28 @@ function findLastAccessedReport( }); } - if (ignoreDomainRooms) { - // We allow public announce rooms, admins, and announce rooms through since we bypass the default rooms beta for them. - // Check where ReportUtils.findLastAccessedReport is called in MainDrawerNavigator.js for more context. - // Domain rooms are now the only type of default room that are on the defaultRooms beta. - sortedReports = sortedReports.filter( - (report) => !isDomainRoom(report) || getPolicyType(report, policies) === CONST.POLICY.TYPE.FREE || hasExpensifyGuidesEmails(Object.keys(report?.participants ?? {}).map(Number)), - ); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const shouldFilter = excludeReportID || ignoreDomainRooms; + if (shouldFilter) { + sortedReports = sortedReports.filter((report) => { + if (excludeReportID && report?.reportID === excludeReportID) { + return false; + } + + // We allow public announce rooms, admins, and announce rooms through since we bypass the default rooms beta for them. + // Check where ReportUtils.findLastAccessedReport is called in MainDrawerNavigator.js for more context. + // Domain rooms are now the only type of default room that are on the defaultRooms beta. + if ( + ignoreDomainRooms && + isDomainRoom(report) && + getPolicyType(report, policies) !== CONST.POLICY.TYPE.FREE && + !hasExpensifyGuidesEmails(Object.keys(report?.participants ?? {}).map(Number)) + ) { + return false; + } + + return true; + }); } if (isFirstTimeNewExpensifyUser) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 91a0ce5da930..6f6f8cfe1944 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -946,6 +946,7 @@ function openReport( function navigateToAndOpenReport( userLogins: string[], shouldDismissModal = true, + actionType?: string, reportName?: string, avatarUri?: string, avatarFile?: File | CustomRNImageManipulatorResult | undefined, @@ -977,7 +978,7 @@ function navigateToAndOpenReport( Navigation.dismissModalWithReport(report); } else { Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME}); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID)); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID), actionType); } } @@ -1972,7 +1973,7 @@ function updateWriteCapabilityAndNavigate(report: Report, newValue: WriteCapabil /** * Navigates to the 1:1 report with Concierge */ -function navigateToConciergeChat(shouldDismissModal = false, checkIfCurrentPageActive = () => true) { +function navigateToConciergeChat(shouldDismissModal = false, checkIfCurrentPageActive = () => true, actionType?: string) { // If conciergeChatReportID contains a concierge report ID, we navigate to the concierge chat using the stored report ID. // Otherwise, we would find the concierge chat and navigate to it. if (!conciergeChatReportID) { @@ -1983,12 +1984,12 @@ function navigateToConciergeChat(shouldDismissModal = false, checkIfCurrentPageA if (!checkIfCurrentPageActive()) { return; } - navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], shouldDismissModal); + navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], shouldDismissModal, actionType); }); } else if (shouldDismissModal) { Navigation.dismissModal(conciergeChatReportID); } else { - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeChatReportID)); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeChatReportID), actionType); } } @@ -2507,46 +2508,20 @@ function getCurrentUserAccountID(): number { } function navigateToMostRecentReport(currentReport: OnyxEntry) { - const reportID = currentReport?.reportID; - const sortedReportsByLastRead = ReportUtils.sortReportsByLastRead(Object.values(allReports ?? {}) as Report[], reportMetadata); - - // We want to filter out the current report, hidden reports and empty chats - const filteredReportsByLastRead = sortedReportsByLastRead.filter( - (sortedReport) => - sortedReport?.reportID !== reportID && - sortedReport?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN && - ReportUtils.shouldReportBeInOptionList({ - report: sortedReport, - currentReportId: '', - isInFocusMode: false, - betas: [], - policies: {}, - excludeEmptyChats: true, - doesReportHaveViolations: false, - includeSelfDM: true, - }), - ); - const lastAccessedReportID = filteredReportsByLastRead.at(-1)?.reportID; + const lastAccessedReportID = ReportUtils.findLastAccessedReport(allReports, false, null, false, false, reportMetadata, undefined, [], currentReport?.reportID)?.reportID; + const isChatThread = ReportUtils.isChatThread(currentReport); + // We are using index 1 here because on index 0, we'll always have the bottomTabNavigator. + const isFirstRoute = navigationRef?.current?.getState()?.index === 1; + // If it is not a chat thread we should call Navigation.goBack to pop the current route first before navigating to last accessed report. + if (!isChatThread && !isFirstRoute) { + Navigation.goBack(); + } + if (lastAccessedReportID) { - const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID ?? ''); - // We are using index 1 here because on index 0, we'll always have the bottomTabNavigator. - const isFirstRoute = navigationRef?.current?.getState()?.index === 1; - // If it is not a chat thread we should call Navigation.goBack to pop the current route first before navigating to last accessed report. - if (!isChatThread && !isFirstRoute) { - Navigation.goBack(); - } - Navigation.navigate(lastAccessedReportRoute, CONST.NAVIGATION.TYPE.FORCED_UP); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID), CONST.NAVIGATION.TYPE.FORCED_UP); } else { - const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]); - const chat = ReportUtils.getChatByParticipants([...participantAccountIDs, currentUserAccountID]); - if (chat?.reportID) { - // If it is not a chat thread we should call Navigation.goBack to pop the current route first before navigating to Concierge. - if (!isChatThread) { - Navigation.goBack(); - } - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat?.reportID), CONST.NAVIGATION.TYPE.FORCED_UP); - } + navigateToConciergeChat(false, () => true, CONST.NAVIGATION.TYPE.FORCED_UP); } } diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index 82f67eb40c86..0b2cdf3f5929 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -93,7 +93,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP } const logins: string[] = (newGroupDraft.participants ?? []).map((participant) => participant.login); - Report.navigateToAndOpenReport(logins, true, newGroupDraft.reportName ?? '', newGroupDraft.avatarUri ?? '', fileRef.current, optimisticReportID.current, true); + Report.navigateToAndOpenReport(logins, true, undefined, newGroupDraft.reportName ?? '', newGroupDraft.avatarUri ?? '', fileRef.current, optimisticReportID.current, true); }; const navigateBack = () => { From b16d864a8d1bc1f458c188acebff8ef0f2b09977 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Tue, 18 Jun 2024 23:05:27 +0700 Subject: [PATCH 02/29] mark GRB on task at highest --- src/libs/ReportUtils.ts | 12 +++- src/libs/SidebarUtils.ts | 4 ++ src/libs/actions/Report.ts | 2 + src/libs/actions/Task.ts | 110 ++++++++++++++++++++++++++++++++++++- src/types/onyx/Report.ts | 3 + 5 files changed, 128 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a02b24c20e35..9af1c13bf5e0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -335,6 +335,7 @@ type OptimisticTaskReport = Pick< | 'notificationPreference' | 'parentReportActionID' | 'lastVisibleActionCreated' + | 'hasParentAccess' >; type TransactionDetails = { @@ -2267,7 +2268,15 @@ function getLastVisibleMessage(reportID: string | undefined, actionsToMerge: Rep * @param [parentReportAction] - The parent report action of the report (Used to check if the task has been canceled) */ function isWaitingForAssigneeToCompleteTask(report: OnyxEntry, parentReportAction: OnyxEntry | EmptyObject = {}): boolean { - return isTaskReport(report) && isReportManager(report) && isOpenTaskReport(report, parentReportAction); + if (report?.hasOutstandingChildTask) { + return true; + } + + if (isOpenTaskReport(report, parentReportAction) && !report?.hasParentAccess && isReportManager(report)) { + return true; + } + + return false; } function isUnreadWithMention(reportOrOption: OnyxEntry | OptionData): boolean { @@ -5056,6 +5065,7 @@ function buildOptimisticTaskReport( statusNum: CONST.REPORT.STATUS_NUM.OPEN, notificationPreference, lastVisibleActionCreated: DateUtils.getDBTime(), + hasParentAccess: true, }; } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index c96d3c511320..06daa2d34e01 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -246,6 +246,8 @@ function getOptionData({ searchText: undefined, isPinned: false, hasOutstandingChildRequest: false, + hasOutstandingChildTask: false, + hasParentAccess: undefined, isIOUReportOwner: null, isChatRoom: false, isArchivedRoom: false, @@ -307,6 +309,8 @@ function getOptionData({ result.isSelfDM = ReportUtils.isSelfDM(report); result.isOneOnOneChat = isOneOnOneChat; result.tooltipText = ReportUtils.getReportParticipantsTitle(visibleParticipantAccountIDs); + result.hasOutstandingChildTask = report.hasOutstandingChildTask; + result.hasParentAccess = report.hasParentAccess; const hasMultipleParticipants = participantPersonalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat || ReportUtils.isExpenseReport(report); const subtitle = ReportUtils.getChatRoomSubtitle(report); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 192c6f720b5f..cfa058eb4c64 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3238,6 +3238,7 @@ function completeOnboarding( managerID: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, isOptimisticReport: true, + managerID: currentUserAccountID, }, }, { @@ -3264,6 +3265,7 @@ function completeOnboarding( value: { stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED, + managerID: currentUserAccountID, }, }); } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 51fcbb53bc5f..2efb27d0d3f9 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -133,12 +133,14 @@ function createTaskAndNavigate( const currentTime = DateUtils.getDBTimeWithSkew(); const lastCommentText = ReportUtils.formatReportLastMessageText(optimisticAddCommentReport?.reportAction?.message?.[0]?.text ?? ''); + const parentReport = ReportUtils.getReport(parentReportID); const optimisticParentReport = { lastVisibleActionCreated: optimisticAddCommentReport.reportAction.created, lastMessageText: lastCommentText, lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, lastMessageTranslationKey: '', + hasOutstandingChildTask: assigneeAccountID === currentUserAccountID ? true : parentReport?.hasOutstandingChildTask, }; // We're only setting onyx data for the task report here because it's possible for the parent report to not exist yet (if you're assigning a task to someone you haven't chatted with before) @@ -274,7 +276,14 @@ function createTaskAndNavigate( }, }); - clearOutTaskInfo(); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`, + value: { + hasOutstandingChildTask: parentReport?.hasOutstandingChildTask, + }, + }), + clearOutTaskInfo(); const parameters: CreateTaskParams = { parentReportActionID: optimisticAddCommentReport.reportAction.reportActionID, @@ -296,6 +305,27 @@ function createTaskAndNavigate( Report.notifyNewAction(parentReportID, currentUserAccountID); } +/** + * @returns the object to update `report.hasOutstandingChildTask` + */ +function getOutstandingChildTask(taskReport: OnyxEntry) { + const parentReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReport?.parentReportID}`] ?? {}; + return Object.values(parentReportActions).some((reportAction) => { + if (reportAction.childReportID === taskReport?.reportID) { + return false; + } + + if ( + reportAction.childType === CONST.REPORT.TYPE.TASK && + reportAction?.childStateNum === CONST.REPORT.STATE_NUM.OPEN && + reportAction?.childStatusNum === CONST.REPORT.STATUS_NUM.OPEN && + !reportAction?.message?.[0]?.isDeletedParentAction + ) { + return true; + } + }); +} + /** * Complete a task */ @@ -303,7 +333,7 @@ function completeTask(taskReport: OnyxEntry) { const taskReportID = taskReport?.reportID ?? '-1'; const message = `marked as complete`; const completedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, message); - + const parentReport = getParentReport(taskReport); const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -353,6 +383,24 @@ function completeTask(taskReport: OnyxEntry) { }, ]; + if (parentReport?.hasOutstandingChildTask) { + const hasOutstandingChildTask = getOutstandingChildTask(taskReport); + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${taskReport?.parentReportID}`, + value: { + hasOutstandingChildTask, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${taskReport?.parentReportID}`, + value: { + hasOutstandingChildTask: parentReport?.hasOutstandingChildTask, + }, + }); + } + const parameters: CompleteTaskParams = { taskReportID, completedTaskReportActionID: completedTaskReportAction.reportActionID, @@ -370,6 +418,8 @@ function reopenTask(taskReport: OnyxEntry) { const taskReportID = taskReport?.reportID ?? '-1'; const message = `marked as incomplete`; const reopenedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED, message); + const parentReport = getParentReport(taskReport); + const hasOutstandingChildTask = taskReport?.managerID === currentUserAccountID ? true : parentReport?.hasOutstandingChildTask; const optimisticData: OnyxUpdate[] = [ { @@ -384,6 +434,13 @@ function reopenTask(taskReport: OnyxEntry) { lastReadTime: reopenedTaskReportAction.created, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${taskReport?.parentReportID}`, + value: { + hasOutstandingChildTask, + }, + }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReportID}`, @@ -411,6 +468,13 @@ function reopenTask(taskReport: OnyxEntry) { statusNum: CONST.REPORT.STATUS_NUM.APPROVED, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${taskReport?.parentReportID}`, + value: { + hasOutstandingChildTask: taskReport?.hasOutstandingChildTask, + }, + }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReportID}`, @@ -566,6 +630,39 @@ function editTaskAssignee(report: OnyxTypes.Report, ownerAccountID: number, assi }, ]; + if (currentUserAccountID === assigneeAccountID) { + const parentReport = getParentReport(report); + if (!isEmptyObject(parentReport)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, + value: {hasOutstandingChildTask: true}, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, + value: {hasOutstandingChildTask: parentReport?.hasOutstandingChildTask}, + }); + } + } + + if (report.managerID === currentUserAccountID) { + const hasOutstandingChildTask = getOutstandingChildTask(report); + const parentReport = getParentReport(report); + if (!isEmptyObject(parentReport)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, + value: {hasOutstandingChildTask}, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, + value: {hasOutstandingChildTask: parentReport?.hasOutstandingChildTask}, + }); + } + } + // If we make a change to the assignee, we want to add a comment to the assignee's chat // Check if the assignee actually changed if (assigneeAccountID && assigneeAccountID !== report.managerID && assigneeAccountID !== ownerAccountID && assigneeChatReport) { @@ -849,6 +946,7 @@ function deleteTask(report: OnyxEntry) { const optimisticReportActions = { [parentReportAction.reportActionID]: optimisticReportAction, }; + const hasOutstandingChildTask = getOutstandingChildTask(report); const optimisticData: OnyxUpdate[] = [ { @@ -867,6 +965,7 @@ function deleteTask(report: OnyxEntry) { value: { lastMessageText: ReportActionsUtils.getLastVisibleMessage(parentReport?.reportID ?? '-1', optimisticReportActions as OnyxTypes.ReportActions).lastMessageText ?? '', lastVisibleActionCreated: ReportActionsUtils.getLastVisibleAction(parentReport?.reportID ?? '-1', optimisticReportActions as OnyxTypes.ReportActions)?.created, + hasOutstandingChildTask, }, }, { @@ -929,6 +1028,13 @@ function deleteTask(report: OnyxEntry) { statusNum: report.statusNum ?? '', } as OnyxTypes.Report, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport?.reportID}`, + value: { + hasOutstandingChildTask: parentReport?.hasOutstandingChildTask, + }, + }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 9b0b35a6da7c..b5d0a0a15d13 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -77,6 +77,9 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< /** Whether the report has a child that is an outstanding expense that is awaiting action from the current user */ hasOutstandingChildRequest?: boolean; + /** Whether the report has a child task that is awaiting action from the current user */ + hasOutstandingChildTask?: boolean; + /** List of icons for report participants */ icons?: OnyxCommon.Icon[]; From 6fdaf2dcd258b43567065c4e46be9a670d76edef Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Tue, 18 Jun 2024 23:21:24 +0700 Subject: [PATCH 03/29] address some lint errors --- src/libs/actions/Task.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 2efb27d0d3f9..6a7a34bf7ec1 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -275,15 +275,15 @@ function createTaskAndNavigate( }, }, }); - failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`, value: { hasOutstandingChildTask: parentReport?.hasOutstandingChildTask, }, - }), - clearOutTaskInfo(); + }); + + clearOutTaskInfo(); const parameters: CreateTaskParams = { parentReportActionID: optimisticAddCommentReport.reportAction.reportActionID, @@ -323,6 +323,8 @@ function getOutstandingChildTask(taskReport: OnyxEntry) { ) { return true; } + + return false; }); } From 2aacaf36872261c413e37bfbf256a66653b51e5f Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Wed, 19 Jun 2024 00:04:10 +0700 Subject: [PATCH 04/29] force type to compare correctly --- src/libs/actions/Task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 6a7a34bf7ec1..35521945218a 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -311,7 +311,7 @@ function createTaskAndNavigate( function getOutstandingChildTask(taskReport: OnyxEntry) { const parentReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReport?.parentReportID}`] ?? {}; return Object.values(parentReportActions).some((reportAction) => { - if (reportAction.childReportID === taskReport?.reportID) { + if (String(reportAction.childReportID) === String(taskReport?.reportID)) { return false; } From 142a46a03c99e5225e5aef6e9d3647099336825b Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Fri, 21 Jun 2024 14:48:09 +0700 Subject: [PATCH 05/29] solve lint error after merging main --- src/libs/actions/Task.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index d4280eb07109..d2635e483d9d 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -133,7 +133,7 @@ function createTaskAndNavigate( const currentTime = DateUtils.getDBTimeWithSkew(); const lastCommentText = ReportUtils.formatReportLastMessageText(optimisticAddCommentReport?.reportAction?.message?.[0]?.text ?? ''); - const parentReport = ReportUtils.getReport(parentReportID); + const parentReport = getReport(parentReportID); const optimisticParentReport = { lastVisibleActionCreated: optimisticAddCommentReport.reportAction.created, lastMessageText: lastCommentText, @@ -912,6 +912,13 @@ function getParentReport(report: OnyxEntry | EmptyObject): Ony return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? {}; } +/** + * Returns the report + */ +function getReport(reportID: string): OnyxEntry | EmptyObject { + return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? {}; +} + /** * Cancels a task by setting the report state to SUBMITTED and status to CLOSED */ From f56f2e13c38963db06fbd27f64390c3a62be37aa Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Fri, 21 Jun 2024 15:07:49 +0700 Subject: [PATCH 06/29] chore --- src/libs/actions/Report.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7edb0a6259a3..d842b1caca6b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3225,6 +3225,8 @@ function completeOnboarding( description: taskDescription ?? '', })); + const hasOutstandingChildTask = tasksData.some((task) => !task.completedTaskReportAction); + const tasksForOptimisticData = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, taskDescription, completedTaskReportAction}) => { acc.push( { @@ -3359,6 +3361,7 @@ function completeOnboarding( key: `${ONYXKEYS.COLLECTION.REPORT}${targetChatReportID}`, value: { lastMentionedTime: DateUtils.getDBTime(), + hasOutstandingChildTask, }, }, { @@ -3390,6 +3393,7 @@ function completeOnboarding( lastMessageTranslationKey: '', lastMessageText: '', lastVisibleActionCreated: '', + hasOutstandingChildTask: false, }; const {lastMessageText = '', lastMessageTranslationKey = ''} = ReportActionsUtils.getLastVisibleMessage(targetChatReportID); if (lastMessageText || lastMessageTranslationKey) { From bdbd9c3e824c2f050b3c5d43eebb445d53a0dd77 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Sat, 22 Jun 2024 05:06:36 +0700 Subject: [PATCH 07/29] update after merging main --- src/libs/actions/Task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index cc2a6530cbb8..ebd00ee1f5e3 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -319,7 +319,7 @@ function getOutstandingChildTask(taskReport: OnyxEntry) { reportAction.childType === CONST.REPORT.TYPE.TASK && reportAction?.childStateNum === CONST.REPORT.STATE_NUM.OPEN && reportAction?.childStatusNum === CONST.REPORT.STATUS_NUM.OPEN && - !reportAction?.message?.[0]?.isDeletedParentAction + ReportActionsUtils.getReportActionMessage(reportAction)?.isDeletedParentAction ) { return true; } From 11cd7df7ba1f2fc23f6d0474856582a3117cd208 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:16:55 +0100 Subject: [PATCH 08/29] ts + lint --- src/libs/actions/Report.ts | 2 +- src/pages/NewChatConfirmPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b1d120962e4c..f0bb402da881 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2551,7 +2551,7 @@ function getCurrentUserAccountID(): number { } function navigateToMostRecentReport(currentReport: OnyxEntry) { - const lastAccessedReportID = ReportUtils.findLastAccessedReport(allReports, false, null, false, false, reportMetadata, undefined, [], currentReport?.reportID)?.reportID; + const lastAccessedReportID = ReportUtils.findLastAccessedReport(allReports, false, undefined, false, false, reportMetadata, undefined, [], currentReport?.reportID)?.reportID; if (lastAccessedReportID) { const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID ?? '-1'); diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx index be810ff146d7..17e5708803cd 100644 --- a/src/pages/NewChatConfirmPage.tsx +++ b/src/pages/NewChatConfirmPage.tsx @@ -106,7 +106,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP const logins: string[] = (newGroupDraft.participants ?? []).map((participant) => participant.login); Report.navigateToAndOpenReport(logins, true, undefined, newGroupDraft.reportName ?? '', newGroupDraft.avatarUri ?? '', avatarFile, optimisticReportID.current, true); - }, [newGroupDraft]); + }, [newGroupDraft, avatarFile]); const stashedLocalAvatarImage = newGroupDraft?.avatarUri; From 3a1c32904963e56a0f16e1c3abaa79f3a34ef503 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 25 Jun 2024 23:25:39 -0700 Subject: [PATCH 09/29] Bump react-native-screens --- package-lock.json | 8 ++++---- package.json | 2 +- ...t-native-screens+3.30.1+001+fix-screen-type.patch | 12 ------------ 3 files changed, 5 insertions(+), 17 deletions(-) delete mode 100644 patches/react-native-screens+3.30.1+001+fix-screen-type.patch diff --git a/package-lock.json b/package-lock.json index fb15d51d1389..ae060c611450 100644 --- a/package-lock.json +++ b/package-lock.json @@ -115,7 +115,7 @@ "react-native-release-profiler": "^0.1.6", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.8.2", - "react-native-screens": "3.30.1", + "react-native-screens": "3.32.0", "react-native-share": "^10.0.2", "react-native-sound": "^0.11.2", "react-native-svg": "14.1.0", @@ -37355,9 +37355,9 @@ } }, "node_modules/react-native-screens": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.30.1.tgz", - "integrity": "sha512-/muEvjocCtFb+j5J3YmLvB25+f4rIU8hnnxgGTkXcAf2omPBY8uhPjJaaFUlvj64VEoEzJcRpugbXWsjfPPIFg==", + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.32.0.tgz", + "integrity": "sha512-wybqZAHX7v8ipOXhh90CqGLkBHw5JYqKNRBX7R/b0c2WQisTOgu0M0yGwBMM6LyXRBT+4k3NTGHdDbpJVpq0yQ==", "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" diff --git a/package.json b/package.json index d4be691e2fc2..de7e891a6d19 100644 --- a/package.json +++ b/package.json @@ -168,7 +168,7 @@ "react-native-release-profiler": "^0.1.6", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.8.2", - "react-native-screens": "3.30.1", + "react-native-screens": "3.32.0", "react-native-share": "^10.0.2", "react-native-sound": "^0.11.2", "react-native-svg": "14.1.0", diff --git a/patches/react-native-screens+3.30.1+001+fix-screen-type.patch b/patches/react-native-screens+3.30.1+001+fix-screen-type.patch deleted file mode 100644 index f282ec58b07b..000000000000 --- a/patches/react-native-screens+3.30.1+001+fix-screen-type.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/node_modules/react-native-screens/src/components/Screen.tsx b/node_modules/react-native-screens/src/components/Screen.tsx -index 3f9a1cb..45767f7 100644 ---- a/node_modules/react-native-screens/src/components/Screen.tsx -+++ b/node_modules/react-native-screens/src/components/Screen.tsx -@@ -79,6 +79,7 @@ export class InnerScreen extends React.Component { - // Due to how Yoga resolves layout, we need to have different components for modal nad non-modal screens - const AnimatedScreen = - Platform.OS === 'android' || -+ stackPresentation === undefined || - stackPresentation === 'push' || - stackPresentation === 'containedModal' || - stackPresentation === 'containedTransparentModal' From 74a1400217de9e60c7b2fbf19cabf07830269a15 Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Wed, 26 Jun 2024 22:29:26 +0700 Subject: [PATCH 10/29] handle conflict --- src/libs/actions/Task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index dcb4c19b8ed8..a42813fc185b 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -914,7 +914,7 @@ function getParentReport(report: OnyxEntry): OnyxEntry | EmptyObject { +function getReport(reportID: string): OnyxEntry { return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? {}; } From 65c260ef8b7a295092097a90fd93487a1fa5afbc Mon Sep 17 00:00:00 2001 From: cretadn22 Date: Wed, 26 Jun 2024 22:36:52 +0700 Subject: [PATCH 11/29] handle conflict --- src/libs/actions/Task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index a42813fc185b..81c9e98a5284 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -915,7 +915,7 @@ function getParentReport(report: OnyxEntry): OnyxEntry { - return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? {}; + return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; } /** From a626bb36b8eab3ac7980ddedd8fe5f50ac068028 Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 26 Jun 2024 09:26:03 -0700 Subject: [PATCH 12/29] Update Podfile.lock --- ios/Podfile.lock | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 35dccc2de393..a5ffdcb4b63c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1243,7 +1243,13 @@ PODS: - react-native-config (1.5.0): - react-native-config/App (= 1.5.0) - react-native-config/App (1.5.0): - - React-Core + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React + - React-Codegen + - React-RCTFabric + - ReactCommon/turbomodule/core - react-native-document-picker (9.1.1): - RCT-Folly - RCTRequired @@ -1974,7 +1980,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (3.30.1): + - RNScreens (3.32.0): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -1988,13 +1994,14 @@ PODS: - React-ImageManager - React-NativeModulesApple - React-RCTFabric + - React-RCTImage - React-rendererdebug - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 3.30.1) + - RNScreens/common (= 3.32.0) - Yoga - - RNScreens/common (3.30.1): + - RNScreens/common (3.32.0): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -2008,6 +2015,7 @@ PODS: - React-ImageManager - React-NativeModulesApple - React-RCTFabric + - React-RCTImage - React-rendererdebug - React-utils - ReactCommon/turbomodule/bridging @@ -2552,7 +2560,7 @@ SPEC CHECKSUMS: react-native-airship: 38e2596999242b68c933959d6145512e77937ac0 react-native-blob-util: 1ddace5234c62e3e6e4e154d305ad07ef686599b react-native-cameraroll: f373bebbe9f6b7c3fd2a6f97c5171cda574cf957 - react-native-config: 5330c8258265c1e5fdb8c009d2cabd6badd96727 + react-native-config: 5ce986133b07fc258828b20b9506de0e683efc1c react-native-document-picker: 8532b8af7c2c930f9e202aac484ac785b0f4f809 react-native-geolocation: f9e92eb774cb30ac1e099f34b3a94f03b4db7eb3 react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 @@ -2612,7 +2620,7 @@ SPEC CHECKSUMS: RNPermissions: 0b61d30d21acbeafe25baaa47d9bae40a0c65216 RNReactNativeHapticFeedback: 616c35bdec7d20d4c524a7949ca9829c09e35f37 RNReanimated: 323436b1a5364dca3b5f8b1a13458455e0de9efe - RNScreens: 9ec969a95987a6caae170ef09313138abf3331e1 + RNScreens: abd354e98519ed267600b7ee64fdcb8e060b1218 RNShare: 2a4cdfc0626ad56b0ef583d424f2038f772afe58 RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 RNSVG: 18f1381e046be2f1c30b4724db8d0c966238089f From 5646ad4767c5755e9fef94e267f74f4f7df75424 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 28 Jun 2024 15:03:06 +0700 Subject: [PATCH 13/29] fix: workspacce chat owner can be removed --- src/pages/RoomMembersPage.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/RoomMembersPage.tsx b/src/pages/RoomMembersPage.tsx index 395b3244f980..e564f139950b 100644 --- a/src/pages/RoomMembersPage.tsx +++ b/src/pages/RoomMembersPage.tsx @@ -185,7 +185,11 @@ function RoomMembersPage({report, session, policies}: RoomMembersPageProps) { } const pendingChatMember = report?.pendingChatMembers?.findLast((member) => member.accountID === accountID.toString()); const isAdmin = !!(policy && policy.employeeList && details.login && policy.employeeList[details.login]?.role === CONST.POLICY.ROLE.ADMIN); - const isDisabled = (isPolicyExpenseChat && isAdmin) || accountID === session?.accountID || pendingChatMember?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + const isDisabled = + (isPolicyExpenseChat && isAdmin) || + accountID === session?.accountID || + pendingChatMember?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || + details.accountID === report.ownerAccountID; result.push({ keyForList: String(accountID), From 50c5b7a19ab4b398bfd6c6fc4d395772603dc455 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 28 Jun 2024 15:26:54 +0200 Subject: [PATCH 14/29] Fix comparing paths in getDistanceFromPathInRootNavigator --- .../BottomTabBar/index.tsx | 2 +- .../BottomTabBar/index.website.tsx | 2 +- .../createCustomStackNavigator/CustomRouter.ts | 2 +- .../createCustomStackNavigator/index.tsx | 2 +- src/libs/Navigation/Navigation.ts | 6 ++++-- src/libs/Navigation/dismissModalWithReport.ts | 2 +- src/libs/Navigation/getTopmostCentralPaneRoute.ts | 2 +- src/libs/Navigation/getTopmostReportActionID.ts | 2 +- src/libs/Navigation/getTopmostReportId.ts | 2 +- .../linkTo/getActionForBottomTabNavigator.ts | 7 +++---- src/libs/Navigation/linkTo/index.ts | 2 +- .../linkingConfig/customGetPathFromState.ts | 11 +---------- .../linkingConfig/getAdaptedStateFromPath.ts | 2 +- src/libs/Navigation/switchPolicyID.ts | 2 +- src/libs/NavigationUtils.ts | 15 +++++++++++++-- 15 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx index 4194fd6c4c3b..ec3d450b9935 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.tsx @@ -17,7 +17,7 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute' import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx index 9fe78273bdb0..87c7a2cf4619 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar/index.website.tsx @@ -17,7 +17,7 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute' import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar'; import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton'; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts index fa7e8a55d1fc..a1768df5e0d6 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/CustomRouter.ts @@ -7,7 +7,7 @@ import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRo import linkingConfig from '@libs/Navigation/linkingConfig'; import getAdaptedStateFromPath from '@libs/Navigation/linkingConfig/getAdaptedStateFromPath'; import type {NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import type {ResponsiveStackNavigatorRouterOptions} from './types'; diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index 84123dbfa569..310766f80e9d 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -9,7 +9,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute'; import navigationRef from '@libs/Navigation/navigationRef'; import type {RootStackParamList, State} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import SCREENS from '@src/SCREENS'; import CustomRouter from './CustomRouter'; import type {ResponsiveStackNavigatorProps, ResponsiveStackNavigatorRouterOptions} from './types'; diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 5a7182405681..bc8fd9db1b71 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -4,7 +4,7 @@ import {CommonActions, getPathFromState, StackActions} from '@react-navigation/n import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName, removePolicyIDParamFromState} from '@libs/NavigationUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; @@ -129,7 +129,9 @@ function getDistanceFromPathInRootNavigator(path?: string): number { break; } - const pathFromState = getPathFromState(currentState, linkingConfig.config); + // When paths are compared, they the policyID param should be excluded from the state. If it's present in currentState, pathFromState will include it and in result it will be different than the path value. + const currentStateWithoutParams = removePolicyIDParamFromState(currentState as State); + const pathFromState = getPathFromState(currentStateWithoutParams, linkingConfig.config); if (path === pathFromState.substring(1)) { return index; } diff --git a/src/libs/Navigation/dismissModalWithReport.ts b/src/libs/Navigation/dismissModalWithReport.ts index 1bb939f5230f..1579a0565726 100644 --- a/src/libs/Navigation/dismissModalWithReport.ts +++ b/src/libs/Navigation/dismissModalWithReport.ts @@ -4,7 +4,7 @@ import {StackActions} from '@react-navigation/native'; import {findLastIndex} from 'lodash'; import type {OnyxEntry} from 'react-native-onyx'; import Log from '@libs/Log'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import NAVIGATORS from '@src/NAVIGATORS'; diff --git a/src/libs/Navigation/getTopmostCentralPaneRoute.ts b/src/libs/Navigation/getTopmostCentralPaneRoute.ts index 977f23cd3cd7..5ac72281eaf6 100644 --- a/src/libs/Navigation/getTopmostCentralPaneRoute.ts +++ b/src/libs/Navigation/getTopmostCentralPaneRoute.ts @@ -1,4 +1,4 @@ -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import type {CentralPaneName, NavigationPartialRoute, RootStackParamList, State} from './types'; // Get the name of topmost central pane route in the navigation stack. diff --git a/src/libs/Navigation/getTopmostReportActionID.ts b/src/libs/Navigation/getTopmostReportActionID.ts index ade982c87b7d..d3c6e41887d8 100644 --- a/src/libs/Navigation/getTopmostReportActionID.ts +++ b/src/libs/Navigation/getTopmostReportActionID.ts @@ -1,5 +1,5 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import SCREENS from '@src/SCREENS'; import type {RootStackParamList} from './types'; diff --git a/src/libs/Navigation/getTopmostReportId.ts b/src/libs/Navigation/getTopmostReportId.ts index 19bf24f1ba74..dc53d040f087 100644 --- a/src/libs/Navigation/getTopmostReportId.ts +++ b/src/libs/Navigation/getTopmostReportId.ts @@ -1,5 +1,5 @@ import type {NavigationState, PartialState} from '@react-navigation/native'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import SCREENS from '@src/SCREENS'; import type {RootStackParamList} from './types'; diff --git a/src/libs/Navigation/linkTo/getActionForBottomTabNavigator.ts b/src/libs/Navigation/linkTo/getActionForBottomTabNavigator.ts index 8af683e273d6..85580d068ad7 100644 --- a/src/libs/Navigation/linkTo/getActionForBottomTabNavigator.ts +++ b/src/libs/Navigation/linkTo/getActionForBottomTabNavigator.ts @@ -13,7 +13,6 @@ function getActionForBottomTabNavigator( shouldNavigate?: boolean, ): Writable | undefined { const bottomTabNavigatorRoute = state.routes.at(0); - if (!bottomTabNavigatorRoute || bottomTabNavigatorRoute.state === undefined || !action || action.type !== CONST.NAVIGATION.ACTION_TYPE.NAVIGATE) { return; } @@ -22,10 +21,10 @@ function getActionForBottomTabNavigator( let payloadParams = params.params as Record; const screen = params.screen; - if (!payloadParams) { - payloadParams = {policyID}; - } else if (!('policyID' in payloadParams && !!payloadParams?.policyID)) { + if (policyID && !payloadParams?.policyID) { payloadParams = {...payloadParams, policyID}; + } else if (!policyID) { + delete payloadParams?.policyID; } // Check if the current bottom tab is the same as the one we want to navigate to. If it is, we don't need to do anything. diff --git a/src/libs/Navigation/linkTo/index.ts b/src/libs/Navigation/linkTo/index.ts index 3c4608d6b5de..2c23cf573248 100644 --- a/src/libs/Navigation/linkTo/index.ts +++ b/src/libs/Navigation/linkTo/index.ts @@ -4,7 +4,7 @@ import {findFocusedRoute} from '@react-navigation/native'; import {omitBy} from 'lodash'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import extractPolicyIDsFromState from '@libs/Navigation/linkingConfig/extractPolicyIDsFromState'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import shallowCompare from '@libs/ObjectUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import getActionsFromPartialDiff from '@navigation/AppNavigator/getActionsFromPartialDiff'; diff --git a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts index 3ae1ed245ec6..a9c9b6f23b19 100644 --- a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts +++ b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts @@ -1,22 +1,13 @@ import {getPathFromState} from '@react-navigation/native'; -import _ from 'lodash'; import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState'; import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; import type {BottomTabName, RootStackParamList, State} from '@libs/Navigation/types'; +import {removePolicyIDParamFromState} from '@libs/NavigationUtils'; import SCREENS from '@src/SCREENS'; // The policy ID parameter should be included in the URL when any of these pages is opened in the bottom tab. const SCREENS_WITH_POLICY_ID_IN_URL: BottomTabName[] = [SCREENS.HOME] as const; -const removePolicyIDParamFromState = (state: State) => { - const stateCopy = _.cloneDeep(state); - const bottomTabRoute = getTopmostBottomTabRoute(stateCopy); - if (bottomTabRoute?.params && 'policyID' in bottomTabRoute.params) { - delete bottomTabRoute.params.policyID; - } - return stateCopy; -}; - const customGetPathFromState: typeof getPathFromState = (state, options) => { // For the Home and Settings pages we should remove policyID from the params, because on small screens it's displayed twice in the URL const stateWithoutPolicyID = removePolicyIDParamFromState(state as State); diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 17ea0e17d1b9..2b057bf5edaa 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -4,7 +4,7 @@ import type {TupleToUnion} from 'type-fest'; import {isAnonymousUser} from '@libs/actions/Session'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import type {BottomTabName, CentralPaneName, FullScreenName, NavigationPartialRoute, RootStackParamList} from '@libs/Navigation/types'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts index 59461bfc3c8f..0f6477a9ee0e 100644 --- a/src/libs/Navigation/switchPolicyID.ts +++ b/src/libs/Navigation/switchPolicyID.ts @@ -3,7 +3,7 @@ import type {NavigationAction, NavigationContainerRef, NavigationState, PartialS import {getPathFromState} from '@react-navigation/native'; import type {Writable} from 'type-fest'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import isCentralPaneName from '@libs/NavigationUtils'; +import {isCentralPaneName} from '@libs/NavigationUtils'; import CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; diff --git a/src/libs/NavigationUtils.ts b/src/libs/NavigationUtils.ts index 4fdc03c3d334..c1502ffa5800 100644 --- a/src/libs/NavigationUtils.ts +++ b/src/libs/NavigationUtils.ts @@ -1,5 +1,7 @@ +import _ from 'lodash'; import SCREENS from '@src/SCREENS'; -import type {CentralPaneName} from './Navigation/types'; +import getTopmostBottomTabRoute from './Navigation/getTopmostBottomTabRoute'; +import type {CentralPaneName, RootStackParamList, State} from './Navigation/types'; const CENTRAL_PANE_SCREEN_NAMES = new Set([ SCREENS.SETTINGS.WORKSPACES, @@ -23,4 +25,13 @@ function isCentralPaneName(screen: string | undefined): screen is CentralPaneNam return CENTRAL_PANE_SCREEN_NAMES.has(screen as CentralPaneName); } -export default isCentralPaneName; +const removePolicyIDParamFromState = (state: State) => { + const stateCopy = _.cloneDeep(state); + const bottomTabRoute = getTopmostBottomTabRoute(stateCopy); + if (bottomTabRoute?.params && 'policyID' in bottomTabRoute.params) { + delete bottomTabRoute.params.policyID; + } + return stateCopy; +}; + +export {isCentralPaneName, removePolicyIDParamFromState}; From 8ce8886a4956a3783d56bade6cbdc6f6c7602216 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 28 Jun 2024 16:18:26 +0200 Subject: [PATCH 15/29] Create copy of topmostCentralPaneRoute.params in getMatchingBottomTabRouteForState --- .../linkingConfig/getMatchingBottomTabRouteForState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts index 4b4ed25959f0..67d76de4932d 100644 --- a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts +++ b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts @@ -23,7 +23,7 @@ function getMatchingBottomTabRouteForState(state: State, pol const tabName = CENTRAL_PANE_TO_TAB_MAPPING[topmostCentralPaneRoute.name]; if (tabName === SCREENS.SEARCH.BOTTOM_TAB) { - const topmostCentralPaneRouteParams = topmostCentralPaneRoute.params as Record; + const topmostCentralPaneRouteParams = {...topmostCentralPaneRoute.params} as Record; delete topmostCentralPaneRouteParams?.policyIDs; if (policyID) { topmostCentralPaneRouteParams.policyID = policyID; From 8de56b22f4a8eb0debfd9637a68f1f1f62e7918f Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Sun, 30 Jun 2024 09:26:50 +0530 Subject: [PATCH 16/29] fix: connection name in categories page --- src/libs/PolicyUtils.ts | 9 ++++++++- .../workspace/categories/WorkspaceCategoriesPage.tsx | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 37068a9a133f..f3300b49e317 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -7,7 +7,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagList, PolicyTags, TaxRate} from '@src/types/onyx'; -import type {ConnectionLastSync, Connections, CustomUnit, NetSuiteConnection, PolicyFeatureName, Rate, Tenant} from '@src/types/onyx/Policy'; +import type {ConnectionLastSync, ConnectionName, Connections, CustomUnit, NetSuiteConnection, PolicyFeatureName, Rate, Tenant} from '@src/types/onyx/Policy'; import type PolicyEmployee from '@src/types/onyx/PolicyEmployee'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Navigation from './Navigation/Navigation'; @@ -515,6 +515,12 @@ function navigateWhenEnableFeature(policyID: string) { }, CONST.WORKSPACE_ENABLE_FEATURE_REDIRECT_DELAY); } +function getCurrentConnectionName(policy: Policy | undefined): string | undefined { + const accountingIntegrations = Object.values(CONST.POLICY.CONNECTIONS.NAME); + const connectionKey = accountingIntegrations.find((integration) => !!policy?.connections?.[integration]); + return connectionKey ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionKey] : undefined; +} + export { canEditTaxRate, extractPolicyIDFromPath, @@ -573,6 +579,7 @@ export { removePendingFieldsFromCustomUnit, navigateWhenEnableFeature, getIntegrationLastSuccessfulDate, + getCurrentConnectionName, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 533d0ada2a13..715af111a226 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -59,7 +59,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyId}`); const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyId}`); const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0; - const isConnectedToQbo = !!policy?.connections?.quickbooksOnline; + const currentConnectionName = PolicyUtils.getCurrentConnectionName(policy); const fetchCategories = useCallback(() => { Category.openPolicyCategoriesPage(policyId); @@ -267,7 +267,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { style={[styles.textNormal, styles.link]} href={`${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(policyId)}`} > - {`${translate(isConnectedToQbo ? 'workspace.accounting.qbo' : 'workspace.accounting.xero')} ${translate('workspace.accounting.settings')}`} + {`${currentConnectionName} ${translate('workspace.accounting.settings')}`} . From 490d1291d90c069aceb778826261953f44760f5b Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Sun, 30 Jun 2024 09:36:34 +0530 Subject: [PATCH 17/29] fix: other pages and code cleanup --- .../WorkspaceCategoriesSettingsPage.tsx | 15 +++++---------- src/pages/workspace/tags/WorkspaceTagsPage.tsx | 4 ++-- src/pages/workspace/taxes/WorkspaceTaxesPage.tsx | 4 ++-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index 8e15ba650d05..8605598eebb6 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import * as PolicyUtils from '@libs/PolicyUtils'; import {useOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -21,18 +22,12 @@ function WorkspaceCategoriesSettingsPage({policy, route}: WorkspaceCategoriesSet const styles = useThemeStyles(); const {translate} = useLocalize(); const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0; - const isConnectedToQbo = policy?.connections?.quickbooksOnline; - const isConnectedToXero = policy?.connections?.xero; const policyID = route.params.policyID ?? '-1'; const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`); - let toggleSubtitle = ''; - if (isConnectedToQbo) { - toggleSubtitle = `${translate('workspace.categories.needCategoryForExportToIntegration')} ${translate('workspace.accounting.qbo')}.`; - } - if (isConnectedToXero) { - toggleSubtitle = `${translate('workspace.categories.needCategoryForExportToIntegration')} ${translate('workspace.accounting.xero')}.`; - } - + const currentConnectionName = PolicyUtils.getCurrentConnectionName(policy); + + const toggleSubtitle = `${translate('workspace.categories.needCategoryForExportToIntegration')} ${currentConnectionName}.`; + const updateWorkspaceRequiresCategory = (value: boolean) => { setWorkspaceRequiresCategory(policyID, value); }; diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index 0c9da8fbe90e..a1bfd3956c5a 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -52,7 +52,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`); const {environmentURL} = useEnvironment(); const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0; - const isConnectedToQbo = !!policy?.connections?.quickbooksOnline; + const currentConnectionName = PolicyUtils.getCurrentConnectionName(policy); const [policyTagLists, isMultiLevelTags] = useMemo(() => [PolicyUtils.getTagLists(policyTags), PolicyUtils.isMultiLevelTags(policyTags)], [policyTags]); const canSelectMultiple = !isMultiLevelTags; @@ -280,7 +280,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { style={[styles.textNormal, styles.link]} href={`${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(policyID)}`} > - {`${translate(isConnectedToQbo ? 'workspace.accounting.qbo' : 'workspace.accounting.xero')} ${translate('workspace.accounting.settings')}`} + {`${currentConnectionName} ${translate('workspace.accounting.settings')}`} . diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index af77a668264c..4c69c4cb9696 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -58,7 +58,7 @@ function WorkspaceTaxesPage({ const hasAccountingConnections = PolicyUtils.hasAccountingConnections(policy); const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0; - const isConnectedToQbo = !!policy?.connections?.quickbooksOnline; + const currentConnectionName = PolicyUtils.getCurrentConnectionName(policy); const fetchTaxes = useCallback(() => { openPolicyTaxesPage(policyID); @@ -252,7 +252,7 @@ function WorkspaceTaxesPage({ style={[styles.textNormal, styles.link]} href={`${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(policyID)}`} > - {`${translate(isConnectedToQbo ? 'workspace.accounting.qbo' : 'workspace.accounting.xero')} ${translate('workspace.accounting.settings')}`} + {`${currentConnectionName} ${translate('workspace.accounting.settings')}`} . From 4e76882531822db96134ad0a2391ed21f6247c89 Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Sun, 30 Jun 2024 13:39:20 +0530 Subject: [PATCH 18/29] refactor: run prettier --- .../categories/WorkspaceCategoriesSettingsPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index 8605598eebb6..8c5732484f3f 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -1,12 +1,12 @@ import React from 'react'; import {View} from 'react-native'; -import * as PolicyUtils from '@libs/PolicyUtils'; import {useOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; @@ -25,9 +25,9 @@ function WorkspaceCategoriesSettingsPage({policy, route}: WorkspaceCategoriesSet const policyID = route.params.policyID ?? '-1'; const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`); const currentConnectionName = PolicyUtils.getCurrentConnectionName(policy); - + const toggleSubtitle = `${translate('workspace.categories.needCategoryForExportToIntegration')} ${currentConnectionName}.`; - + const updateWorkspaceRequiresCategory = (value: boolean) => { setWorkspaceRequiresCategory(policyID, value); }; From d30705560af27dfc90cf748c5abdbdafb07c3311 Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Sun, 30 Jun 2024 14:05:19 +0530 Subject: [PATCH 19/29] fix: remove unused var --- src/libs/PolicyUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index f3300b49e317..fbc236e880de 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -7,7 +7,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagList, PolicyTags, TaxRate} from '@src/types/onyx'; -import type {ConnectionLastSync, ConnectionName, Connections, CustomUnit, NetSuiteConnection, PolicyFeatureName, Rate, Tenant} from '@src/types/onyx/Policy'; +import type {ConnectionLastSync, Connections, CustomUnit, NetSuiteConnection, PolicyFeatureName, Rate, Tenant} from '@src/types/onyx/Policy'; import type PolicyEmployee from '@src/types/onyx/PolicyEmployee'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Navigation from './Navigation/Navigation'; From 44da559cf665d3a9372ca4d5e59be786786a6d80 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 1 Jul 2024 09:32:35 +0200 Subject: [PATCH 20/29] Import cloneDeep directly from lodash --- src/libs/NavigationUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/NavigationUtils.ts b/src/libs/NavigationUtils.ts index c1502ffa5800..34fc0b971ef6 100644 --- a/src/libs/NavigationUtils.ts +++ b/src/libs/NavigationUtils.ts @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import cloneDeep from 'lodash/cloneDeep'; import SCREENS from '@src/SCREENS'; import getTopmostBottomTabRoute from './Navigation/getTopmostBottomTabRoute'; import type {CentralPaneName, RootStackParamList, State} from './Navigation/types'; @@ -26,7 +26,7 @@ function isCentralPaneName(screen: string | undefined): screen is CentralPaneNam } const removePolicyIDParamFromState = (state: State) => { - const stateCopy = _.cloneDeep(state); + const stateCopy = cloneDeep(state); const bottomTabRoute = getTopmostBottomTabRoute(stateCopy); if (bottomTabRoute?.params && 'policyID' in bottomTabRoute.params) { delete bottomTabRoute.params.policyID; From eb55e3452f10c81bb51fcef31aa751aab9a6822b Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 1 Jul 2024 09:33:18 +0200 Subject: [PATCH 21/29] Refactor comment in getDistanceFromPathInRootNavigator --- 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 bc8fd9db1b71..e9bfb7227403 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -129,9 +129,9 @@ function getDistanceFromPathInRootNavigator(path?: string): number { break; } - // When paths are compared, they the policyID param should be excluded from the state. If it's present in currentState, pathFromState will include it and in result it will be different than the path value. - const currentStateWithoutParams = removePolicyIDParamFromState(currentState as State); - const pathFromState = getPathFromState(currentStateWithoutParams, linkingConfig.config); + // When comparing path and pathFromState, the policyID parameter isn't included in the comparison + const currentStateWithoutPolicyID = removePolicyIDParamFromState(currentState as State); + const pathFromState = getPathFromState(currentStateWithoutPolicyID, linkingConfig.config); if (path === pathFromState.substring(1)) { return index; } From 35cbdde785526c91a36aa143a445765876dbaed9 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 1 Jul 2024 16:06:55 +0700 Subject: [PATCH 22/29] fix: No option to add receipt when submitting to someone --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 53bbebec7b02..25e1b250b026 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -553,9 +553,7 @@ function IOURequestStepConfirmation({ Date: Tue, 2 Jul 2024 01:00:28 +0700 Subject: [PATCH 23/29] update copy based on accounting integration --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/ConnectionUtils.ts | 19 +++++++++++++++++++ ...uickbooksCompanyCardExpenseAccountPage.tsx | 7 ++----- ...oksCompanyCardExpenseAccountSelectPage.tsx | 9 ++------- 5 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 src/libs/ConnectionUtils.ts diff --git a/src/languages/en.ts b/src/languages/en.ts index 958a1704d239..a39f37aedef3 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2088,6 +2088,7 @@ export default { accountsPayableDescription: 'Choose where to create vendor bills.', bankAccount: 'Bank account', bankAccountDescription: 'Choose where to send checks from.', + creditCardAccount: 'Credit card account', companyCardsLocationEnabledDescription: "QuickBooks Online doesn't support locations on vendor bill exports. As you have locations enabled on your workspace, this export option is unavailable.", outOfPocketTaxEnabledDescription: diff --git a/src/languages/es.ts b/src/languages/es.ts index f8664d8b4d2c..8ba1a01f4b10 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2102,6 +2102,7 @@ export default { accountsPayableDescription: 'Elige dónde crear las facturas de proveedores.', bankAccount: 'Cuenta bancaria', bankAccountDescription: 'Elige desde dónde enviar los cheques.', + creditCardAccount: 'Cuenta tarjeta de crédito', companyCardsLocationEnabledDescription: 'QuickBooks Online no permite lugares en las exportaciones de facturas de proveedores. Como tienes activadas los lugares en tu espacio de trabajo, esta opción de exportación no está disponible.', exportPreferredExporterNote: diff --git a/src/libs/ConnectionUtils.ts b/src/libs/ConnectionUtils.ts new file mode 100644 index 000000000000..b3a5e38ffb8a --- /dev/null +++ b/src/libs/ConnectionUtils.ts @@ -0,0 +1,19 @@ +import CONST from '@src/CONST'; +import type {QBONonReimbursableExportAccountType} from '@src/types/onyx/Policy'; +import {translateLocal} from './Localize'; + +function getQBONonReimbursableExportAccountType(exportDestination: QBONonReimbursableExportAccountType | undefined): string { + switch (exportDestination) { + case CONST.QUICKBOOKS_NON_REIMBURSABLE_EXPORT_ACCOUNT_TYPE.DEBIT_CARD: + return translateLocal('workspace.qbo.bankAccount'); + case CONST.QUICKBOOKS_NON_REIMBURSABLE_EXPORT_ACCOUNT_TYPE.CREDIT_CARD: + return translateLocal('workspace.qbo.creditCardAccount'); + case CONST.QUICKBOOKS_NON_REIMBURSABLE_EXPORT_ACCOUNT_TYPE.VENDOR_BILL: + return translateLocal('workspace.qbo.accountsPayable'); + default: + return translateLocal('workspace.qbo.account'); + } +} + +// eslint-disable-next-line import/prefer-default-export +export {getQBONonReimbursableExportAccountType}; diff --git a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx index a286ca4222e0..4068dd2b73b4 100644 --- a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx +++ b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx @@ -8,6 +8,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; +import * as ConnectionUtils from '@libs/ConnectionUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; @@ -51,11 +52,7 @@ function QuickbooksCompanyCardExpenseAccountPage({policy}: WithPolicyConnections Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT.getRoute(policyID))} brickRoadIndicator={errorFields?.nonReimbursableExpensesAccount ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} diff --git a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectPage.tsx b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectPage.tsx index 013539334681..3c44888d782d 100644 --- a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectPage.tsx +++ b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectPage.tsx @@ -10,6 +10,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; +import * as ConnectionUtils from '@libs/ConnectionUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; @@ -86,13 +87,7 @@ function QuickbooksCompanyCardExpenseAccountSelectPage({policy}: WithPolicyConne featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} > - + Date: Tue, 2 Jul 2024 01:25:27 +0700 Subject: [PATCH 24/29] update es copy --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 8ba1a01f4b10..7dd366593f91 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2102,7 +2102,7 @@ export default { accountsPayableDescription: 'Elige dónde crear las facturas de proveedores.', bankAccount: 'Cuenta bancaria', bankAccountDescription: 'Elige desde dónde enviar los cheques.', - creditCardAccount: 'Cuenta tarjeta de crédito', + creditCardAccount: 'Cuenta de la tarjeta de crédito', companyCardsLocationEnabledDescription: 'QuickBooks Online no permite lugares en las exportaciones de facturas de proveedores. Como tienes activadas los lugares en tu espacio de trabajo, esta opción de exportación no está disponible.', exportPreferredExporterNote: From 17cba98b9c790e3a6f65c1a130926b0edfb67e1c Mon Sep 17 00:00:00 2001 From: Alberto Gonzalez-Cela Date: Mon, 1 Jul 2024 21:40:57 +0200 Subject: [PATCH 25/29] Revert "Add new read commands for the workspace profile and initial settings pages" --- .../parameters/OpenPolicyInitialPageParams.ts | 5 ---- .../parameters/OpenPolicyProfilePageParams.ts | 5 ---- src/libs/API/parameters/index.ts | 2 -- src/libs/API/types.ts | 4 --- src/libs/actions/Policy/Policy.ts | 16 ----------- src/pages/workspace/WorkspaceInitialPage.tsx | 16 ++--------- src/pages/workspace/WorkspaceProfilePage.tsx | 27 +++---------------- src/pages/workspace/withPolicy.tsx | 1 - 8 files changed, 6 insertions(+), 70 deletions(-) delete mode 100644 src/libs/API/parameters/OpenPolicyInitialPageParams.ts delete mode 100644 src/libs/API/parameters/OpenPolicyProfilePageParams.ts diff --git a/src/libs/API/parameters/OpenPolicyInitialPageParams.ts b/src/libs/API/parameters/OpenPolicyInitialPageParams.ts deleted file mode 100644 index 764abe9a6a77..000000000000 --- a/src/libs/API/parameters/OpenPolicyInitialPageParams.ts +++ /dev/null @@ -1,5 +0,0 @@ -type OpenPolicyInitialPageParams = { - policyID: string; -}; - -export default OpenPolicyInitialPageParams; diff --git a/src/libs/API/parameters/OpenPolicyProfilePageParams.ts b/src/libs/API/parameters/OpenPolicyProfilePageParams.ts deleted file mode 100644 index 55dce33a3dac..000000000000 --- a/src/libs/API/parameters/OpenPolicyProfilePageParams.ts +++ /dev/null @@ -1,5 +0,0 @@ -type OpenPolicyProfilePageParams = { - policyID: string; -}; - -export default OpenPolicyProfilePageParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index ef44ce06bced..f032edf96e36 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -193,8 +193,6 @@ export type {default as OpenPolicyDistanceRatesPageParams} from './OpenPolicyDis export type {default as OpenPolicyTaxesPageParams} from './OpenPolicyTaxesPageParams'; export type {default as EnablePolicyTaxesParams} from './EnablePolicyTaxesParams'; export type {default as OpenPolicyMoreFeaturesPageParams} from './OpenPolicyMoreFeaturesPageParams'; -export type {default as OpenPolicyProfilePageParams} from './OpenPolicyProfilePageParams'; -export type {default as OpenPolicyInitialPageParams} from './OpenPolicyInitialPageParams'; export type {default as CreatePolicyDistanceRateParams} from './CreatePolicyDistanceRateParams'; export type {default as SetPolicyDistanceRatesUnitParams} from './SetPolicyDistanceRatesUnitParams'; export type {default as SetPolicyDistanceRatesDefaultCategoryParams} from './SetPolicyDistanceRatesDefaultCategoryParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 062f0e915873..1d6456f3df47 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -537,8 +537,6 @@ const READ_COMMANDS = { OPEN_POLICY_WORKFLOWS_PAGE: 'OpenPolicyWorkflowsPage', OPEN_POLICY_DISTANCE_RATES_PAGE: 'OpenPolicyDistanceRatesPage', OPEN_POLICY_MORE_FEATURES_PAGE: 'OpenPolicyMoreFeaturesPage', - OPEN_POLICY_PROFILE_PAGE: 'OpenPolicyProfilePage', - OPEN_POLICY_INITIAL_PAGE: 'OpenPolicyInitialPage', OPEN_POLICY_ACCOUNTING_PAGE: 'OpenPolicyAccountingPage', SEARCH: 'Search', OPEN_SUBSCRIPTION_PAGE: 'OpenSubscriptionPage', @@ -587,8 +585,6 @@ type ReadCommandParameters = { [READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE]: Parameters.OpenPolicyWorkflowsPageParams; [READ_COMMANDS.OPEN_POLICY_DISTANCE_RATES_PAGE]: Parameters.OpenPolicyDistanceRatesPageParams; [READ_COMMANDS.OPEN_POLICY_MORE_FEATURES_PAGE]: Parameters.OpenPolicyMoreFeaturesPageParams; - [READ_COMMANDS.OPEN_POLICY_PROFILE_PAGE]: Parameters.OpenPolicyProfilePageParams; - [READ_COMMANDS.OPEN_POLICY_INITIAL_PAGE]: Parameters.OpenPolicyInitialPageParams; [READ_COMMANDS.OPEN_POLICY_ACCOUNTING_PAGE]: Parameters.OpenPolicyAccountingPageParams; [READ_COMMANDS.SEARCH]: Parameters.SearchParams; [READ_COMMANDS.OPEN_SUBSCRIPTION_PAGE]: null; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index b42b0c0d38a6..1bb53fbfa002 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -18,9 +18,7 @@ import type { EnablePolicyWorkflowsParams, LeavePolicyParams, OpenDraftWorkspaceRequestParams, - OpenPolicyInitialPageParams, OpenPolicyMoreFeaturesPageParams, - OpenPolicyProfilePageParams, OpenPolicyTaxesPageParams, OpenPolicyWorkflowsPageParams, OpenWorkspaceInvitePageParams, @@ -2823,18 +2821,6 @@ function openPolicyMoreFeaturesPage(policyID: string) { API.read(READ_COMMANDS.OPEN_POLICY_MORE_FEATURES_PAGE, params); } -function openPolicyProfilePage(policyID: string) { - const params: OpenPolicyProfilePageParams = {policyID}; - - API.read(READ_COMMANDS.OPEN_POLICY_PROFILE_PAGE, params); -} - -function openPolicyInitialPage(policyID: string) { - const params: OpenPolicyInitialPageParams = {policyID}; - - API.read(READ_COMMANDS.OPEN_POLICY_INITIAL_PAGE, params); -} - function setPolicyCustomTaxName(policyID: string, customTaxName: string) { const policy = getPolicy(policyID); const originalCustomTaxName = policy?.taxRates?.name; @@ -3042,8 +3028,6 @@ export { enablePolicyWorkflows, enableDistanceRequestTax, openPolicyMoreFeaturesPage, - openPolicyProfilePage, - openPolicyInitialPage, generateCustomUnitID, clearQBOErrorField, clearXeroErrorField, diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 29bd29de7b83..242bb5885d1d 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -1,4 +1,4 @@ -import {useFocusEffect, useNavigationState} from '@react-navigation/native'; +import {useNavigationState} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; @@ -87,7 +87,7 @@ function dismissError(policyID: string, pendingAction: PendingAction | undefined } } -function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAccount, policyCategories, route}: WorkspaceInitialPageProps) { +function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAccount, policyCategories}: WorkspaceInitialPageProps) { const styles = useThemeStyles(); const policy = policyDraft?.id ? policyDraft : policyProp; const [isCurrencyModalOpen, setIsCurrencyModalOpen] = useState(false); @@ -135,18 +135,6 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, reimbursementAcc setIsCurrencyModalOpen(false); }, [policy?.outputCurrency, isCurrencyModalOpen]); - const fetchPolicyData = useCallback(() => { - Policy.openPolicyInitialPage(route.params.policyID); - }, [route.params.policyID]); - - useNetwork({onReconnect: fetchPolicyData}); - - useFocusEffect( - useCallback(() => { - fetchPolicyData(); - }, [fetchPolicyData]), - ); - /** Call update workspace currency and hide the modal */ const confirmCurrencyChangeAndHideModal = useCallback(() => { Policy.updateGeneralSettings(policyID, policyName, CONST.CURRENCY.USD); diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index 6f2097688b5c..6a86a83b5d11 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -1,5 +1,3 @@ -import {useFocusEffect} from '@react-navigation/native'; -import type {StackScreenProps} from '@react-navigation/stack'; import {ExpensiMark} from 'expensify-common'; import React, {useCallback, useState} from 'react'; import type {ImageStyle, StyleProp} from 'react-native'; @@ -17,14 +15,12 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Section from '@components/Section'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; @@ -33,23 +29,22 @@ import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import withPolicy from './withPolicy'; import type {WithPolicyProps} from './withPolicy'; import WorkspacePageWithSections from './WorkspacePageWithSections'; -type WorkspaceProfilePageOnyxProps = { +type WorkSpaceProfilePageOnyxProps = { /** Constant, list of available currencies */ currencyList: OnyxEntry; }; -type WorkspaceProfilePageProps = WithPolicyProps & WorkspaceProfilePageOnyxProps & StackScreenProps; +type WorkSpaceProfilePageProps = WithPolicyProps & WorkSpaceProfilePageOnyxProps; const parser = new ExpensiMark(); -function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkspaceProfilePageProps) { +function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfilePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -87,20 +82,6 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkspaceProfi const imageStyle: StyleProp = isSmallScreenWidth ? [styles.mhv12, styles.mhn5, styles.mbn5] : [styles.mhv8, styles.mhn8, styles.mbn5]; const shouldShowAddress = !readOnly || formattedAddress; - const fetchPolicyData = useCallback(() => { - Policy.openPolicyProfilePage(route.params.policyID); - }, [route.params.policyID]); - - useNetwork({onReconnect: fetchPolicyData}); - - // We have the same focus effect in the WorkspaceInitialPage, this way we can get the policy data in narrow - // as well as in the wide layout when looking at policy settings. - useFocusEffect( - useCallback(() => { - fetchPolicyData(); - }, [fetchPolicyData]), - ); - const DefaultAvatar = useCallback( () => ( ({ + withOnyx({ currencyList: {key: ONYXKEYS.CURRENCY_LIST}, })(WorkspaceProfilePage), ); diff --git a/src/pages/workspace/withPolicy.tsx b/src/pages/workspace/withPolicy.tsx index 4a908c0928ab..6300d3fcf280 100644 --- a/src/pages/workspace/withPolicy.tsx +++ b/src/pages/workspace/withPolicy.tsx @@ -16,7 +16,6 @@ type PolicyRoute = RouteProp< NavigatorsParamList, | typeof SCREENS.REIMBURSEMENT_ACCOUNT_ROOT | typeof SCREENS.WORKSPACE.INITIAL - | typeof SCREENS.WORKSPACE.PROFILE | typeof SCREENS.WORKSPACE.BILLS | typeof SCREENS.WORKSPACE.MORE_FEATURES | typeof SCREENS.WORKSPACE.MEMBERS From f60f7781c29f0f7416a29ed7256aa6f2233b8d10 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 1 Jul 2024 20:32:52 +0000 Subject: [PATCH 26/29] Update version to 9.0.3-4 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index c7fbf56f86e6..edbd22f2a3b3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009000303 - versionName "9.0.3-3" + versionCode 1009000304 + versionName "9.0.3-4" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index f400a6f43605..ecbe190ba247 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.3.3 + 9.0.3.4 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index b800b9f73118..a22929abb60d 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.3.3 + 9.0.3.4 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 7e562b7de1f2..cd0698dfaba0 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.3 CFBundleVersion - 9.0.3.3 + 9.0.3.4 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 07b13b5f078a..afc59257f4bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.3-3", + "version": "9.0.3-4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.3-3", + "version": "9.0.3-4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index ddb75aae9700..cf03afda8136 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.3-3", + "version": "9.0.3-4", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From faec573e172847f9a833da0c7b5e39f3ebcac82f Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Mon, 1 Jul 2024 14:41:47 -0600 Subject: [PATCH 27/29] Revert "[No QA] Fail patch-package on errors or warnings" --- ...y+react-native-live-markdown+0.1.85.patch} | 0 ...ve-keyboard-controller+1.12.2.patch.patch} | 0 scripts/applyPatches.sh | 39 ------------------- scripts/postInstall.sh | 7 +--- 4 files changed, 2 insertions(+), 44 deletions(-) rename patches/{@expensify+react-native-live-markdown+0.1.88.patch => @expensify+react-native-live-markdown+0.1.85.patch} (100%) rename patches/{react-native-keyboard-controller+1.12.2.patch => react-native-keyboard-controller+1.12.2.patch.patch} (100%) delete mode 100755 scripts/applyPatches.sh diff --git a/patches/@expensify+react-native-live-markdown+0.1.88.patch b/patches/@expensify+react-native-live-markdown+0.1.85.patch similarity index 100% rename from patches/@expensify+react-native-live-markdown+0.1.88.patch rename to patches/@expensify+react-native-live-markdown+0.1.85.patch diff --git a/patches/react-native-keyboard-controller+1.12.2.patch b/patches/react-native-keyboard-controller+1.12.2.patch.patch similarity index 100% rename from patches/react-native-keyboard-controller+1.12.2.patch rename to patches/react-native-keyboard-controller+1.12.2.patch.patch diff --git a/scripts/applyPatches.sh b/scripts/applyPatches.sh deleted file mode 100755 index 31e1b7f50e6a..000000000000 --- a/scripts/applyPatches.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# This script is a simple wrapper around patch-package that fails if any errors or warnings are detected. -# This is useful because patch-package does not fail on errors or warnings by default, -# which means that broken patches are easy to miss, and leads to developer frustration and wasted time. - -SCRIPTS_DIR=$(dirname "${BASH_SOURCE[0]}") -source "$SCRIPTS_DIR/shellUtils.sh" - -# Run patch-package and capture its output and exit code, while still displaying the original output to the terminal -# (we use `script -q /dev/null` to preserve colorization in the output) -TEMP_OUTPUT="$(mktemp)" -script -q /dev/null npx patch-package --error-on-fail 2>&1 | tee "$TEMP_OUTPUT" -EXIT_CODE=${PIPESTATUS[0]} -OUTPUT="$(cat "$TEMP_OUTPUT")" -rm -f "$TEMP_OUTPUT" - -# Check if the output contains a warning message -echo "$OUTPUT" | grep -q "Warning:" -WARNING_FOUND=$? - -printf "\n"; - -# Determine the final exit code -if [ "$EXIT_CODE" -eq 0 ]; then - if [ $WARNING_FOUND -eq 0 ]; then - # patch-package succeeded but warning was found - error "It looks like you upgraded a dependency without upgrading the patch. Please review the patch, determine if it's still needed, and port it to the new version of the dependency." - exit 1 - else - # patch-package succeeded and no warning was found - success "patch-package succeeded without errors or warnings" - exit 0 - fi -else - # patch-package failed - error "patch-package failed to apply a patch" - exit "$EXIT_CODE" -fi diff --git a/scripts/postInstall.sh b/scripts/postInstall.sh index 782c8ef5822c..339fdf25cb10 100755 --- a/scripts/postInstall.sh +++ b/scripts/postInstall.sh @@ -1,14 +1,11 @@ #!/bin/bash -# Exit immediately if any command exits with a non-zero status -set -e - # Go to project root ROOT_DIR=$(dirname "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)") cd "$ROOT_DIR" || exit 1 -# Apply packages using patch-package -scripts/applyPatches.sh +# Run patch-package +npx patch-package # Install node_modules in subpackages, unless we're in a CI/CD environment, # where the node_modules for subpackages are cached separately. From 051e86c99dd1526607a3fdae143fbcb876b33d36 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 1 Jul 2024 15:43:46 -0700 Subject: [PATCH 28/29] Revert "Create usePaginatedReportActions hook" --- src/hooks/usePaginatedReportActions.ts | 33 ---- src/pages/ReportDetailsPage.tsx | 14 +- src/pages/home/ReportScreen.tsx | 52 ++++-- tests/ui/UnreadIndicatorsTest.tsx | 218 ++++++++++++------------- tests/utils/TestHelper.ts | 31 +--- 5 files changed, 157 insertions(+), 191 deletions(-) delete mode 100644 src/hooks/usePaginatedReportActions.ts diff --git a/src/hooks/usePaginatedReportActions.ts b/src/hooks/usePaginatedReportActions.ts deleted file mode 100644 index 44a82253b7c0..000000000000 --- a/src/hooks/usePaginatedReportActions.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {useMemo} from 'react'; -import {useOnyx} from 'react-native-onyx'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import ONYXKEYS from '@src/ONYXKEYS'; - -/** - * Get the longest continuous chunk of reportActions including the linked reportAction. If not linking to a specific action, returns the continuous chunk of newest reportActions. - */ -function usePaginatedReportActions(reportID?: string, reportActionID?: string) { - // Use `||` instead of `??` to handle empty string. - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const reportIDWithDefault = reportID || '-1'; - const [sortedAllReportActions = []] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportIDWithDefault}`, { - canEvict: false, - selector: (allReportActions) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true), - }); - - const reportActions = useMemo(() => { - if (!sortedAllReportActions.length) { - return []; - } - return ReportActionsUtils.getContinuousReportActionChain(sortedAllReportActions, reportActionID); - }, [reportActionID, sortedAllReportActions]); - - const linkedAction = useMemo(() => sortedAllReportActions.find((obj) => String(obj.reportActionID) === String(reportActionID)), [reportActionID, sortedAllReportActions]); - - return { - reportActions, - linkedAction, - }; -} - -export default usePaginatedReportActions; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index b1ef9fbfdd97..dcbad36d1eda 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -22,7 +22,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import usePaginatedReportActions from '@hooks/usePaginatedReportActions'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; @@ -80,10 +79,21 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD const {translate} = useLocalize(); const {isOffline} = useNetwork(); const styles = useThemeStyles(); + // The app would crash due to subscribing to the entire report collection if parentReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || '-1'}`); - const {reportActions} = usePaginatedReportActions(report.reportID); + const [sortedAllReportActions = []] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID ?? '-1'}`, { + canEvict: false, + selector: (allReportActions: OnyxEntry) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true), + }); + + const reportActions = useMemo(() => { + if (!sortedAllReportActions.length) { + return []; + } + return ReportActionsUtils.getContinuousReportActionChain(sortedAllReportActions); + }, [sortedAllReportActions]); const transactionThreadReportID = useMemo( () => ReportActionsUtils.getOneTransactionThreadReportID(report.reportID, reportActions ?? [], isOffline), diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 3076aeb87bd1..3c2ae7bbc6e6 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -26,11 +26,11 @@ import useIsReportOpenInRHP from '@hooks/useIsReportOpenInRHP'; import useLastAccessedReportID from '@hooks/useLastAccessedReportID'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import usePaginatedReportActions from '@hooks/usePaginatedReportActions'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import useViewportOffsetTop from '@hooks/useViewportOffsetTop'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import {getCurrentUserAccountID} from '@libs/actions/Report'; import Timing from '@libs/actions/Timing'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -68,6 +68,9 @@ type ReportScreenOnyxProps = { /** The policies which the user has access to */ policies: OnyxCollection; + /** An array containing all report actions related to this report, sorted based on a date criterion */ + sortedAllReportActions: OnyxTypes.ReportAction[]; + /** Additional report details */ reportNameValuePairs: OnyxEntry; @@ -116,6 +119,7 @@ function ReportScreen({ betas = [], route, reportNameValuePairs, + sortedAllReportActions, reportMetadata = { isLoadingInitialReportActions: true, isLoadingOlderReportActions: false, @@ -279,14 +283,12 @@ function ReportScreen({ const prevReport = usePrevious(report); const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isLinkingToMessage, setIsLinkingToMessage] = useState(!!reportActionIDFromRoute); - - const [currentUserAccountID = -1] = useOnyx(ONYXKEYS.SESSION, {selector: (value) => value?.accountID}); - const {reportActions, linkedAction} = usePaginatedReportActions(report.reportID, reportActionIDFromRoute); - const isLinkedActionDeleted = useMemo(() => !!linkedAction && !ReportActionsUtils.shouldReportActionBeVisible(linkedAction, linkedAction.reportActionID), [linkedAction]); - const isLinkedActionInaccessibleWhisper = useMemo( - () => !!linkedAction && ReportActionsUtils.isWhisperAction(linkedAction) && !(linkedAction?.whisperedToAccountIDs ?? []).includes(currentUserAccountID), - [currentUserAccountID, linkedAction], - ); + const reportActions = useMemo(() => { + if (!sortedAllReportActions.length) { + return []; + } + return ReportActionsUtils.getContinuousReportActionChain(sortedAllReportActions, reportActionIDFromRoute); + }, [reportActionIDFromRoute, sortedAllReportActions]); // Define here because reportActions are recalculated before mount, allowing data to display faster than useEffect can trigger. // If we have cached reportActions, they will be shown immediately. @@ -311,6 +313,10 @@ function ReportScreen({ const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; const isEmptyChat = useMemo(() => ReportUtils.isEmptyReport(report), [report]); const isOptimisticDelete = report.statusNum === CONST.REPORT.STATUS_NUM.CLOSED; + const isLinkedMessageAvailable = useMemo( + (): boolean => sortedAllReportActions.findIndex((obj) => String(obj.reportActionID) === String(reportActionIDFromRoute)) > -1, + [sortedAllReportActions, reportActionIDFromRoute], + ); // If there's a non-404 error for the report we should show it instead of blocking the screen const hasHelpfulErrors = Object.keys(report?.errorFields ?? {}).some((key) => key !== 'notFound'); @@ -402,12 +408,12 @@ function ReportScreen({ const isLoading = isLoadingApp ?? (!reportIDFromRoute || (!isSidebarLoaded && !isReportOpenInRHP) || PersonalDetailsUtils.isPersonalDetailsEmpty()); const shouldShowSkeleton = - !linkedAction && + !isLinkedMessageAvailable && (isLinkingToMessage || !isCurrentReportLoadedFromOnyx || (reportActions.length === 0 && !!reportMetadata?.isLoadingInitialReportActions) || isLoading || - (!!reportActionIDFromRoute && !!reportMetadata?.isLoadingInitialReportActions)); + (!!reportActionIDFromRoute && reportMetadata?.isLoadingInitialReportActions)); const shouldShowReportActionList = isCurrentReportLoadedFromOnyx && !isLoading; const currentReportIDFormRoute = route.params?.reportID; @@ -672,16 +678,28 @@ function ReportScreen({ fetchReport(); }, [fetchReport]); + const {isLinkedReportActionDeleted, isInaccessibleWhisper} = useMemo(() => { + const currentUserAccountID = getCurrentUserAccountID(); + if (!reportActionIDFromRoute || !sortedAllReportActions) { + return {isLinkedReportActionDeleted: false, isInaccessibleWhisper: false}; + } + const action = sortedAllReportActions.find((item) => item.reportActionID === reportActionIDFromRoute); + return { + isLinkedReportActionDeleted: action && !ReportActionsUtils.shouldReportActionBeVisible(action, action.reportActionID), + isInaccessibleWhisper: action && ReportActionsUtils.isWhisperAction(action) && !(action?.whisperedToAccountIDs ?? []).includes(currentUserAccountID), + }; + }, [reportActionIDFromRoute, sortedAllReportActions]); + // If user redirects to an inaccessible whisper via a deeplink, on a report they have access to, // then we set reportActionID as empty string, so we display them the report and not the "Not found page". useEffect(() => { - if (!isLinkedActionInaccessibleWhisper) { + if (!isInaccessibleWhisper) { return; } Navigation.isNavigationReady().then(() => { Navigation.setParams({reportActionID: ''}); }); - }, [isLinkedActionInaccessibleWhisper]); + }, [isInaccessibleWhisper]); useEffect(() => { if (!!report.lastReadTime || !ReportUtils.isTaskReport(report)) { @@ -691,7 +709,7 @@ function ReportScreen({ Report.readNewestAction(report.reportID); }, [report]); - if ((!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted) ?? (!shouldShowSkeleton && reportActionIDFromRoute && reportActions?.length === 0 && !isLinkingToMessage)) { + if ((!isInaccessibleWhisper && isLinkedReportActionDeleted) ?? (!shouldShowSkeleton && reportActionIDFromRoute && reportActions?.length === 0 && !isLinkingToMessage)) { return ( `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, + canEvict: false, + selector: (allReportActions: OnyxEntry) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true), + }, reportNameValuePairs: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${getReportID(route)}`, allowStaleData: true, @@ -833,6 +856,7 @@ export default withCurrentReportID( ReportScreen, (prevProps, nextProps) => prevProps.isSidebarLoaded === nextProps.isSidebarLoaded && + lodashIsEqual(prevProps.sortedAllReportActions, nextProps.sortedAllReportActions) && lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) && lodashIsEqual(prevProps.betas, nextProps.betas) && lodashIsEqual(prevProps.policies, nextProps.policies) && diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index f84c75823753..b5990ee5d002 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -200,104 +200,81 @@ let reportAction9CreatedDate: string; /** * Sets up a test with a logged in user that has one unread chat from another user. Returns the test instance. */ -async function signInAndGetAppWithUnreadChat() { +function signInAndGetAppWithUnreadChat(): Promise { // Render the App and sign in as a test user. render(); - await waitForBatchedUpdatesWithAct(); - await waitForBatchedUpdatesWithAct(); - - const hintText = Localize.translateLocal('loginForm.loginForm'); - const loginForm = screen.queryAllByLabelText(hintText); - expect(loginForm).toHaveLength(1); - - await act(async () => { - await TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); - }); - await waitForBatchedUpdatesWithAct(); - - User.subscribeToUserEvents(); - await waitForBatchedUpdates(); - - const TEN_MINUTES_AGO = subMinutes(new Date(), 10); - reportAction3CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 30), CONST.DATE.FNS_DB_FORMAT_STRING); - reportAction9CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 90), CONST.DATE.FNS_DB_FORMAT_STRING); - - // Simulate setting an unread report and personal details - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { - reportID: REPORT_ID, - reportName: CONST.REPORT.DEFAULT_REPORT_NAME, - lastReadTime: reportAction3CreatedDate, - lastVisibleActionCreated: reportAction9CreatedDate, - lastMessageText: 'Test', - participants: {[USER_B_ACCOUNT_ID]: {hidden: false}}, - lastActorAccountID: USER_B_ACCOUNT_ID, - type: CONST.REPORT.TYPE.CHAT, - }); - const createdReportActionID = NumberUtils.rand64().toString(); - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, { - [createdReportActionID]: { - actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, - automatic: false, - created: format(TEN_MINUTES_AGO, CONST.DATE.FNS_DB_FORMAT_STRING), - reportActionID: createdReportActionID, - message: [ - { - style: 'strong', - text: '__FAKE__', - type: 'TEXT', - }, - { - style: 'normal', - text: 'created this report', - type: 'TEXT', + return waitForBatchedUpdatesWithAct() + .then(async () => { + await waitForBatchedUpdatesWithAct(); + const hintText = Localize.translateLocal('loginForm.loginForm'); + const loginForm = screen.queryAllByLabelText(hintText); + expect(loginForm).toHaveLength(1); + + await act(async () => { + await TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); + }); + return waitForBatchedUpdatesWithAct(); + }) + .then(() => { + User.subscribeToUserEvents(); + return waitForBatchedUpdates(); + }) + .then(async () => { + const TEN_MINUTES_AGO = subMinutes(new Date(), 10); + reportAction3CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 30), CONST.DATE.FNS_DB_FORMAT_STRING); + reportAction9CreatedDate = format(addSeconds(TEN_MINUTES_AGO, 90), CONST.DATE.FNS_DB_FORMAT_STRING); + + // Simulate setting an unread report and personal details + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { + reportID: REPORT_ID, + reportName: CONST.REPORT.DEFAULT_REPORT_NAME, + lastReadTime: reportAction3CreatedDate, + lastVisibleActionCreated: reportAction9CreatedDate, + lastMessageText: 'Test', + participants: {[USER_B_ACCOUNT_ID]: {hidden: false}}, + lastActorAccountID: USER_B_ACCOUNT_ID, + type: CONST.REPORT.TYPE.CHAT, + }); + const createdReportActionID = NumberUtils.rand64().toString(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, { + [createdReportActionID]: { + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + automatic: false, + created: format(TEN_MINUTES_AGO, CONST.DATE.FNS_DB_FORMAT_STRING), + reportActionID: createdReportActionID, + message: [ + { + style: 'strong', + text: '__FAKE__', + type: 'TEXT', + }, + { + style: 'normal', + text: 'created this report', + type: 'TEXT', + }, + ], }, - ], - }, - 1: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 10), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '1', createdReportActionID), - 2: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 20), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '2', '1'), - 3: TestHelper.buildTestReportComment(reportAction3CreatedDate, USER_B_ACCOUNT_ID, '3', '2'), - 4: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 40), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '4', '3'), - 5: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 50), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '5', '4'), - 6: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 60), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '6', '5'), - 7: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 70), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '7', '6'), - 8: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 80), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '8', '7'), - 9: TestHelper.buildTestReportComment(reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9', '8'), - }); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { - [USER_B_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), - }); - - // We manually setting the sidebar as loaded since the onLayout event does not fire in tests - AppActions.setSidebarLoaded(); - - await waitForBatchedUpdatesWithAct(); -} - -let lastComment = 'Current User Comment 1'; -async function addComment() { - const num = Number.parseInt(lastComment.slice(-1), 10); - lastComment = `${lastComment.slice(0, -1)}${num + 1}`; - const comment = lastComment; - const reportActionsBefore = (await TestHelper.onyxGet(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`)) as Record; - Report.addComment(REPORT_ID, comment); - const reportActionsAfter = (await TestHelper.onyxGet(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`)) as Record; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const newReportActionID = Object.keys(reportActionsAfter).find((reportActionID) => !reportActionsBefore[reportActionID])!; - await act(() => - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, { - [newReportActionID]: { - previousReportActionID: '9', - }, - }), - ); - await waitForBatchedUpdatesWithAct(); - - // Verify the comment is visible (it will appear twice, once in the LHN and once on the report screen) - expect(screen.getAllByText(comment)[0]).toBeOnTheScreen(); + 1: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 10), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '1', createdReportActionID), + 2: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 20), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '2', '1'), + 3: TestHelper.buildTestReportComment(reportAction3CreatedDate, USER_B_ACCOUNT_ID, '3', '2'), + 4: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 40), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '4', '3'), + 5: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 50), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '5', '4'), + 6: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 60), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '6', '5'), + 7: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 70), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '7', '6'), + 8: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 80), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '8', '7'), + 9: TestHelper.buildTestReportComment(reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9', '8'), + }); + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [USER_B_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), + }); + + // We manually setting the sidebar as loaded since the onLayout event does not fire in tests + AppActions.setSidebarLoaded(); + return waitForBatchedUpdatesWithAct(); + }); } -const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); - describe('Unread Indicators', () => { afterEach(() => { jest.clearAllMocks(); @@ -342,6 +319,7 @@ describe('Unread Indicators', () => { expect(reportComments).toHaveLength(9); // Since the last read timestamp is the timestamp of action 3 we should have an unread indicator above the next "unread" action which will // have actionID of 4 + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); const reportActionID = unreadIndicator[0]?.props?.['data-action-id']; @@ -357,6 +335,7 @@ describe('Unread Indicators', () => { .then(async () => { await act(() => transitionEndCB?.()); // Verify the unread indicator is present + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); }) @@ -379,6 +358,7 @@ describe('Unread Indicators', () => { }) .then(() => { // Verify the unread indicator is not present + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(0); // Tap on the chat again @@ -386,6 +366,7 @@ describe('Unread Indicators', () => { }) .then(() => { // Verify the unread indicator is not present + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(0); expect(areYouOnChatListScreen()).toBe(false); @@ -495,6 +476,7 @@ describe('Unread Indicators', () => { }) .then(() => { // Verify the indicator appears above the last action + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); const reportActionID = unreadIndicator[0]?.props?.['data-action-id']; @@ -529,6 +511,7 @@ describe('Unread Indicators', () => { return navigateToSidebarOption(0); }) .then(() => { + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(0); @@ -537,23 +520,30 @@ describe('Unread Indicators', () => { return waitFor(() => expect(isNewMessagesBadgeVisible()).toBe(false)); })); - it('Keep showing the new line indicator when a new message is created by the current user', async () => { - await signInAndGetAppWithUnreadChat(); - - // Verify we are on the LHN and that the chat shows as unread in the LHN - expect(areYouOnChatListScreen()).toBe(true); + it('Keep showing the new line indicator when a new message is created by the current user', () => + signInAndGetAppWithUnreadChat() + .then(() => { + // Verify we are on the LHN and that the chat shows as unread in the LHN + expect(areYouOnChatListScreen()).toBe(true); - // Navigate to the report and verify the indicator is present - await navigateToSidebarOption(0); - await act(() => transitionEndCB?.()); - let unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); - expect(unreadIndicator).toHaveLength(1); + // Navigate to the report and verify the indicator is present + return navigateToSidebarOption(0); + }) + .then(async () => { + await act(() => transitionEndCB?.()); + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); + const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); + expect(unreadIndicator).toHaveLength(1); - // Leave a comment as the current user and verify the indicator is not removed - await addComment(); - unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); - expect(unreadIndicator).toHaveLength(1); - }); + // Leave a comment as the current user and verify the indicator is removed + Report.addComment(REPORT_ID, 'Current User Comment 1'); + return waitForBatchedUpdates(); + }) + .then(() => { + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); + const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); + expect(unreadIndicator).toHaveLength(1); + })); xit('Keeps the new line indicator when the user moves the App to the background', () => signInAndGetAppWithUnreadChat() @@ -565,6 +555,7 @@ describe('Unread Indicators', () => { return navigateToSidebarOption(0); }) .then(() => { + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); @@ -573,6 +564,7 @@ describe('Unread Indicators', () => { }) .then(() => navigateToSidebarOption(0)) .then(() => { + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(0); @@ -581,6 +573,7 @@ describe('Unread Indicators', () => { return waitForBatchedUpdates(); }) .then(() => { + const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); let unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); @@ -604,10 +597,11 @@ describe('Unread Indicators', () => { signInAndGetAppWithUnreadChat() // Navigate to the chat and simulate leaving a comment from the current user .then(() => navigateToSidebarOption(0)) - .then(() => + .then(() => { // Leave a comment as the current user - addComment(), - ) + Report.addComment(REPORT_ID, 'Current User Comment 1'); + return waitForBatchedUpdates(); + }) .then(() => { // Simulate the response from the server so that the comment can be deleted in this test lastReportAction = reportActions ? CollectionUtils.lastItem(reportActions) : undefined; @@ -625,7 +619,7 @@ describe('Unread Indicators', () => { expect(alternateText).toHaveLength(1); // This message is visible on the sidebar and the report screen, so there are two occurrences. - expect(screen.getAllByText(lastComment)[0]).toBeOnTheScreen(); + expect(screen.getAllByText('Current User Comment 1')[0]).toBeOnTheScreen(); if (lastReportAction) { Report.deleteReportComment(REPORT_ID, lastReportAction); diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index dffb2b4e312a..9ca0969abc6a 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -1,6 +1,5 @@ import {Str} from 'expensify-common'; import Onyx from 'react-native-onyx'; -import type {ConnectOptions, OnyxKey} from 'react-native-onyx'; import CONST from '@src/CONST'; import * as Session from '@src/libs/actions/Session'; import HttpUtils from '@src/libs/HttpUtils'; @@ -248,33 +247,5 @@ const createAddListenerMock = () => { return {triggerTransitionEnd, addListener}; }; -/** - * Get an Onyx value. Only for use in tests for now. - */ -async function onyxGet(key: OnyxKey): Promise>['callback']>[0]> { - return new Promise((resolve) => { - // eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs - // @ts-expect-error This does not need more strict type checking as it's only for tests - const connectionID = Onyx.connect({ - key, - callback: (value) => { - Onyx.disconnect(connectionID); - resolve(value); - }, - waitForCollectionCallback: true, - }); - }); -} - export type {MockFetch, FormData}; -export { - assertFormDataMatchesObject, - buildPersonalDetails, - buildTestReportComment, - createAddListenerMock, - getGlobalFetchMock, - setPersonalDetails, - signInWithTestUser, - signOutTestUser, - onyxGet, -}; +export {assertFormDataMatchesObject, buildPersonalDetails, buildTestReportComment, createAddListenerMock, getGlobalFetchMock, setPersonalDetails, signInWithTestUser, signOutTestUser}; From eb8b251e6874019aa2a553b96bcc8b93eedc164b Mon Sep 17 00:00:00 2001 From: OSBotify Date: Mon, 1 Jul 2024 23:46:37 +0000 Subject: [PATCH 29/29] Update version to 9.0.3-5 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index edbd22f2a3b3..0f69346a44bc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009000304 - versionName "9.0.3-4" + versionCode 1009000305 + versionName "9.0.3-5" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ecbe190ba247..ad8cc982e052 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.3.4 + 9.0.3.5 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index a22929abb60d..8fc553fe8c0c 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.3.4 + 9.0.3.5 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index cd0698dfaba0..83e4d904584b 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.3 CFBundleVersion - 9.0.3.4 + 9.0.3.5 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index d4feb1d0a3cc..e9cc310d29e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.3-4", + "version": "9.0.3-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.3-4", + "version": "9.0.3-5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e46a940c396f..e5e8f8fa8e60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.3-4", + "version": "9.0.3-5", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",