From b6cad2e39e332127b7ef52089be6dcd4f8bcc021 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 21 Aug 2025 11:57:26 +0700 Subject: [PATCH 001/208] fix wrong reply count when deleting multilevel thread --- src/libs/ReportUtils.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 32c59bf33754..26122a783e1f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5833,7 +5833,7 @@ function buildOptimisticAddCommentReportAction( * @param type - The type of action in the child report */ -function updateOptimisticParentReportAction(parentReportAction: OnyxEntry, lastVisibleActionCreated: string, type: string): UpdateOptimisticParentReportAction { +function updateOptimisticParentReportAction(parentReportAction: OnyxEntry, lastVisibleActionCreated: string, type: string, deleteBy = 1): UpdateOptimisticParentReportAction { let childVisibleActionCount = parentReportAction?.childVisibleActionCount ?? 0; let childCommenterCount = parentReportAction?.childCommenterCount ?? 0; let childOldestFourAccountIDs = parentReportAction?.childOldestFourAccountIDs; @@ -5851,7 +5851,7 @@ function updateOptimisticParentReportAction(parentReportAction: OnyxEntry 0) { - childVisibleActionCount -= 1; + childVisibleActionCount -= deleteBy; } if (childVisibleActionCount === 0) { @@ -9778,6 +9778,7 @@ function getOptimisticDataForParentReportAction(report: Report | undefined, last const ancestors = getAllAncestorReportActionIDs(report, true); const totalAncestor = ancestors.reportIDs.length; + let previousActionDeleted = false; return Array.from(Array(totalAncestor), (_, index) => { const ancestorReport = getReportOrDraftReport(ancestors.reportIDs.at(index)); @@ -9790,12 +9791,15 @@ function getOptimisticDataForParentReportAction(report: Report | undefined, last if (!ancestorReportAction?.reportActionID || isEmptyObject(ancestorReportAction)) { return null; } + const updatedReportAction = updateOptimisticParentReportAction(ancestorReportAction, lastVisibleActionCreated, type, previousActionDeleted ? index + 1: undefined); + + previousActionDeleted = isDeletedAction(ancestorReportAction) && updatedReportAction.childVisibleActionCount === 0; return { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${ancestorReport.reportID}`, value: { - [ancestorReportAction.reportActionID]: updateOptimisticParentReportAction(ancestorReportAction, lastVisibleActionCreated, type), + [ancestorReportAction.reportActionID]: updatedReportAction, }, }; }); From 909c6119700d0d9206aad6e58c0d1c91cbfa77bf Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 21 Aug 2025 12:09:36 +0700 Subject: [PATCH 002/208] prettier --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 26122a783e1f..ffc8d4522e12 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -9791,7 +9791,7 @@ function getOptimisticDataForParentReportAction(report: Report | undefined, last if (!ancestorReportAction?.reportActionID || isEmptyObject(ancestorReportAction)) { return null; } - const updatedReportAction = updateOptimisticParentReportAction(ancestorReportAction, lastVisibleActionCreated, type, previousActionDeleted ? index + 1: undefined); + const updatedReportAction = updateOptimisticParentReportAction(ancestorReportAction, lastVisibleActionCreated, type, previousActionDeleted ? index + 1 : undefined); previousActionDeleted = isDeletedAction(ancestorReportAction) && updatedReportAction.childVisibleActionCount === 0; From cc6236415a6a5733cfcba59627c98aaee820aced Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 15 Sep 2025 21:15:20 +0530 Subject: [PATCH 003/208] Refactored usage of ONYXKEYS.PERSONAL_DETAILS_LIST from Member Actions --- src/libs/actions/IOU.ts | 2 +- src/libs/actions/Policy/Member.ts | 79 +++----- src/pages/workspace/WorkspaceMembersPage.tsx | 177 +++++------------- .../members/WorkspaceMemberDetailsPage.tsx | 10 +- tests/actions/PolicyMemberTest.ts | 14 +- 5 files changed, 91 insertions(+), 191 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 81fc9031992d..eed6b66fbc6f 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5499,7 +5499,7 @@ function shareTrackedExpense(trackedExpenseParams: TrackedExpenseParams) { optimisticData: addAccountantToWorkspaceOptimisticData, successData: addAccountantToWorkspaceSuccessData, failureData: addAccountantToWorkspaceFailureData, - } = buildUpdateWorkspaceMembersRoleOnyxData(policyID, [accountantAccountID], CONST.POLICY.ROLE.ADMIN); + } = buildUpdateWorkspaceMembersRoleOnyxData(policyID, [accountantEmail], [accountantAccountID], CONST.POLICY.ROLE.ADMIN); optimisticData?.push(...addAccountantToWorkspaceOptimisticData); successData?.push(...addAccountantToWorkspaceSuccessData); failureData?.push(...addAccountantToWorkspaceFailureData); diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index 438095113b75..d7e965e9daee 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -28,17 +28,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as FormActions from '@userActions/FormActions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type { - ImportedSpreadsheetMemberData, - InvitedEmailsToAccountIDs, - PersonalDetailsList, - Policy, - PolicyEmployee, - PolicyOwnershipChangeChecks, - Report, - ReportAction, - ReportActions, -} from '@src/types/onyx'; +import type {ImportedSpreadsheetMemberData, InvitedEmailsToAccountIDs, Policy, PolicyEmployee, PolicyOwnershipChangeChecks, Report, ReportAction, ReportActions} from '@src/types/onyx'; import type {PendingAction} from '@src/types/onyx/OnyxCommon'; import type {JoinWorkspaceResolution} from '@src/types/onyx/OriginalMessage'; import type {ApprovalRule} from '@src/types/onyx/Policy'; @@ -54,7 +44,6 @@ type OnyxDataReturnType = { }; type WorkspaceMembersRoleData = { - accountID: number; email: string; role: ValueOf; }; @@ -109,12 +98,6 @@ Onyx.connect({ }, }); -let allPersonalDetails: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => (allPersonalDetails = val), -}); - let policyOwnershipChecks: Record; Onyx.connect({ key: ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, @@ -133,12 +116,6 @@ function isApprover(policy: OnyxEntry, employeeLogin: string) { ); } -/** Temporary function alias for isApprover with employeeAccountID */ -function isApproverTemp(policy: OnyxEntry, employeeAccountID: number) { - const employeeLogin = allPersonalDetails?.[employeeAccountID]?.login; - return isApprover(policy, employeeLogin ?? ''); -} - /** * Returns the policy of the report * @deprecated Get the data straight from Onyx - This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 @@ -430,20 +407,19 @@ function resetAccountingPreferredExporter(policyID: string, loginList: string[]) * Remove the passed members from the policy employeeList * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ -function removeMembers(accountIDs: number[], policyID: string) { - // In case user selects only themselves (admin), their email will be filtered out and the members - // array passed will be empty, prevent the function from proceeding in that case as there is no one to remove - if (accountIDs.length === 0) { +function removeMembers(policyID: string, emailList: string[], policyMemberEmailsToAccountIDs: Record) { + if (emailList.length === 0) { return; } + const accountIDs = emailList.map((email) => policyMemberEmailsToAccountIDs[email]).filter((id) => id !== undefined); + const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const; // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 // eslint-disable-next-line deprecation/deprecation const policy = getPolicy(policyID); const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); - const emailList = accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).filter((login) => !!login) as string[]; const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy?.name ?? '', CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY), ); @@ -453,11 +429,12 @@ function removeMembers(accountIDs: number[], policyID: string) { CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, policy?.id, policy?.name ?? '', - accountIDs.filter((accountID) => { - const login = allPersonalDetails?.[accountID]?.login; - const role = login ? policy?.employeeList?.[login]?.role : ''; - return role === CONST.POLICY.ROLE.ADMIN || role === CONST.POLICY.ROLE.AUDITOR; - }), + emailList + .filter((login) => { + const role = login ? policy?.employeeList?.[login]?.role : ''; + return role === CONST.POLICY.ROLE.ADMIN || role === CONST.POLICY.ROLE.AUDITOR; + }) + .map((login) => policyMemberEmailsToAccountIDs[login]), ); const preferredExporterOnyxData = resetAccountingPreferredExporter(policyID, emailList); @@ -678,21 +655,14 @@ function removeMembers(accountIDs: number[], policyID: string) { API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); } -function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, accountIDs: number[], newRole: ValueOf) { +function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, logins: string[], accountIDs: number[], newRole: ValueOf) { const previousEmployeeList = {...allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList}; - const memberRoles: WorkspaceMembersRoleData[] = accountIDs.reduce((result: WorkspaceMembersRoleData[], accountID: number) => { - if (!allPersonalDetails?.[accountID]?.login) { - return result; - } - - result.push({ - accountID, - email: allPersonalDetails?.[accountID]?.login ?? '', + const memberRoles: WorkspaceMembersRoleData[] = logins.map((login: string) => { + return { + email: login, role: newRole, - }); - - return result; - }, []); + }; + }); const optimisticData: OnyxUpdate[] = [ { @@ -790,8 +760,8 @@ function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, accountIDs: n return {optimisticData, successData, failureData, memberRoles}; } -function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newRole: ValueOf) { - const {optimisticData, successData, failureData, memberRoles} = buildUpdateWorkspaceMembersRoleOnyxData(policyID, accountIDs, newRole); +function updateWorkspaceMembersRole(policyID: string, logins: string[], accountIDs: number[], newRole: ValueOf) { + const {optimisticData, successData, failureData, memberRoles} = buildUpdateWorkspaceMembersRoleOnyxData(policyID, logins, accountIDs, newRole); const params: UpdateWorkspaceMembersRoleParams = { policyID, @@ -1148,11 +1118,10 @@ function askToJoinPolicy(policyID: string) { /** * Removes an error after trying to delete a member */ -function clearDeleteMemberError(policyID: string, accountID: number) { - const email = allPersonalDetails?.[accountID]?.login ?? ''; +function clearDeleteMemberError(policyID: string, login: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { employeeList: { - [email]: { + [login]: { pendingAction: null, errors: null, }, @@ -1163,11 +1132,10 @@ function clearDeleteMemberError(policyID: string, accountID: number) { /** * Removes an error after trying to add a member */ -function clearAddMemberError(policyID: string, accountID: number) { - const email = allPersonalDetails?.[accountID]?.login ?? ''; +function clearAddMemberError(policyID: string, login: string, accountID: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { employeeList: { - [email]: null, + [login]: null, }, }); Onyx.merge(`${ONYXKEYS.PERSONAL_DETAILS_LIST}`, { @@ -1390,5 +1358,4 @@ export { clearWorkspaceInviteRoleDraft, setImportedSpreadsheetMemberData, clearImportedSpreadsheetMemberData, - isApproverTemp, }; diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index 13a61fb8d82e..be1c46d598ea 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -3,7 +3,6 @@ import {deepEqual} from 'fast-equals'; import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {TextInput} from 'react-native'; import {InteractionManager, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; @@ -38,7 +37,7 @@ import { clearInviteDraft, clearWorkspaceOwnerChangeFlow, downloadMembersCSV, - isApproverTemp, + isApprover, openWorkspaceMembersPage, removeMembers, updateWorkspaceMembersRole, @@ -50,7 +49,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types'; import {isPersonalDetailsReady, sortAlphabetically} from '@libs/OptionsListUtils'; -import {getAccountIDsByLogins, getDisplayNameOrDefault, getPersonalDetailsByIDs} from '@libs/PersonalDetailsUtils'; +import {getDisplayNameOrDefault, getPersonalDetailsByIDs} from '@libs/PersonalDetailsUtils'; import {getMemberAccountIDsForWorkspace, isDeletedPolicyEmployee, isExpensifyTeam, isPaidGroupPolicy, isPolicyAdmin as isPolicyAdminUtils} from '@libs/PolicyUtils'; import {getDisplayNameForParticipant} from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; @@ -61,8 +60,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {PersonalDetails, PersonalDetailsList, PolicyEmployee, PolicyEmployeeList} from '@src/types/onyx'; -import type {Errors, PendingAction} from '@src/types/onyx/OnyxCommon'; +import type {PersonalDetails, PolicyEmployee, PolicyEmployeeList} from '@src/types/onyx'; +import type {PendingAction} from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import MemberRightIcon from './MemberRightIcon'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; @@ -79,26 +78,14 @@ function invertObject(object: Record): Record { return Object.fromEntries(invertedEntries); } -type MemberOption = Omit & {accountID: number}; +type MemberOption = Omit & {accountID: number; login: string}; function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembersPageProps) { - const {policyMemberEmailsToAccountIDs, employeeListDetails} = useMemo(() => { - const emailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList, true); - const details = Object.keys(policy?.employeeList ?? {}).reduce>((acc, email) => { - const employee = policy?.employeeList?.[email]; - const accountID = emailsToAccountIDs[email]; - if (!employee) { - return acc; - } - acc[accountID] = employee; - return acc; - }, {}); - return {policyMemberEmailsToAccountIDs: emailsToAccountIDs, employeeListDetails: details}; - }, [policy?.employeeList]); + const policyMemberEmailsToAccountIDs = useMemo(() => getMemberAccountIDsForWorkspace(policy?.employeeList, true), [policy?.employeeList]); + const employeeListDetails = useMemo(() => policy?.employeeList ?? ({} as PolicyEmployeeList), [policy?.employeeList]); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const styles = useThemeStyles(); const [removeMembersConfirmModalVisible, setRemoveMembersConfirmModalVisible] = useState(false); - const [errors, setErrors] = useState({}); const {isOffline} = useNetwork(); const prevIsOffline = usePrevious(isOffline); const accountIDs = useMemo(() => Object.values(policyMemberEmailsToAccountIDs ?? {}).map((accountID) => Number(accountID)), [policyMemberEmailsToAccountIDs]); @@ -107,22 +94,20 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers const [isOfflineModalVisible, setIsOfflineModalVisible] = useState(false); const [isDownloadFailureModalVisible, setIsDownloadFailureModalVisible] = useState(false); const isOfflineAndNoMemberDataAvailable = isEmptyObject(policy?.employeeList) && isOffline; - const prevPersonalDetails = usePrevious(personalDetails); const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const {isAccountLocked, showLockedAccountModal} = useContext(LockedAccountContext); const filterEmployees = useCallback( - (employee?: PolicyEmployee) => { + (employee: PolicyEmployee | undefined) => { if (!employee?.email) { return false; } - const employeeAccountID = getAccountIDsByLogins([employee.email]).at(0); - if (!employeeAccountID) { + if (employee.email === policy?.owner || employee.email === currentUserPersonalDetails.login) { return false; } - const isPendingDelete = employeeListDetails?.[employeeAccountID]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; - return accountIDs.includes(employeeAccountID) && !isPendingDelete; + const isPendingDelete = employee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + return !isPendingDelete; }, - [accountIDs, employeeListDetails], + [currentUserPersonalDetails.login, policy?.owner], ); const [selectedEmployees, setSelectedEmployees] = useFilteredSelection(employeeListDetails, filterEmployees); @@ -158,29 +143,20 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers const canSelectMultiple = isPolicyAdmin && (shouldUseNarrowLayout ? isMobileSelectionModeEnabled : true); const confirmModalPrompt = useMemo(() => { - const approverAccountID = selectedEmployees.find((selectedEmployee) => isApproverTemp(policy, selectedEmployee)); - if (!approverAccountID) { + const approverEmail = selectedEmployees.find((selectedEmployee) => isApprover(policy, selectedEmployee)); + if (!approverEmail) { + const firstSelectedEmployeeAccountID = policyMemberEmailsToAccountIDs[selectedEmployees[0]]; return translate('workspace.people.removeMembersPrompt', { count: selectedEmployees.length, - memberName: formatPhoneNumber(getPersonalDetailsByIDs({accountIDs: selectedEmployees, currentUserAccountID}).at(0)?.displayName ?? ''), + memberName: formatPhoneNumber(getPersonalDetailsByIDs({accountIDs: [firstSelectedEmployeeAccountID], currentUserAccountID}).at(0)?.displayName ?? ''), }); } + const approverAccountID = policyMemberEmailsToAccountIDs[approverEmail]; return translate('workspace.people.removeMembersWarningPrompt', { memberName: getDisplayNameForParticipant({accountID: approverAccountID}), ownerName: getDisplayNameForParticipant({accountID: policy?.ownerAccountID}), }); - }, [selectedEmployees, translate, policy, currentUserAccountID, formatPhoneNumber]); - /** - * Get filtered personalDetails list with current employeeList - */ - const filterPersonalDetails = (members: OnyxEntry, details: OnyxEntry): PersonalDetailsList => - Object.keys(members ?? {}).reduce((acc, key) => { - const memberAccountIdKey = policyMemberEmailsToAccountIDs[key] ?? ''; - if (details?.[memberAccountIdKey]) { - acc[memberAccountIdKey] = details[memberAccountIdKey]; - } - return acc; - }, {} as PersonalDetailsList); + }, [selectedEmployees, policyMemberEmailsToAccountIDs, translate, policy, formatPhoneNumber, currentUserAccountID]); /** * Get members for the current workspace */ @@ -188,51 +164,17 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers openWorkspaceMembersPage(route.params.policyID, Object.keys(getMemberAccountIDsForWorkspace(policy?.employeeList))); }, [route.params.policyID, policy?.employeeList]); - /** - * Check if the current selection includes members that cannot be removed - */ - const validateSelection = useCallback(() => { - const newErrors: Errors = {}; - selectedEmployees.forEach((member) => { - if (member !== policy?.ownerAccountID && member !== session?.accountID) { - return; - } - newErrors[member] = translate('workspace.people.error.cannotRemove'); - }); - setErrors(newErrors); - }, [selectedEmployees, policy?.ownerAccountID, session?.accountID, translate]); - useEffect(() => { getWorkspaceMembers(); }, [getWorkspaceMembers]); useEffect(() => { - validateSelection(); - }, [validateSelection]); - - useEffect(() => { - if (removeMembersConfirmModalVisible && !deepEqual(accountIDs, prevAccountIDs)) { - setRemoveMembersConfirmModalVisible(false); + if (!removeMembersConfirmModalVisible || deepEqual(accountIDs, prevAccountIDs)) { + return; } - setSelectedEmployees((prevSelectedEmployees) => { - // Filter all personal details in order to use the elements needed for the current workspace - const currentPersonalDetails = filterPersonalDetails(policy?.employeeList ?? {}, personalDetails); - // We need to filter the previous selected employees by the new personal details, since unknown/new user id's change when transitioning from offline to online - const prevSelectedElements = prevSelectedEmployees.map((id) => { - const prevItem = prevPersonalDetails?.[id]; - const res = Object.values(currentPersonalDetails).find((item) => prevItem?.login === item?.login); - return res?.accountID ?? id; - }); - - const currentSelectedElements = Object.entries(getMemberAccountIDsForWorkspace(policy?.employeeList)) - .filter((employee) => policy?.employeeList?.[employee[0]]?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) - .map((employee) => employee[1]); - - // This is an equivalent of the lodash intersection function. The reduce method below is used to filter the items that exist in both arrays. - return [prevSelectedElements, currentSelectedElements].reduce((prev, members) => prev.filter((item) => members.includes(item))); - }); + setRemoveMembersConfirmModalVisible(false); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [policy?.employeeList, policyMemberEmailsToAccountIDs]); + }, [accountIDs]); useEffect(() => { const isReconnecting = prevIsOffline && !isOffline; @@ -259,19 +201,13 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ const removeUsers = () => { - if (!isEmptyObject(errors)) { - return; - } - - // Remove the admin from the list - const accountIDsToRemove = session?.accountID ? selectedEmployees.filter((id) => id !== session.accountID) : selectedEmployees; - - // Check if any of the account IDs are approvers - const hasApprovers = accountIDsToRemove.some((accountID) => isApproverTemp(policy, accountID)); + // Check if any of the members are approvers + const hasApprovers = selectedEmployees.some((email) => isApprover(policy, email)); if (hasApprovers) { const ownerEmail = ownerDetails.login; - accountIDsToRemove.forEach((accountID) => { + selectedEmployees.forEach((login) => { + const accountID = policyMemberEmailsToAccountIDs[login]; const removedApprover = personalDetails?.[accountID]; if (!removedApprover?.login || !ownerEmail) { return; @@ -295,7 +231,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers setRemoveMembersConfirmModalVisible(false); InteractionManager.runAfterInteractions(() => { setSelectedEmployees([]); - removeMembers(accountIDsToRemove, route.params.policyID); + removeMembers(policyID, selectedEmployees, policyMemberEmailsToAccountIDs); }); }; @@ -303,9 +239,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers * Show the modal to confirm removal of the selected members */ const askForConfirmationToRemove = () => { - if (!isEmptyObject(errors)) { - return; - } setRemoveMembersConfirmModalVisible(true); }; @@ -314,46 +247,42 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers */ const toggleAllUsers = (memberList: MemberOption[]) => { const enabledAccounts = memberList.filter((member) => !member.isDisabled && !member.isDisabledCheckbox); - const someSelected = enabledAccounts.some((member) => selectedEmployees.includes(member.accountID)); + const someSelected = selectedEmployees.length > 0; if (someSelected) { setSelectedEmployees([]); } else { - const everyAccountId = enabledAccounts.map((member) => member.accountID); - setSelectedEmployees(everyAccountId); + const everyLogin = enabledAccounts.map((member) => member.login); + setSelectedEmployees(everyLogin); } - - validateSelection(); }; /** * Add user from the selectedEmployees list */ const addUser = useCallback( - (accountID: number) => { - setSelectedEmployees((prevSelected) => [...prevSelected, accountID]); - validateSelection(); + (login: string) => { + setSelectedEmployees((prevSelected) => [...prevSelected, login]); }, - [validateSelection, setSelectedEmployees], + [setSelectedEmployees], ); /** * Remove user from the selectedEmployees list */ const removeUser = useCallback( - (accountID: number) => { - setSelectedEmployees((prevSelected) => prevSelected.filter((id) => id !== accountID)); - validateSelection(); + (login: string) => { + setSelectedEmployees((prevSelected) => prevSelected.filter((email) => email !== login)); }, - [validateSelection, setSelectedEmployees], + [setSelectedEmployees], ); /** * Toggle user from the selectedEmployees list */ const toggleUser = useCallback( - (accountID: number, pendingAction?: PendingAction) => { - if (accountID === policy?.ownerAccountID && accountID !== session?.accountID) { + (login: string, pendingAction?: PendingAction) => { + if (login === policy?.owner && login !== currentUserPersonalDetails.login) { return; } @@ -362,13 +291,13 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers } // Add or remove the user if the checkbox is enabled - if (selectedEmployees.includes(accountID)) { - removeUser(accountID); + if (selectedEmployees.includes(login)) { + removeUser(login); } else { - addUser(accountID); + addUser(login); } }, - [selectedEmployees, addUser, removeUser, policy?.ownerAccountID, session?.accountID], + [policy?.owner, currentUserPersonalDetails.login, selectedEmployees, removeUser, addUser], ); /** Opens the member details page */ @@ -390,9 +319,9 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers const dismissError = useCallback( (item: MemberOption) => { if (item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - clearDeleteMemberError(route.params.policyID, item.accountID); + clearDeleteMemberError(route.params.policyID, item.login); } else { - clearAddMemberError(route.params.policyID, item.accountID); + clearAddMemberError(route.params.policyID, item.login, item.accountID); } }, [route.params.policyID], @@ -431,6 +360,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers result.push({ keyForList: String(accountID), accountID, + login: details.login ?? '', isDisabledCheckbox: !(isPolicyAdmin && accountID !== policy?.ownerAccountID && accountID !== session?.accountID), isDisabled: isPendingDeleteOrError, isInteractive: !details.isOptimisticPersonalDetail, @@ -552,17 +482,14 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers }; const changeUserRole = (role: ValueOf) => { - if (!isEmptyObject(errors)) { - return; - } - - const accountIDsToUpdate = selectedEmployees.filter((accountID) => { - const email = personalDetails?.[accountID]?.login ?? ''; - return policy?.employeeList?.[email]?.role !== role; + const loginsToUpdate = selectedEmployees.filter((login) => { + return policy?.employeeList?.[login]?.role !== role; }); + const accountIDsToUpdate = loginsToUpdate.map((login) => policyMemberEmailsToAccountIDs[login]).filter((id) => id !== undefined); + setSelectedEmployees([]); - updateWorkspaceMembersRole(route.params.policyID, accountIDsToUpdate, role); + updateWorkspaceMembersRole(route.params.policyID, loginsToUpdate, accountIDsToUpdate, role); }; const getBulkActionsButtonOptions = () => { @@ -800,13 +727,13 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers ListItem={TableListItem} shouldUseDefaultRightHandSideCheckmark={false} turnOnSelectionModeOnLongPress={isPolicyAdmin} - onTurnOnSelectionMode={(item) => item && toggleUser(item?.accountID)} + onTurnOnSelectionMode={(item) => item && toggleUser(item.login)} shouldUseUserSkeletonView disableKeyboardShortcuts={removeMembersConfirmModalVisible} headerMessage={shouldUseNarrowLayout ? headerMessage : undefined} onSelectRow={openMemberDetails} shouldSingleExecuteRowSelect={!isPolicyAdmin} - onCheckboxPress={(item) => toggleUser(item.accountID)} + onCheckboxPress={(item) => toggleUser(item.login)} onSelectAll={filteredData.length > 0 ? () => toggleAllUsers(filteredData) : undefined} onDismissError={dismissError} showLoadingPlaceholder={isLoading} diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index c1f01c9e3218..4c66a36653c7 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -177,15 +177,15 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM // Function to remove a member and close the modal const removeMemberAndCloseModal = useCallback(() => { - removeMembers([accountID], policyID); + removeMembers(policyID, [memberLogin], {[memberLogin]: accountID}); const previousEmployeesCount = Object.keys(policy?.employeeList ?? {}).length; const remainingEmployeeCount = previousEmployeesCount - 1; if (remainingEmployeeCount === 1 && policy?.preventSelfApproval) { // We can't let the "Prevent Self Approvals" enabled if there's only one workspace user - setPolicyPreventSelfApproval(route.params.policyID, false); + setPolicyPreventSelfApproval(policyID, false); } setIsRemoveMemberConfirmModalVisible(false); - }, [accountID, policy?.employeeList, policy?.preventSelfApproval, policyID, route.params.policyID]); + }, [accountID, memberLogin, policy?.employeeList, policy?.preventSelfApproval, policyID]); const removeUser = useCallback(() => { const ownerEmail = ownerDetails?.login; @@ -264,9 +264,9 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const changeRole = useCallback( ({value}: ListItemType) => { setIsRoleSelectionModalVisible(false); - updateWorkspaceMembersRole(policyID, [accountID], value); + updateWorkspaceMembersRole(policyID, [memberLogin], [accountID], value); }, - [accountID, policyID], + [accountID, memberLogin, policyID], ); const startChangeOwnershipFlow = useCallback(() => { diff --git a/tests/actions/PolicyMemberTest.ts b/tests/actions/PolicyMemberTest.ts index 92f4fe6e2fec..1691b855d671 100644 --- a/tests/actions/PolicyMemberTest.ts +++ b/tests/actions/PolicyMemberTest.ts @@ -110,7 +110,7 @@ describe('actions/PolicyMember', () => { Onyx.set(`${ONYXKEYS.PERSONAL_DETAILS_LIST}`, {[fakeUser2.accountID]: fakeUser2}); await waitForBatchedUpdates(); // When a user's role is set as admin on a policy - Member.updateWorkspaceMembersRole(fakePolicy.id, [fakeUser2.accountID], CONST.POLICY.ROLE.ADMIN); + Member.updateWorkspaceMembersRole(fakePolicy.id, [fakeUser2.login ?? ''], [fakeUser2.accountID], CONST.POLICY.ROLE.ADMIN); await waitForBatchedUpdates(); await new Promise((resolve) => { const connection = Onyx.connect({ @@ -153,7 +153,7 @@ describe('actions/PolicyMember', () => { }); await waitForBatchedUpdates(); // When an admin is demoted from their admin role to a user role - Member.updateWorkspaceMembersRole(fakePolicy.id, [fakeUser2.accountID], CONST.POLICY.ROLE.USER); + Member.updateWorkspaceMembersRole(fakePolicy.id, [fakeUser2.login ?? ''], [fakeUser2.accountID], CONST.POLICY.ROLE.USER); await waitForBatchedUpdates(); await new Promise((resolve) => { const connection = Onyx.connect({ @@ -459,7 +459,12 @@ describe('actions/PolicyMember', () => { // When removing am admin, auditor, and user members mockFetch?.pause?.(); - Member.removeMembers([adminAccountID, auditorAccountID, userAccountID], policyID); + const memberEmailsToAccountIDs = { + [adminEmail]: adminAccountID, + [auditorEmail]: auditorAccountID, + [userEmail]: userAccountID, + }; + Member.removeMembers(policyID, [adminEmail, auditorEmail, userEmail], memberEmailsToAccountIDs); await waitForBatchedUpdates(); @@ -500,6 +505,7 @@ describe('actions/PolicyMember', () => { const workspaceReportID = '1'; const expenseReportID = '2'; const userAccountID = 1236; + const userEmail = 'user@example.com'; await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${workspaceReportID}`, { ...createRandomReport(Number(workspaceReportID)), @@ -518,7 +524,7 @@ describe('actions/PolicyMember', () => { // When removing a member from the workspace mockFetch?.pause?.(); - Member.removeMembers([userAccountID], policyID); + Member.removeMembers(policyID, [userEmail], {[userEmail]: userAccountID}); await waitForBatchedUpdates(); From bc96bb82961e078267904f9b445bcfe6363b8411 Mon Sep 17 00:00:00 2001 From: Maruf Sharifi Date: Thu, 2 Oct 2025 12:30:38 +0430 Subject: [PATCH 004/208] fix(ios-chat): remove empty space above keyboard after navigating from expense request --- src/pages/home/report/ReportActionsView.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 225575902cdd..a3f81c56521c 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -107,7 +107,7 @@ function ReportActionsView({ const {shouldUseNarrowLayout} = useResponsiveLayout(); const isFocused = useIsFocused(); - const [isNavigatingToLinkedMessage, setNavigatingToLinkedMessage] = useState(!!reportActionID); + const [isNavigatingToLinkedMessage, setNavigatingToLinkedMessage] = useState(false); const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); @@ -271,16 +271,16 @@ function ReportActionsView({ useEffect(() => { let timerID: NodeJS.Timeout; - if (isTheFirstReportActionIsLinked) { + if (!isTheFirstReportActionIsLinked && reportActionID) { setNavigatingToLinkedMessage(true); - } else { // After navigating to the linked reportAction, apply this to correctly set // `autoscrollToTopThreshold` prop when linking to a specific reportAction. - // eslint-disable-next-line deprecation/deprecation InteractionManager.runAfterInteractions(() => { // Using a short delay to ensure the view is updated after interactions timerID = setTimeout(() => setNavigatingToLinkedMessage(false), 10); }); + } else { + setNavigatingToLinkedMessage(false); } return () => { @@ -289,7 +289,7 @@ function ReportActionsView({ } clearTimeout(timerID); }; - }, [isTheFirstReportActionIsLinked]); + }, [isTheFirstReportActionIsLinked, reportActionID]); // Show skeleton while loading initial report actions when data is incomplete/missing and online const shouldShowSkeletonForInitialLoad = isLoadingInitialReportActions && (isReportDataIncomplete || isMissingReportActions) && !isOffline; From c688d8fb5197fbc994205a1cbfbf2c542da44f7e Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Sat, 4 Oct 2025 01:58:23 +0800 Subject: [PATCH 005/208] fix: pinned room shows no acitivity after workspace is deleted --- src/libs/SidebarUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 989d480bbc4e..2f289161f345 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -231,7 +231,7 @@ function getReportsToDisplayInLHN( const reportsToDisplay: ReportsToDisplayInLHN = {}; Object.entries(allReportsDictValues).forEach(([reportID, report]) => { - if (!report) { + if (!report || !report.reportID) { return; } @@ -269,7 +269,7 @@ function updateReportsToDisplayInLHN( const displayedReportsCopy = {...displayedReports}; updatedReportsKeys.forEach((reportID) => { const report = reports?.[reportID]; - if (!report) { + if (!report || !report.reportID) { return; } From 26967ae462a2e5880d5f26bf9ffc9e000624d9f8 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 7 Oct 2025 15:33:20 +0530 Subject: [PATCH 006/208] Fixed some minor bugs --- src/pages/workspace/WorkspaceMembersPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index b8a5a5bbc82e..3be15ef2710d 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -359,7 +359,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers const isPendingDeleteOrError = isPolicyAdmin && (policyEmployee.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !isEmptyObject(policyEmployee.errors)); result.push({ - keyForList: String(accountID), + keyForList: details.login ?? '', accountID, login: details.login ?? '', isDisabledCheckbox: !(isPolicyAdmin && accountID !== policy?.ownerAccountID && accountID !== session?.accountID), @@ -422,7 +422,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers if (isEmptyObject(invitedEmailsToAccountIDsDraft) || accountIDs === prevAccountIDs) { return; } - const invitedEmails = Object.values(invitedEmailsToAccountIDsDraft).map(String); + const invitedEmails = Object.keys(invitedEmailsToAccountIDsDraft); selectionListRef.current?.scrollAndHighlightItem?.(invitedEmails); clearInviteDraft(route.params.policyID); }, [invitedEmailsToAccountIDsDraft, isFocused, accountIDs, prevAccountIDs, route.params.policyID]); @@ -725,7 +725,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers ref={selectionListRef} canSelectMultiple={canSelectMultiple} sections={[{data: filteredData, isDisabled: false}]} - selectedItems={selectedEmployees.map(String)} + selectedItems={selectedEmployees} ListItem={TableListItem} shouldUseDefaultRightHandSideCheckmark={false} turnOnSelectionModeOnLongPress={isPolicyAdmin} From 49e1551af5d8bef9470c2d431783a9bc14f49f26 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 7 Oct 2025 16:28:42 +0530 Subject: [PATCH 007/208] Rename variables --- src/libs/actions/Policy/Member.ts | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index b2a3de5a0511..92b514f9c62c 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -407,12 +407,12 @@ function resetAccountingPreferredExporter(policyID: string, loginList: string[]) * Remove the passed members from the policy employeeList * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ -function removeMembers(policyID: string, emailList: string[], policyMemberEmailsToAccountIDs: Record) { - if (emailList.length === 0) { +function removeMembers(policyID: string, selectedMemberEmails: string[], policyMemberEmailsToAccountIDs: Record) { + if (selectedMemberEmails.length === 0) { return; } - const accountIDs = emailList.map((email) => policyMemberEmailsToAccountIDs[email]).filter((id) => id !== undefined); + const accountIDs = selectedMemberEmails.map((email) => policyMemberEmailsToAccountIDs[email]).filter((id) => id !== undefined); const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const; // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 @@ -429,19 +429,19 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, policy?.id, policy?.name ?? '', - emailList + selectedMemberEmails .filter((login) => { const role = login ? policy?.employeeList?.[login]?.role : ''; return role === CONST.POLICY.ROLE.ADMIN || role === CONST.POLICY.ROLE.AUDITOR; }) .map((login) => policyMemberEmailsToAccountIDs[login]), ); - const preferredExporterOnyxData = resetAccountingPreferredExporter(policyID, emailList); + const preferredExporterOnyxData = resetAccountingPreferredExporter(policyID, selectedMemberEmails); const optimisticMembersState: OnyxCollectionInputValue = {}; const successMembersState: OnyxCollectionInputValue = {}; const failureMembersState: OnyxCollectionInputValue = {}; - emailList.forEach((email) => { + selectedMemberEmails.forEach((email) => { optimisticMembersState[email] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; successMembersState[email] = null; failureMembersState[email] = {errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.people.error.genericRemove')}; @@ -451,7 +451,7 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails const employee = policy?.employeeList?.[employeeEmail]; optimisticMembersState[employeeEmail] = optimisticMembersState[employeeEmail] ?? {}; failureMembersState[employeeEmail] = failureMembersState[employeeEmail] ?? {}; - if (employee?.submitsTo && emailList.includes(employee?.submitsTo)) { + if (employee?.submitsTo && selectedMemberEmails.includes(employee?.submitsTo)) { optimisticMembersState[employeeEmail] = { ...optimisticMembersState[employeeEmail], submitsTo: policy?.owner, @@ -461,7 +461,7 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails submitsTo: employee?.submitsTo, }; } - if (employee?.forwardsTo && emailList.includes(employee?.forwardsTo)) { + if (employee?.forwardsTo && selectedMemberEmails.includes(employee?.forwardsTo)) { optimisticMembersState[employeeEmail] = { ...optimisticMembersState[employeeEmail], forwardsTo: policy?.owner, @@ -471,7 +471,7 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails forwardsTo: employee?.forwardsTo, }; } - if (employee?.overLimitForwardsTo && emailList.includes(employee?.overLimitForwardsTo)) { + if (employee?.overLimitForwardsTo && selectedMemberEmails.includes(employee?.overLimitForwardsTo)) { optimisticMembersState[employeeEmail] = { ...optimisticMembersState[employeeEmail], overLimitForwardsTo: policy?.owner, @@ -484,7 +484,7 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails }); const approvalRules: ApprovalRule[] = policy?.rules?.approvalRules ?? []; - const optimisticApprovalRules = approvalRules.filter((rule) => !emailList.includes(rule?.approver ?? '')); + const optimisticApprovalRules = approvalRules.filter((rule) => !selectedMemberEmails.includes(rule?.approver ?? '')); const optimisticData: OnyxUpdate[] = [ { @@ -492,7 +492,7 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails key: policyKey, value: { employeeList: optimisticMembersState, - approver: emailList.includes(policy?.approver ?? '') ? policy?.owner : policy?.approver, + approver: selectedMemberEmails.includes(policy?.approver ?? '') ? policy?.owner : policy?.approver, rules: { ...(policy?.rules ?? {}), approvalRules: optimisticApprovalRules, @@ -607,7 +607,7 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails if (!isEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically const employeeListEmails = Object.keys(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList ?? {}); - const remainingLogins = employeeListEmails.filter((email) => !emailList.includes(email)); + const remainingLogins = employeeListEmails.filter((email) => !selectedMemberEmails.includes(email)); const invitedPrimaryToSecondaryLogins: Record = {}; if (policy?.primaryLoginsInvited) { @@ -648,16 +648,16 @@ function removeMembers(policyID: string, emailList: string[], policyMemberEmails }); const params: DeleteMembersFromWorkspaceParams = { - emailList: emailList.join(','), + emailList: selectedMemberEmails.join(','), policyID, }; API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); } -function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, logins: string[], accountIDs: number[], newRole: ValueOf) { +function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, selectedMemberEmails: string[], selectedMemberAccountIDs: number[], newRole: ValueOf) { const previousEmployeeList = {...allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList}; - const memberRoles: WorkspaceMembersRoleData[] = logins.map((login: string) => { + const memberRoles: WorkspaceMembersRoleData[] = selectedMemberEmails.map((login: string) => { return { email: login, role: newRole, @@ -724,7 +724,7 @@ function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, logins: strin const failureDataParticipants: Record = {...adminRoom.participants}; const optimisticParticipants: Record = {}; if (newRole === CONST.POLICY.ROLE.ADMIN || newRole === CONST.POLICY.ROLE.AUDITOR) { - accountIDs.forEach((accountID) => { + selectedMemberAccountIDs.forEach((accountID) => { if (adminRoom?.participants?.[accountID]) { return; } @@ -732,7 +732,7 @@ function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, logins: strin failureDataParticipants[accountID] = null; }); } else { - accountIDs.forEach((accountID) => { + selectedMemberAccountIDs.forEach((accountID) => { if (!adminRoom?.participants?.[accountID]) { return; } @@ -760,8 +760,8 @@ function buildUpdateWorkspaceMembersRoleOnyxData(policyID: string, logins: strin return {optimisticData, successData, failureData, memberRoles}; } -function updateWorkspaceMembersRole(policyID: string, logins: string[], accountIDs: number[], newRole: ValueOf) { - const {optimisticData, successData, failureData, memberRoles} = buildUpdateWorkspaceMembersRoleOnyxData(policyID, logins, accountIDs, newRole); +function updateWorkspaceMembersRole(policyID: string, selectedMemberEmails: string[], selectedMemberAccountIDs: number[], newRole: ValueOf) { + const {optimisticData, successData, failureData, memberRoles} = buildUpdateWorkspaceMembersRoleOnyxData(policyID, selectedMemberEmails, selectedMemberAccountIDs, newRole); const params: UpdateWorkspaceMembersRoleParams = { policyID, From 45d8d87557bb33fbfbdd410dbfac4a82ca83abc0 Mon Sep 17 00:00:00 2001 From: Yauheni Horbach Date: Wed, 8 Oct 2025 13:55:13 +0200 Subject: [PATCH 008/208] Add new command for revert and update getChildTransactions --- src/components/MoneyReportHeader.tsx | 4 +- src/components/MoneyRequestHeader.tsx | 4 +- .../ReportActionItem/MoneyRequestView.tsx | 8 +- .../API/parameters/SplitTransactionParams.ts | 10 +- src/libs/API/parameters/index.ts | 2 +- src/libs/API/types.ts | 2 + src/libs/TransactionUtils/index.ts | 6 +- src/libs/actions/IOU.ts | 60 ++++--- src/pages/iou/SplitExpenseEditPage.tsx | 5 +- src/pages/iou/SplitExpensePage.tsx | 24 +-- tests/actions/IOUTest.ts | 158 +++++++++++++++++- 11 files changed, 217 insertions(+), 66 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index f4d49e371347..754434dc2947 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -201,6 +201,8 @@ function MoneyReportHeader({ const [lastDistanceExpenseType] = useOnyx(ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE, {canBeMissing: true}); const exportTemplates = useMemo(() => getExportTemplates(integrationsExportTemplates ?? [], csvExportLayouts ?? {}, policy), [integrationsExportTemplates, csvExportLayouts, policy]); const {isBetaEnabled} = usePermissions(); + const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false}); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); const requestParentReportAction = useMemo(() => { if (!reportActions || !transactionThreadReport?.parentReportActionID) { @@ -1008,7 +1010,7 @@ function MoneyReportHeader({ } const currentTransaction = transactions.at(0); - initSplitExpense(currentTransaction); + initSplitExpense(allTransactions, allReports, currentTransaction); }, }, [CONST.REPORT.SECONDARY_ACTIONS.MERGE]: { diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index e20130bc8c1e..bdcbcefa23bd 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -113,6 +113,8 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre const reportID = report?.reportID; const {removeTransaction} = useSearchContext(); const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction); + const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false}); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext); const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP; @@ -309,7 +311,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre icon: Expensicons.ArrowSplit, value: CONST.REPORT.SECONDARY_ACTIONS.SPLIT, onSelected: () => { - initSplitExpense(transaction); + initSplitExpense(allTransactions, allReports, transaction); }, }, [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MERGE]: { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index bc0d0dfc8685..7c95fab1a822 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -132,6 +132,8 @@ function MoneyRequestView({ const {translate, toLocaleDigit} = useLocalize(); const {getReportRHPActiveRoute} = useActiveRoute(); + const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false}); + const parentReportID = report?.parentReportID; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`]; const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, { @@ -454,7 +456,7 @@ function MoneyRequestView({ } if (isExpenseSplit && isBetaEnabled(CONST.BETAS.NEWDOT_UPDATE_SPLITS)) { - initSplitExpense(transaction); + initSplitExpense(allTransactions, allReports, transaction); return; } @@ -485,7 +487,7 @@ function MoneyRequestView({ } if (isExpenseSplit && isBetaEnabled(CONST.BETAS.NEWDOT_UPDATE_SPLITS)) { - initSplitExpense(transaction); + initSplitExpense(allTransactions, allReports, transaction); return; } @@ -634,7 +636,7 @@ function MoneyRequestView({ } if (isExpenseSplit && isBetaEnabled(CONST.BETAS.NEWDOT_UPDATE_SPLITS)) { - initSplitExpense(transaction); + initSplitExpense(allTransactions, allReports, transaction); return; } diff --git a/src/libs/API/parameters/SplitTransactionParams.ts b/src/libs/API/parameters/SplitTransactionParams.ts index 8b611cd8d038..7668b1b5f268 100644 --- a/src/libs/API/parameters/SplitTransactionParams.ts +++ b/src/libs/API/parameters/SplitTransactionParams.ts @@ -1,7 +1,6 @@ import type {Comment} from '@src/types/onyx/Transaction'; -type SplitTransactionSplitsParam = Array<{ - amount: number; +type SplitTransactionSplitParam = { transactionID: string; category?: string; tag?: string; @@ -15,11 +14,14 @@ type SplitTransactionSplitsParam = Array<{ reimbursable?: boolean; billable?: boolean; reportID?: string; -}>; +}; + +type SplitTransactionSplitsParam = SplitTransactionSplitParam[]; type SplitTransactionParams = { transactionID: string; [key: string]: string | boolean; }; -export type {SplitTransactionParams, SplitTransactionSplitsParam}; +type RevertSplitTransactionParams = SplitTransactionSplitParam & { comment?: string}; +export type {SplitTransactionParams, SplitTransactionSplitsParam, RevertSplitTransactionParams}; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 287e150bf990..03b0bb187c83 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -172,7 +172,7 @@ export type {default as CompleteSplitBillParams} from './CompleteSplitBillParams export type {default as UpdateMoneyRequestParams} from './UpdateMoneyRequestParams'; export type {default as RequestMoneyParams} from './RequestMoneyParams'; export type {default as SplitBillParams} from './SplitBillParams'; -export type {SplitTransactionParams, SplitTransactionSplitsParam} from './SplitTransactionParams'; +export type {SplitTransactionParams, SplitTransactionSplitsParam, RevertSplitTransactionParams} from './SplitTransactionParams'; export type {default as DeleteMoneyRequestParams} from './DeleteMoneyRequestParams'; export type {default as CreateDistanceRequestParams} from './CreateDistanceRequestParams'; export type {default as StartSplitBillParams} from './StartSplitBillParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index cfe596d190ff..589fe499e9d2 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -213,6 +213,7 @@ const WRITE_COMMANDS = { SPLIT_BILL_AND_OPEN_REPORT: 'SplitBillAndOpenReport', UPDATE_SPLIT_TRANSACTION: 'UpdateSplitTransaction', SPLIT_TRANSACTION: 'Transaction_Split', + REVERT_SPLIT_TRANSACTION: 'RevertSplitTransaction', DELETE_MONEY_REQUEST: 'DeleteMoneyRequest', REJECT_MONEY_REQUEST: 'RejectMoneyRequest', MARK_TRANSACTION_VIOLATION_AS_RESOLVED: 'MarkTransactionViolationAsResolved', @@ -710,6 +711,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SPLIT_BILL]: Parameters.SplitBillParams; [WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT]: Parameters.SplitBillParams; [WRITE_COMMANDS.SPLIT_TRANSACTION]: Parameters.SplitTransactionParams; + [WRITE_COMMANDS.REVERT_SPLIT_TRANSACTION]: Parameters.RevertSplitTransactionParams; [WRITE_COMMANDS.UPDATE_SPLIT_TRANSACTION]: Parameters.SplitTransactionParams; [WRITE_COMMANDS.DELETE_MONEY_REQUEST]: Parameters.DeleteMoneyRequestParams; [WRITE_COMMANDS.REJECT_MONEY_REQUEST]: Parameters.RejectMoneyRequestParams; diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 5bfa24ba34a8..1a94bc13f008 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1927,9 +1927,9 @@ function isTransactionPendingDelete(transaction: OnyxEntry): boolea /** * Retrieves all “child” transactions associated with a given original transaction */ -function getChildTransactions(originalTransactionID: string | undefined) { - return Object.values(allTransactions ?? {}).filter((currentTransaction) => { - const currentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${currentTransaction?.reportID}`]; +function getChildTransactions(transactions: OnyxCollection, reports: OnyxCollection, originalTransactionID: string | undefined) { + return Object.values(transactions ?? {}).filter((currentTransaction) => { + const currentReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${currentTransaction?.reportID}`]; return ( currentTransaction?.comment?.originalTransactionID === originalTransactionID && !!currentReport && diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 6d6e0c76f204..9d53b10227ac 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -33,6 +33,7 @@ import type { RequestMoneyParams, ResolveDuplicatesParams, RetractReportParams, + RevertSplitTransactionParams, SendInvoiceParams, SendMoneyParams, SetNameValuePairParams, @@ -12796,7 +12797,7 @@ function initSplitExpenseItemData( /** * Create a draft transaction to set up split expense details for the split expense flow */ -function initSplitExpense(transaction: OnyxEntry) { +function initSplitExpense(transactions: OnyxCollection, reports: OnyxCollection, transaction: OnyxEntry) { if (!transaction) { return; } @@ -12806,9 +12807,9 @@ function initSplitExpense(transaction: OnyxEntry) { const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction); if (isExpenseSplit) { const originalTransactionID = transaction.comment?.originalTransactionID; - const originalTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]; + const originalTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]; - const relatedTransactions = getChildTransactions(originalTransactionID); + const relatedTransactions = getChildTransactions(transactions, reports, originalTransactionID); const transactionDetails = getTransactionDetails(originalTransaction); const splitExpenses = relatedTransactions.map((currentTransaction) => initSplitExpenseItemData(currentTransaction)); @@ -13002,6 +13003,8 @@ function clearSplitTransactionDraftErrors(transactionID: string | undefined) { } function saveSplitTransactions( + transactions: OnyxCollection, + reports: OnyxCollection, draftTransaction: OnyxEntry, hash: number, policyCategories: OnyxTypes.PolicyCategories | undefined, @@ -13012,7 +13015,7 @@ function saveSplitTransactions( const expenseReport = transactionReport?.type === CONST.REPORT.TYPE.EXPENSE ? transactionReport : parentTransactionReport; const originalTransactionID = draftTransaction?.comment?.originalTransactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID; - const originalTransaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]; + const originalTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]; const originalTransactionDetails = getTransactionDetails(originalTransaction); const iouActions = getIOUActionForTransactions([originalTransactionID], expenseReport?.reportID); @@ -13021,13 +13024,13 @@ function saveSplitTransactions( const splitExpenses = draftTransaction?.comment?.splitExpenses ?? []; // List of all child transactions that have been created after split - const originalChildTransactions = getChildTransactions(originalTransactionID); - const isCreationOfSplits = originalChildTransactions.length === 0; + const originalChildTransactions = getChildTransactions(transactions, reports, originalTransactionID); const processedChildTransactionIDs: string[] = []; const reportTotal = transactionReport?.total ?? 0; const splitExpensesTotal = draftTransaction?.comment?.splitExpensesTotal ?? 0; + const isCreationOfSplits = originalChildTransactions.length === 0; const isReverseSplitOperation = splitExpenses.length === 1 && originalChildTransactions.length > 0; let changesInReportTotal = 0; @@ -13072,7 +13075,7 @@ function saveSplitTransactions( splitExpenses.forEach((splitExpense, index) => { const existingTransactionID = isReverseSplitOperation ? originalTransactionID : splitExpense.transactionID; - const splitTransaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${existingTransactionID}`]; + const splitTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${existingTransactionID}`]; if (splitTransaction) { processedChildTransactionIDs.push(splitTransaction.transactionID); } @@ -13212,7 +13215,7 @@ function saveSplitTransactions( (currentTransaction) => !processedChildTransactionIDs.includes(currentTransaction?.transactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID), ); undeletedTransactions.forEach((undeletedTransaction) => { - const splitTransaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${undeletedTransaction?.transactionID}`]; + const splitTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${undeletedTransaction?.transactionID}`]; const splitReportActions = getAllReportActions(splitTransaction?.reportID); const currentReportAction = Object.values(splitReportActions).find((action) => { const transactionID = isMoneyRequestAction(action) ? (getOriginalMessage(action)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID) : CONST.DEFAULT_NUMBER_ID; @@ -13306,24 +13309,33 @@ function saveSplitTransactions( }); } - // Prepare splitApiParams for the Transaction_Split API call which requires a specific format for the splits - // The format is: splits[0][amount], splits[0][category], splits[0][tag] etc. - const splitApiParams = {} as Record; - splits.forEach((split, i) => { - Object.entries(split).forEach(([key, value]) => { - splitApiParams[`splits[${i}][${key}]`] = value !== null && typeof value === 'object' ? JSON.stringify(value) : value; + if (isReverseSplitOperation) { + const parameters = { + ...splits.at(0), + comment: splits.at(0)?.comment?.comment, + } as RevertSplitTransactionParams; + API.write(WRITE_COMMANDS.REVERT_SPLIT_TRANSACTION, parameters, {optimisticData, successData, failureData}); + } else { + // Prepare splitApiParams for the Transaction_Split API call which requires a specific format for the splits + // The format is: splits[0][amount], splits[0][category], splits[0][tag] etc. + const splitApiParams = {} as Record; + splits.forEach((split, i) => { + Object.entries(split).forEach(([key, value]) => { + splitApiParams[`splits[${i}][${key}]`] = value !== null && typeof value === 'object' ? JSON.stringify(value) : value; + }); }); - }); - const parameters: SplitTransactionParams = { - ...splitApiParams, - transactionID: originalTransactionID, - }; + const parameters: SplitTransactionParams = { + ...splitApiParams, + transactionID: originalTransactionID, + }; - if (isCreationOfSplits) { - API.write(WRITE_COMMANDS.SPLIT_TRANSACTION, parameters, {optimisticData, successData, failureData}); - } else { - // eslint-disable-next-line rulesdir/no-multiple-api-calls - API.write(WRITE_COMMANDS.UPDATE_SPLIT_TRANSACTION, parameters, {optimisticData, successData, failureData}); + if (isCreationOfSplits) { + // eslint-disable-next-line rulesdir/no-multiple-api-calls + API.write(WRITE_COMMANDS.SPLIT_TRANSACTION, parameters, {optimisticData, successData, failureData}); + } else { + // eslint-disable-next-line rulesdir/no-multiple-api-calls + API.write(WRITE_COMMANDS.UPDATE_SPLIT_TRANSACTION, parameters, {optimisticData, successData, failureData}); + } } // eslint-disable-next-line deprecation/deprecation InteractionManager.runAfterInteractions(() => removeDraftSplitTransaction(originalTransactionID)); diff --git a/src/pages/iou/SplitExpenseEditPage.tsx b/src/pages/iou/SplitExpenseEditPage.tsx index e3d678772f07..3fd95d6d47c1 100644 --- a/src/pages/iou/SplitExpenseEditPage.tsx +++ b/src/pages/iou/SplitExpenseEditPage.tsx @@ -54,6 +54,9 @@ function SplitExpenseEditPage({route}: SplitExpensePageProps) { const transactionDetails = useMemo>(() => getTransactionDetails(transaction) ?? {}, [transaction]); const transactionDetailsAmount = transactionDetails?.amount ?? 0; + const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false}); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); + const [draftTransactionWithSplitExpenses] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, {canBeMissing: false}); const splitExpensesList = draftTransactionWithSplitExpenses?.comment?.splitExpenses; @@ -68,7 +71,7 @@ function SplitExpenseEditPage({route}: SplitExpensePageProps) { const isSplitAvailable = report && transaction && isSplitAction(report, [transaction], policy); const isCategoryRequired = !!policy?.requiresCategory; - const childTransactions = useMemo(() => getChildTransactions(transactionID), [transactionID]); + const childTransactions = useMemo(() => getChildTransactions(allTransactions, allReports, transactionID), [allReports, allTransactions, transactionID]); const reportName = getReportName(report, policy); const shouldShowTags = !!policy?.areTagsEnabled && !!(transactionTag || hasEnabledTags(policyTagLists)); diff --git a/src/pages/iou/SplitExpensePage.tsx b/src/pages/iou/SplitExpensePage.tsx index 3d987483661a..50002cd67048 100644 --- a/src/pages/iou/SplitExpensePage.tsx +++ b/src/pages/iou/SplitExpensePage.tsx @@ -71,6 +71,7 @@ function SplitExpensePage({route}: SplitExpensePageProps) { const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`, {canBeMissing: false}); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true}); const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false}); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(reportID)}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); @@ -86,7 +87,7 @@ function SplitExpensePage({route}: SplitExpensePageProps) { const isPerDiem = isPerDiemRequest(transaction); const isCard = isCardTransaction(transaction); - const childTransactions = useMemo(() => getChildTransactions(transactionID), [transactionID]); + const childTransactions = useMemo(() => getChildTransactions(allTransactions, allReports, transactionID), [allReports, allTransactions, transactionID]); const splitFieldDataFromChildTransactions = useMemo(() => childTransactions.map((currentTransaction) => initSplitExpenseItemData(currentTransaction)), [childTransactions]); const splitFieldDataFromOriginalTransaction = useMemo(() => initSplitExpenseItemData(transaction), [transaction]); @@ -152,25 +153,8 @@ function SplitExpensePage({route}: SplitExpensePageProps) { return; } - saveSplitTransactions(draftTransaction, currentSearchHash, policyCategories, expenseReportPolicy); - }, [ - splitExpenses, - childTransactions.length, - isBetaEnabled, - draftTransaction, - sumOfSplitExpenses, - transactionDetailsAmount, - isPerDiem, - isCard, - splitFieldDataFromChildTransactions, - currentSearchHash, - policyCategories, - expenseReportPolicy, - splitFieldDataFromOriginalTransaction, - translate, - transactionID, - transactionDetails?.currency, - ]); + saveSplitTransactions(allTransactions, allReports, draftTransaction, currentSearchHash, policyCategories, expenseReportPolicy); + }, [splitExpenses, childTransactions.length, isBetaEnabled, draftTransaction, sumOfSplitExpenses, transactionDetailsAmount, isPerDiem, isCard, splitFieldDataFromChildTransactions, allTransactions, allReports, currentSearchHash, policyCategories, expenseReportPolicy, splitFieldDataFromOriginalTransaction, translate, transactionID, transactionDetails?.currency]); const onSplitExpenseAmountChange = useCallback( (currentItemTransactionID: string, value: number) => { diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 06a2da41f79e..ce551f5e498e 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2701,13 +2701,32 @@ describe('actions/IOU', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, { [iouAction.reportActionID]: iouAction, }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction); const draftTransaction: OnyxEntry = { ...transaction, comment: { originalTransactionID: transaction.transactionID, }, }; - saveSplitTransactions(draftTransaction, 1, undefined, undefined); + + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + saveSplitTransactions(allTransactions, allReports, draftTransaction, 1, undefined, undefined); await waitForBatchedUpdates(); @@ -2757,6 +2776,7 @@ describe('actions/IOU', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, { [iouAction.reportActionID]: iouAction, }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction); const draftTransaction: OnyxEntry = { ...transaction, comment: { @@ -2766,7 +2786,25 @@ describe('actions/IOU', () => { // When splitting the expense const hash = 1; - saveSplitTransactions(draftTransaction, hash, undefined, undefined); + + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + saveSplitTransactions(allTransactions, allReports, draftTransaction, hash, undefined, undefined); await waitForBatchedUpdates(); @@ -2825,6 +2863,7 @@ describe('actions/IOU', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, { [iouAction.reportActionID]: iouAction, }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction); const splitTransactionID1 = '34'; const splitTransactionID2 = '35'; const draftTransaction: OnyxEntry = { @@ -2840,7 +2879,25 @@ describe('actions/IOU', () => { // When splitting the expense const hash = 1; - saveSplitTransactions(draftTransaction, hash, undefined, undefined); + + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + saveSplitTransactions(allTransactions, allReports, draftTransaction, hash, undefined, undefined); await waitForBatchedUpdates(); @@ -6689,7 +6746,24 @@ describe('actions/IOU', () => { reportID: '456', }; - initSplitExpense(transaction); + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + initSplitExpense(allTransactions, allReports, transaction); await waitForBatchedUpdates(); const draftTransaction = await getOnyxValue(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transaction.transactionID}`); @@ -6715,7 +6789,24 @@ describe('actions/IOU', () => { it('should not initialize split expense for null transaction', async () => { const transaction: Transaction | undefined = undefined; - initSplitExpense(transaction); + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + initSplitExpense(allTransactions, allReports, transaction); await waitForBatchedUpdates(); expect(transaction).toBeFalsy(); @@ -6739,7 +6830,24 @@ describe('actions/IOU', () => { reportID: '456', }; - initSplitExpense(transaction); + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + initSplitExpense(allTransactions, allReports, transaction); await waitForBatchedUpdates(); const draftTransaction = await getOnyxValue(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transaction.transactionID}`); @@ -7196,7 +7304,24 @@ describe('actions/IOU', () => { }, }; - saveSplitTransactions(draftTransaction, -2, undefined, undefined); + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + saveSplitTransactions(allTransactions, allReports, draftTransaction, -2, undefined, undefined); await waitForBatchedUpdates(); const split1 = await getOnyxValue(`${ONYXKEYS.COLLECTION.TRANSACTION}235`); @@ -7296,7 +7421,24 @@ describe('actions/IOU', () => { }, }; - saveSplitTransactions(draftTransaction, -2, undefined, undefined); + let allTransactions: OnyxCollection; + let allReports: OnyxCollection; + await getOnyxData({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + allTransactions = value; + }, + }); + await getOnyxData({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + }, + }); + + saveSplitTransactions(allTransactions, allReports, draftTransaction, -2, undefined, undefined); await waitForBatchedUpdates(); const split1 = await getOnyxValue(`${ONYXKEYS.COLLECTION.TRANSACTION}235`); From de9466e8c72d33b68068d44dd4f685aebaf35f4e Mon Sep 17 00:00:00 2001 From: Yauheni Horbach Date: Wed, 8 Oct 2025 14:10:52 +0200 Subject: [PATCH 009/208] Fix prettier issues --- .../API/parameters/SplitTransactionParams.ts | 3 ++- src/libs/actions/IOU.ts | 4 ++-- src/pages/iou/SplitExpensePage.tsx | 21 +++++++++++++++++- tests/actions/IOUTest.ts | 22 +++++++++---------- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/libs/API/parameters/SplitTransactionParams.ts b/src/libs/API/parameters/SplitTransactionParams.ts index 7668b1b5f268..463faa6b1136 100644 --- a/src/libs/API/parameters/SplitTransactionParams.ts +++ b/src/libs/API/parameters/SplitTransactionParams.ts @@ -23,5 +23,6 @@ type SplitTransactionParams = { [key: string]: string | boolean; }; -type RevertSplitTransactionParams = SplitTransactionSplitParam & { comment?: string}; +type RevertSplitTransactionParams = SplitTransactionSplitParam & {comment?: string}; + export type {SplitTransactionParams, SplitTransactionSplitsParam, RevertSplitTransactionParams}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9d53b10227ac..54204aa44b43 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -13004,7 +13004,7 @@ function clearSplitTransactionDraftErrors(transactionID: string | undefined) { function saveSplitTransactions( transactions: OnyxCollection, - reports: OnyxCollection, + reports: OnyxCollection, draftTransaction: OnyxEntry, hash: number, policyCategories: OnyxTypes.PolicyCategories | undefined, @@ -13310,7 +13310,7 @@ function saveSplitTransactions( } if (isReverseSplitOperation) { - const parameters = { + const parameters = { ...splits.at(0), comment: splits.at(0)?.comment?.comment, } as RevertSplitTransactionParams; diff --git a/src/pages/iou/SplitExpensePage.tsx b/src/pages/iou/SplitExpensePage.tsx index 50002cd67048..2c61a130701e 100644 --- a/src/pages/iou/SplitExpensePage.tsx +++ b/src/pages/iou/SplitExpensePage.tsx @@ -154,7 +154,26 @@ function SplitExpensePage({route}: SplitExpensePageProps) { } saveSplitTransactions(allTransactions, allReports, draftTransaction, currentSearchHash, policyCategories, expenseReportPolicy); - }, [splitExpenses, childTransactions.length, isBetaEnabled, draftTransaction, sumOfSplitExpenses, transactionDetailsAmount, isPerDiem, isCard, splitFieldDataFromChildTransactions, allTransactions, allReports, currentSearchHash, policyCategories, expenseReportPolicy, splitFieldDataFromOriginalTransaction, translate, transactionID, transactionDetails?.currency]); + }, [ + splitExpenses, + childTransactions.length, + isBetaEnabled, + draftTransaction, + sumOfSplitExpenses, + transactionDetailsAmount, + isPerDiem, + isCard, + splitFieldDataFromChildTransactions, + allTransactions, + allReports, + currentSearchHash, + policyCategories, + expenseReportPolicy, + splitFieldDataFromOriginalTransaction, + translate, + transactionID, + transactionDetails?.currency, + ]); const onSplitExpenseAmountChange = useCallback( (currentItemTransactionID: string, value: number) => { diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index ce551f5e498e..a7f19030b31a 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2708,7 +2708,7 @@ describe('actions/IOU', () => { originalTransactionID: transaction.transactionID, }, }; - + let allTransactions: OnyxCollection; let allReports: OnyxCollection; await getOnyxData({ @@ -2725,7 +2725,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + saveSplitTransactions(allTransactions, allReports, draftTransaction, 1, undefined, undefined); await waitForBatchedUpdates(); @@ -2786,7 +2786,7 @@ describe('actions/IOU', () => { // When splitting the expense const hash = 1; - + let allTransactions: OnyxCollection; let allReports: OnyxCollection; await getOnyxData({ @@ -2803,7 +2803,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + saveSplitTransactions(allTransactions, allReports, draftTransaction, hash, undefined, undefined); await waitForBatchedUpdates(); @@ -2879,7 +2879,7 @@ describe('actions/IOU', () => { // When splitting the expense const hash = 1; - + let allTransactions: OnyxCollection; let allReports: OnyxCollection; await getOnyxData({ @@ -2896,7 +2896,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + saveSplitTransactions(allTransactions, allReports, draftTransaction, hash, undefined, undefined); await waitForBatchedUpdates(); @@ -6762,7 +6762,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + initSplitExpense(allTransactions, allReports, transaction); await waitForBatchedUpdates(); @@ -6805,7 +6805,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + initSplitExpense(allTransactions, allReports, transaction); await waitForBatchedUpdates(); @@ -6846,7 +6846,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + initSplitExpense(allTransactions, allReports, transaction); await waitForBatchedUpdates(); @@ -7320,7 +7320,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + saveSplitTransactions(allTransactions, allReports, draftTransaction, -2, undefined, undefined); await waitForBatchedUpdates(); @@ -7437,7 +7437,7 @@ describe('actions/IOU', () => { allReports = value; }, }); - + saveSplitTransactions(allTransactions, allReports, draftTransaction, -2, undefined, undefined); await waitForBatchedUpdates(); From 51758396fad2ad40079ced96ceadf677f8103fd2 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Tue, 16 Sep 2025 13:22:32 +0200 Subject: [PATCH 010/208] Add eslint rule to prevent adding new backTo params to screen type definitions --- .eslintrc.js | 13 ++ src/libs/Navigation/types.ts | 263 ++++++++++++++++++++++++++++++++++- 2 files changed, 274 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 792bb6e89ada..f8dbe866d554 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -351,6 +351,19 @@ module.exports = { }, overrides: [ + // Prevent adding new `backTo` params to screen type definitions + { + files: ['src/libs/Navigation/types.ts'], + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: 'TSPropertySignature[key.name="backTo"]', + message: 'The `backTo` route param is deprecated. Do not add new `backTo` properties to screen param lists. See contributingGuides/NAVIGATION.md.', + }, + ], + }, + }, // Enforces every Onyx type and its properties to have a comment explaining its purpose. { files: ['src/types/onyx/**/*.ts'], diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 2d9b9009c410..6c547f60e9df 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -71,11 +71,13 @@ type SplitNavigatorParamList = { type SplitNavigatorBySidebar = (typeof SIDEBAR_TO_SPLIT)[T]; type BackToParams = { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; type ConsoleNavigatorParamList = { [SCREENS.PUBLIC_CONSOLE_DEBUG]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; }; @@ -92,22 +94,27 @@ type SettingsNavigatorParamList = { country?: Country | ''; }; [SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; country: string; }; [SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS]: { contactMethod: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; shouldSkipInitialValidation?: string; }; [SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; forwardTo?: Routes; }; [SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_VERIFY_ACCOUNT]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; forwardTo?: Routes; }; @@ -121,10 +128,12 @@ type SettingsNavigatorParamList = { }; [SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_VALIDATE]: { login: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; forwardTo?: Routes; }; [SCREENS.SETTINGS.MERGE_ACCOUNTS.MERGE_RESULT]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; result: ValueOf; login: string; @@ -133,11 +142,13 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.LOCK.UNLOCK_ACCOUNT]: undefined; [SCREENS.SETTINGS.LOCK.FAILED_TO_LOCK_ACCOUNT]: undefined; [SCREENS.SETTINGS.CONSOLE]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.SETTINGS.SHARE_LOG]: { /** URL of the generated file to share logs in a report */ source: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: undefined; @@ -152,6 +163,7 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: { /** cardID of selected card */ cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD_CONFIRMATION]: { @@ -185,6 +197,7 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ADDRESS]: { policyID: string; country?: Country | ''; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.NAME]: undefined; @@ -192,6 +205,7 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.SHARE]: undefined; [SCREENS.WORKSPACE.INVITE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.MEMBERS_IMPORT]: { @@ -205,48 +219,58 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.INVITE_MESSAGE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.INVITE_MESSAGE_ROLE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CATEGORY_CREATE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORY_CREATE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CATEGORY_EDIT]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORY_EDIT]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CATEGORY_PAYROLL_CODE]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORY_PAYROLL_CODE]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CATEGORY_GL_CODE]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORY_GL_CODE]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CATEGORY_DEFAULT_TAX_RATE]: { @@ -272,21 +296,25 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.CATEGORY_SETTINGS]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORY_SETTINGS]: { policyID: string; categoryName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.UPGRADE]: { policyID?: string; featureName?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; categoryId?: string; }; [SCREENS.WORKSPACE.DOWNGRADE]: { policyID?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.PAY_AND_DOWNGRADE]: { @@ -294,34 +322,42 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORIES_SETTINGS]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CATEGORIES_IMPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORIES_IMPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CATEGORIES_IMPORTED]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORIES_IMPORTED]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAG_CREATE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAG_CREATE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.DISTANCE_RATE_DETAILS]: { @@ -346,41 +382,54 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.TAGS_SETTINGS]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_SETTINGS]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAGS_IMPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_IMPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAGS_IMPORT_OPTIONS]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAGS_IMPORT_MULTI_LEVEL_SETTINGS]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAGS_IMPORTED]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md + backTo?: Routes; + }; + [SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_IMPORTED]: { + policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; - [SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_IMPORTED]: {policyID: string; backTo?: Routes}; [SCREENS.WORKSPACE.TAGS_IMPORTED_MULTI_LEVEL]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAG_SETTINGS]: { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; parentTagsFilter?: string; }; @@ -388,63 +437,74 @@ type SettingsNavigatorParamList = { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; parentTagsFilter?: string; }; [SCREENS.WORKSPACE.TAG_LIST_VIEW]: { policyID: string; orderWeight: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAG_LIST_VIEW]: { policyID: string; orderWeight: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAGS_EDIT]: { policyID: string; orderWeight: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_EDIT]: { policyID: string; orderWeight: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAG_EDIT]: { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAG_EDIT]: { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAG_APPROVER]: { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAG_APPROVER]: { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAG_GL_CODE]: { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAG_GL_CODE]: { policyID: string; orderWeight: number; tagName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS.SUBSCRIPTION.SIZE]: { @@ -510,17 +570,20 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.OWNER_CHANGE_SUCCESS]: { policyID: string; accountID: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.OWNER_CHANGE_ERROR]: { policyID: string; accountID: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.OWNER_CHANGE_CHECK]: { policyID: string; accountID: number; error: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.CREATE_DISTANCE_RATE]: { @@ -551,26 +614,32 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_DATE_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_INVOICE_ACCOUNT_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES_ACCOUNT_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_NON_REIMBURSABLE_DEFAULT_VENDOR_SELECT]: { @@ -578,26 +647,32 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT_COMPANY_CARD_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_PREFERRED_EXPORTER]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_COMPANY_CARD_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_NON_REIMBURSABLE_DEFAULT_VENDOR_SELECT]: { @@ -605,34 +680,42 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_ADVANCED]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT_DATE_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT_PREFERRED_EXPORTER]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES_ACCOUNT_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_SETUP_MODAL]: { @@ -688,10 +771,12 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PURCHASE_BILL_DATE_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_ADVANCED]: { @@ -699,10 +784,12 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_STATUS_SELECTOR]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_INVOICE_ACCOUNT_SELECTOR]: { @@ -710,6 +797,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: { @@ -717,6 +805,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREREQUISITES]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.ENTER_SAGE_INTACCT_CREDENTIALS]: { @@ -773,47 +862,57 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_PREFERRED_EXPORTER_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_DATE_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT_EXPENSES]: { policyID: string; expenseType: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT_EXPENSES_DESTINATION_SELECT]: { policyID: string; expenseType: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT_EXPENSES_VENDOR_SELECT]: { policyID: string; expenseType: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT_EXPENSES_PAYABLE_ACCOUNT_SELECT]: { policyID: string; expenseType: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT_EXPENSES_JOURNAL_POSTING_PREFERENCE_SELECT]: { policyID: string; expenseType: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_RECEIVABLE_ACCOUNT_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_INVOICE_ITEM_PREFERENCE_SELECT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_INVOICE_ITEM_SELECT]: { @@ -876,39 +975,48 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_DESTINATION]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DESTINATION]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_DEFAULT_VENDOR]: { policyID: string; reimbursable: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADVANCED]: { @@ -932,6 +1040,7 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.DELEGATE.DELEGATE_ROLE]: { login: string; role?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS.DELEGATE.UPDATE_DELEGATE_ROLE]: { @@ -956,14 +1065,17 @@ type SettingsNavigatorParamList = { cardID: string; }; [SCREENS.KEYBOARD_SHORTCUTS]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { [EXIT_SURVEY_REASON_FORM_INPUT_IDS.REASON]: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.WORKSPACE.TAX_CREATE]: { @@ -1000,12 +1112,14 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.COMPANY_CARDS_BANK_CONNECTION]: { policyID: string; bankName: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: { policyID: string; bank: CompanyCardFeed; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARD_NAME]: { @@ -1017,10 +1131,12 @@ type SettingsNavigatorParamList = { policyID: string; cardID: string; bank: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: { @@ -1031,6 +1147,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_FREQUENCY]: { @@ -1042,6 +1159,7 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD]: { policyID: string; feed: CompanyCardFeed; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME]: { @@ -1053,41 +1171,49 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.EXPENSIFY_CARD_DETAILS]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_NAME]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT_TYPE]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.EXPENSIFY_CARD.EXPENSIFY_CARD_DETAILS]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.EXPENSIFY_CARD.EXPENSIFY_CARD_NAME]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.EXPENSIFY_CARD.EXPENSIFY_CARD_LIMIT]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.EXPENSIFY_CARD.EXPENSIFY_CARD_LIMIT_TYPE]: { policyID: string; cardID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.REPORTS_DEFAULT_TITLE]: { @@ -1161,6 +1287,7 @@ type SettingsNavigatorParamList = { type TwoFactorAuthNavigatorParamList = { [SCREENS.TWO_FACTOR_AUTH.ROOT]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; forwardTo?: string; }; @@ -1169,10 +1296,12 @@ type TwoFactorAuthNavigatorParamList = { forwardTo?: Routes; }; [SCREENS.TWO_FACTOR_AUTH.VERIFY]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; forwardTo?: string; }; [SCREENS.TWO_FACTOR_AUTH.SUCCESS]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; forwardTo?: string; }; @@ -1198,6 +1327,7 @@ type ProfileNavigatorParamList = { accountID: string; reportID: string; login?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; }; @@ -1212,16 +1342,19 @@ type NewReportWorkspaceSelectionNavigatorParamList = { type ReportDetailsNavigatorParamList = { [SCREENS.REPORT_DETAILS.ROOT]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_DETAILS.SHARE_CODE]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_DETAILS.EXPORT]: { reportID: string; policyID: string; connectionName: ConnectionName; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1229,6 +1362,7 @@ type ReportDetailsNavigatorParamList = { type ReportChangeWorkspaceNavigatorParamList = { [SCREENS.REPORT_CHANGE_WORKSPACE.ROOT]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1236,22 +1370,27 @@ type ReportChangeWorkspaceNavigatorParamList = { type ReportSettingsNavigatorParamList = { [SCREENS.REPORT_SETTINGS.ROOT]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_SETTINGS.NAME]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_SETTINGS.NOTIFICATION_PREFERENCES]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_SETTINGS.WRITE_CAPABILITY]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_SETTINGS.VISIBILITY]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1259,6 +1398,7 @@ type ReportSettingsNavigatorParamList = { type ReportDescriptionNavigatorParamList = { [SCREENS.REPORT_DESCRIPTION_ROOT]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1266,20 +1406,24 @@ type ReportDescriptionNavigatorParamList = { type ParticipantsNavigatorParamList = { [SCREENS.REPORT_PARTICIPANTS.ROOT]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_PARTICIPANTS.INVITE]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_PARTICIPANTS.DETAILS]: { reportID: string; accountID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.REPORT_PARTICIPANTS.ROLE]: { reportID: string; accountID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1287,16 +1431,19 @@ type ParticipantsNavigatorParamList = { type RoomMembersNavigatorParamList = { [SCREENS.ROOM_MEMBERS.ROOT]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.ROOM_MEMBERS.INVITE]: { reportID: string; role?: 'accountant'; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.ROOM_MEMBERS.DETAILS]: { reportID: string; accountID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1306,12 +1453,14 @@ type MoneyRequestNavigatorParamList = { iouType: IOUType; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.EDIT_REPORT]: { action: IOUAction; iouType: IOUType; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; shouldTurnOffSelectionMode?: boolean; }; @@ -1320,6 +1469,7 @@ type MoneyRequestNavigatorParamList = { iouType: IOUType; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; reportActionID?: string; }; @@ -1327,6 +1477,7 @@ type MoneyRequestNavigatorParamList = { iouType: IOUType; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: { @@ -1334,6 +1485,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.STEP_DATE]: { @@ -1341,6 +1493,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; reportActionID?: string; }; @@ -1349,6 +1502,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; reportActionID: string; }; @@ -1358,6 +1512,7 @@ type MoneyRequestNavigatorParamList = { transactionID: string; reportActionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: { @@ -1365,6 +1520,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; currency?: string; }; @@ -1373,6 +1529,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; reportActionID: string; orderWeight: string; @@ -1382,11 +1539,14 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: { iouType: IOUType; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; action: IOUAction; pageIndex: string; @@ -1397,6 +1557,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; reportActionID?: string; }; @@ -1405,6 +1566,7 @@ type MoneyRequestNavigatorParamList = { iouType: ValueOf; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: undefined; @@ -1415,6 +1577,7 @@ type MoneyRequestNavigatorParamList = { iouType: IOUType; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; backToReport?: string; reportActionID?: string; @@ -1424,6 +1587,7 @@ type MoneyRequestNavigatorParamList = { iouType: IOUType; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; backToReport?: string; reportActionID?: string; @@ -1433,6 +1597,7 @@ type MoneyRequestNavigatorParamList = { iouType: IOUType; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; backToReport?: string; reportActionID?: string; @@ -1444,6 +1609,7 @@ type MoneyRequestNavigatorParamList = { // These are not used in the screen, but are needed for the navigation // for IOURequestStepDistance and IOURequestStepAmount components + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: never; action: never; currency: never; @@ -1461,6 +1627,7 @@ type MoneyRequestNavigatorParamList = { iouType: IOUType; reportID: string; transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; action: IOUAction; pageIndex?: string; @@ -1472,6 +1639,7 @@ type MoneyRequestNavigatorParamList = { action: IOUAction; iouType: ValueOf; transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; reportID: string; reportActionID?: string; @@ -1482,6 +1650,7 @@ type MoneyRequestNavigatorParamList = { transactionID: string; reportID: string; pageIndex?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; participantsAutoAssigned?: string; backToReport?: string; @@ -1492,11 +1661,13 @@ type MoneyRequestNavigatorParamList = { transactionID: string; reportID: string; pageIndex: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; backToReport?: string; }; [SCREENS.MONEY_REQUEST.RECEIPT_VIEW]: { transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.STEP_CURRENCY]: { @@ -1505,6 +1676,7 @@ type MoneyRequestNavigatorParamList = { transactionID: string; reportID: string; pageIndex?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; currency?: string; }; @@ -1516,6 +1688,7 @@ type MoneyRequestNavigatorParamList = { reportID: string; /** Link to previous page */ + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: ExpensifyRoute; /** Hash that includes info about what is searched for */ @@ -1529,6 +1702,7 @@ type MoneyRequestNavigatorParamList = { reportID: string; /** Link to previous page */ + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: ExpensifyRoute; }; [SCREENS.MONEY_REQUEST.STEP_ATTENDEES]: { @@ -1536,6 +1710,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.STEP_ACCOUNTANT]: { @@ -1543,6 +1718,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; [SCREENS.MONEY_REQUEST.STEP_UPGRADE]: { @@ -1550,6 +1726,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; upgradePath?: ValueOf; shouldSubmitExpense?: boolean; @@ -1559,6 +1736,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; }; [SCREENS.MONEY_REQUEST.STEP_TIME]: { @@ -1566,11 +1744,13 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; }; [SCREENS.MONEY_REQUEST.STEP_SUBRATE]: { iouType: Exclude; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; action: IOUAction; pageIndex: string; @@ -1581,6 +1761,7 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; }; [SCREENS.MONEY_REQUEST.STEP_TIME_EDIT]: { @@ -1588,11 +1769,13 @@ type MoneyRequestNavigatorParamList = { iouType: Exclude; transactionID: string; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; }; [SCREENS.MONEY_REQUEST.STEP_SUBRATE_EDIT]: { iouType: Exclude; reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; action: IOUAction; pageIndex: string; @@ -1605,6 +1788,7 @@ type MoneyRequestNavigatorParamList = { // These are not used in the screen, but are needed for the navigation // for IOURequestStepDistanceMap component + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: never; action: never; currency: never; @@ -1616,6 +1800,7 @@ type MoneyRequestNavigatorParamList = { type WorkspaceConfirmationNavigatorParamList = { [SCREENS.WORKSPACE_CONFIRMATION.ROOT]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.CURRENCY.SELECTION]: { @@ -1634,19 +1819,24 @@ type WorkspaceDuplicateNavigatorParamList = { type NewTaskNavigatorParamList = { [SCREENS.NEW_TASK.ROOT]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.NEW_TASK.TASK_ASSIGNEE_SELECTOR]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.NEW_TASK.TASK_SHARE_DESTINATION_SELECTOR]: undefined; [SCREENS.NEW_TASK.DETAILS]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.NEW_TASK.TITLE]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.NEW_TASK.DESCRIPTION]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1660,10 +1850,12 @@ type TeachersUniteNavigatorParamList = { type TaskDetailsNavigatorParamList = { [SCREENS.TASK.TITLE]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TASK.ASSIGNEE]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1676,6 +1868,7 @@ type SplitDetailsNavigatorParamList = { [SCREENS.SPLIT_DETAILS.ROOT]: { reportID: string; reportActionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SPLIT_DETAILS.EDIT_REQUEST]: { @@ -1694,6 +1887,7 @@ type AddPersonalBankAccountNavigatorParamList = { type ReimbursementAccountNavigatorParamList = { [SCREENS.REIMBURSEMENT_ACCOUNT_ROOT]: { stepToOpen?: ReimbursementAccountStepToOpen; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; policyID?: string; }; @@ -1718,6 +1912,7 @@ type FlagCommentNavigatorParamList = { [SCREENS.FLAG_COMMENT_ROOT]: { reportID: string; reportActionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1727,6 +1922,7 @@ type EditRequestNavigatorParamList = { fieldID: string; reportID: string; policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1745,17 +1941,20 @@ type FeatureTrainingNavigatorParamList = { type ReferralDetailsNavigatorParamList = { [SCREENS.REFERRAL_DETAILS]: { contentType: ValueOf; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: string; }; }; type PrivateNotesNavigatorParamList = { [SCREENS.PRIVATE_NOTES.LIST]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.PRIVATE_NOTES.EDIT]: { reportID: string; accountID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1763,34 +1962,42 @@ type PrivateNotesNavigatorParamList = { type TransactionDuplicateNavigatorParamList = { [SCREENS.TRANSACTION_DUPLICATE.REVIEW]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRANSACTION_DUPLICATE.MERCHANT]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRANSACTION_DUPLICATE.CATEGORY]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRANSACTION_DUPLICATE.TAG]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRANSACTION_DUPLICATE.DESCRIPTION]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRANSACTION_DUPLICATE.TAX_CODE]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRANSACTION_DUPLICATE.BILLABLE]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRANSACTION_DUPLICATE.REIMBURSABLE]: { threadReportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1798,18 +2005,22 @@ type TransactionDuplicateNavigatorParamList = { type MergeTransactionNavigatorParamList = { [SCREENS.MERGE_TRANSACTION.LIST_PAGE]: { transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.MERGE_TRANSACTION.RECEIPT_PAGE]: { transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.MERGE_TRANSACTION.DETAILS_PAGE]: { transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.MERGE_TRANSACTION.CONFIRMATION_PAGE]: { transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1872,6 +2083,7 @@ type TravelNavigatorParamList = { [SCREENS.TRAVEL.TRIP_SUMMARY]: { reportID: string; transactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.TRAVEL.TRIP_DETAILS]: { @@ -1879,6 +2091,7 @@ type TravelNavigatorParamList = { transactionID: string; sequenceIndex: number; pnr: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.TRAVEL.TCS]: { @@ -1889,15 +2102,19 @@ type TravelNavigatorParamList = { }; [SCREENS.TRAVEL.WORKSPACE_ADDRESS]: { domain: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRAVEL.PUBLIC_DOMAIN_ERROR]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRAVEL.UPGRADE]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRAVEL.DOMAIN_SELECTOR]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.TRAVEL.VERIFY_ACCOUNT]: { @@ -1912,6 +2129,7 @@ type ReportsSplitNavigatorParamList = { reportActionID?: string; openOnAdminRoom?: boolean; referrer?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.ATTACHMENTS]: AttachmentModalScreensParamList[typeof SCREENS.ATTACHMENTS]; @@ -1922,22 +2140,28 @@ type SettingsSplitNavigatorParamList = { [SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined; [SCREENS.SETTINGS.SECURITY]: undefined; [SCREENS.SETTINGS.PROFILE.ROOT]?: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS.WALLET.ROOT]: undefined; [SCREENS.SETTINGS.ABOUT]: undefined; [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; [SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined; - [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]?: {backTo?: Routes}; + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]?: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md + backTo?: Routes; + }; }; type WorkspaceSplitNavigatorParamList = { [SCREENS.WORKSPACE.INITIAL]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.PROFILE]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.EXPENSIFY_CARD]: { @@ -1952,21 +2176,25 @@ type WorkspaceSplitNavigatorParamList = { [SCREENS.WORKSPACE.RECEIPT_PARTNERS_INVITE]: { policyID: string; integration: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.RECEIPT_PARTNERS_INVITE_EDIT]: { policyID: string; integration: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.COMPANY_CARDS_TRANSACTION_START_DATE]: { policyID: string; feed: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.PER_DIEM]: { @@ -1977,6 +2205,7 @@ type WorkspaceSplitNavigatorParamList = { }; [SCREENS.WORKSPACE.WORKFLOWS_APPROVALS_NEW]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.WORKFLOWS_APPROVALS_EDIT]: { @@ -1985,11 +2214,13 @@ type WorkspaceSplitNavigatorParamList = { }; [SCREENS.WORKSPACE.WORKFLOWS_APPROVALS_EXPENSES_FROM]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.WORKFLOWS_APPROVALS_APPROVER]: { policyID: string; approverIndex: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: { @@ -2012,10 +2243,12 @@ type WorkspaceSplitNavigatorParamList = { }; [SCREENS.WORKSPACE.CATEGORIES]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORIES_ROOT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.MORE_FEATURES]: { @@ -2023,10 +2256,12 @@ type WorkspaceSplitNavigatorParamList = { }; [SCREENS.WORKSPACE.TAGS]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_ROOT]: { policyID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE.TAXES]: { @@ -2069,42 +2304,55 @@ type WorkspaceSplitNavigatorParamList = { type OnboardingModalNavigatorParamList = { [SCREENS.ONBOARDING.PERSONAL_DETAILS]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.PRIVATE_DOMAIN]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.WORKSPACES]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.PURPOSE]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.EMPLOYEES]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.ACCOUNTING]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.INTERESTED_FEATURES]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.WORK_EMAIL]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.WORK_EMAIL_VALIDATION]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.WORKSPACE_OPTIONAL]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.WORKSPACE_CONFIRMATION]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.WORKSPACE_CURRENCY]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.ONBOARDING.WORKSPACE_INVITE]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; }; @@ -2207,6 +2455,7 @@ type AttachmentModalScreensParamList = { }; [SCREENS.PROFILE_AVATAR]: AttachmentModalContainerModalProps & { accountID: number; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE_AVATAR]: AttachmentModalContainerModalProps & { @@ -2241,6 +2490,7 @@ type AuthScreensParamList = SharedScreensParamList & [SCREENS.TRACK_EXPENSE]: undefined; [SCREENS.SUBMIT_EXPENSE]: undefined; [SCREENS.WORKSPACES_LIST]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.WORKSPACE_JOIN_USER]: { @@ -2271,6 +2521,7 @@ type SearchReportParamList = { [SCREENS.SEARCH.REPORT_RHP]: { reportID: string; reportActionID?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: { @@ -2281,6 +2532,7 @@ type SearchReportParamList = { reportID: string; /** Link to previous page */ + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: ExpensifyRoute; /** Hash that includes info about what is searched for */ @@ -2288,6 +2540,7 @@ type SearchReportParamList = { }; [SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS]: { /** Link to previous page */ + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; /** Selected transactions' report ID */ reportID: string; @@ -2302,6 +2555,7 @@ type SearchFullscreenNavigatorParamList = { }; [SCREENS.SEARCH.MONEY_REQUEST_REPORT]: { reportID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -2329,12 +2583,14 @@ type SplitExpenseParamList = { reportID: string; transactionID: string; splitExpenseTransactionID?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; [SCREENS.MONEY_REQUEST.SPLIT_EXPENSE_EDIT]: { reportID: string; transactionID: string; splitExpenseTransactionID: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -2362,11 +2618,13 @@ type DebugParamList = { fieldName: string; fieldValue?: string; policyID?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.DEBUG.DETAILS_DATE_TIME_PICKER_PAGE]: { fieldName: string; fieldValue?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: string; }; [SCREENS.DEBUG.TRANSACTION]: { @@ -2401,6 +2659,7 @@ type ReportChangeApproverParamList = { type TestToolsModalModalNavigatorParamList = { [SCREENS.TEST_TOOLS_MODAL.ROOT]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; From 2f3cd5c4ea10fad4a7a13e74fa0bf31266c2fc39 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Fri, 3 Oct 2025 13:49:41 +0200 Subject: [PATCH 011/208] Deprecate backTo in NAVIGATION.md --- .eslintrc.js | 5 +- contributingGuides/NAVIGATION.md | 109 +++++++++++++++++++++++++++++-- src/libs/Navigation/types.ts | 3 + 3 files changed, 109 insertions(+), 8 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index f8dbe866d554..5ffd8bc99206 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -274,7 +274,7 @@ module.exports = { { selector: 'CallExpression[callee.name="getUrlWithBackToParam"]', message: - 'Usage of getUrlWithBackToParam function is prohibited. This is legacy code and no new occurrences should be added. Please look into documentation and use alternative routing methods instead.', + 'Usage of getUrlWithBackToParam function is prohibited. This is legacy code and no new occurrences should be added. Please look into the `How to remove backTo from URL` section in contributingGuides/NAVIGATION.md. and use alternative routing methods instead.', }, // These are the original rules from AirBnB's style guide, modified to allow for...of loops and for...in loops @@ -359,7 +359,8 @@ module.exports = { 'error', { selector: 'TSPropertySignature[key.name="backTo"]', - message: 'The `backTo` route param is deprecated. Do not add new `backTo` properties to screen param lists. See contributingGuides/NAVIGATION.md.', + message: + 'The `backTo` route param is deprecated. Do not add new `backTo` properties to screen param lists. Please look into the `How to remove backTo from URL` section in contributingGuides/NAVIGATION.md. and use alternative routing methods instead.', }, ], }, diff --git a/contributingGuides/NAVIGATION.md b/contributingGuides/NAVIGATION.md index bad97b5b7494..be05e0d6b6b3 100644 --- a/contributingGuides/NAVIGATION.md +++ b/contributingGuides/NAVIGATION.md @@ -16,7 +16,9 @@ The navigation in the app is built on top of the `react-navigation` library. To - [Debugging](#debugging) - [Reading state when it changes](#reading-state-when-it-changes) - [Finding the code that calls the navigation function](#finding-the-code-that-calls-the-navigation-function) - - [Using `backTo` route param](#using-backto-route-param) + - [How to remove backTo from URL](#how-to-remove-backto-from-url) + - [Separate routes for each screen instance](#separate-routes-for-each-screen-instance) + - [Maintaining the order of screens in forms](#maintaining-the-order-of-screens-in-forms) - [Generating state from a path](#generating-state-from-a-path) - [Setting the correct screen underneath RHP](#setting-the-correct-screen-underneath-rhp) - [Performance solutions](#performance-solutions) @@ -71,7 +73,7 @@ Navigation.navigate( ); // Navigation with forceReplace - replaces current screen instead of pushing a new one -Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: nextReportID, backTo}), {forceReplace: true}); +Navigation.navigate(ROUTES.SETTINGS_WALLET, {forceReplace: true}); // Navigation with a callback to handle anonymous users interceptAnonymousUser(() => { @@ -434,10 +436,13 @@ const handleStateChange = (state: NavigationState | undefined) => { The easiest way to find the piece of code from which the navigation method was called is to use a debugger and breakpoints. You should attach a breakpoint in the navigation method and check the call stack, this way you can easily find the navigation method that caused the problem. -## Using `backTo` route param +## How to remove backTo from URL > [!WARNING] -> **Deprecated**: The `backTo` parameter is deprecated and should not be used in new implementations. Most problems that `backTo` solved can be resolved by adding one or more routes for a single screen. If you don't know how to solve your problem, contact someone from the navigation team. +> **Deprecated**: The `backTo` parameter is deprecated and should not be used in new implementations. Most problems that `backTo` solved can be resolved by adding one or more routes for a single screen. If you don't know how to solve your problem, contact someone from the navigation team. Old documentation on how to use `backTo` can be found below. + +
+Using `backTo` route param When a particular screen can be opened from two or more different pages, we can use `backTo` route parameter to handle such case. @@ -495,6 +500,98 @@ function NewSettingsScreen({route}: NewSettingsScreenNavigationProps) { ); } ``` +
+ +### Separate routes for each screen instance + +Often, you will need to reuse a single screen across multiple navigation flows. For example, the `ReportScreen` can be viewed in full screen width in the Inbox tab and in the Reports tab in the RHP. The proper approach to implementing such a mechanism is to create a new route for each screen instance within a single flow. + +Considerations when removing `backTo` from a URL: +- For RHP screens, check if the correct central screen is under the overlay after refreshing the page. More information on how to set the default screen underneath RHP can be found (here)[#setting-the-correct-screen-underneath-rhp]. +- Ensure that after refreshing the page and pressing the back button in the application, you return to the page from which you initially accessed the currently displayed screen. + +An example of a screen that is reused in two flows is `ReportScreen` (`src/pages/home/ReportScreen.tsx`): +1. Sharing types + +```ts +type ReportScreenNavigationProps = + | PlatformStackScreenProps + | PlatformStackScreenProps; +``` + +2. Binding one component to multiple screens + +`src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx` +```ts +const loadReportScreen = () => require('@pages/home/ReportScreen').default; + +return ( + + + // ... + + + + ); +``` + +3. Custom component behavior depending on the current route + +If we want the component's behavior to change based on the current route, we can implement this using the `route` property. We can use it to specify the correct return route after a refresh. + +```ts +import SCREENS from '@src/SCREENS'; +import type {ReportsSplitNavigatorParamList, SearchReportParamList} from '@navigation/types'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; + +type ReportScreenNavigationProps = + | PlatformStackScreenProps + | PlatformStackScreenProps; + +function ReportScreen({route, navigation}: ReportScreenProps) { + + const customAction = () => { + if(route.name === SCREENS.SEARCH.REPORT_RHP) { + // do something when screen is SCREENS.SEARCH.REPORT_RHP + }else { + // do something when screen is SCREENS.REPORT + } + } +} +``` + +### Maintaining the order of screens in forms +Another case where maintaining the screen order after a refresh is important is in forms. There are several methods to achieve this: +1. For multi-step forms, append the values from each screen to the URL. This approach makes it easy to recreate the screen order with the appropriate values. + +We have a three-stage form where each screen is responsible for entering another value. + +Form fields: +- First name +- Last name +- Date of birth + +We should use the following url pattern: +- FirstNamePage: `/example-form/:firstName` +- LastNamePage: `/example-form/:firstName/:lastName` +- DateOfBirthPage: `/example-form/:firstName:/:lastName/:dateOfBirth` + +Thanks to this structure, we are able to easily recreate the order of screens with the appropriate values ​​in the form. + +2. Store form data in Onyx. +3. Reset the form to the first screen after a refresh. +If we do not want to preserve the form's values after a refresh, we should reset the form. To handle this properly, we can perform a replace on the current screen (replace it with the first screen in the form's sequence). + ## Generating state from a path @@ -507,7 +604,7 @@ In Expensify, we use an extended implementation of this function because: - When opening a link leading to an onboarding screen, all previous screens in this flow have to be present in the navigation state. - In case of opening the RHP, appropriate screens should be pushed to the navigation to be displayed below the overlay. A guide on how to set up a good screen for RHP can be found [here](#how-to-set-a-correct-screen-below-the-rhp). - When opening the settings of a specific workspace, the workspace list needs to be pushed to the state. -- When the `backTo` parameter is in the URL, we need to build a state also for the screen we want to return to. +- When the `backTo` parameter is in the URL, we need to build a state also for the screen we want to return to. (`backTo` parameter is deprecated, more information can be found [here](#how-to-properly-remove-backto-from-url)) Here are examples how the state is generated based on route: @@ -623,7 +720,7 @@ In the above example, we can see that when building a state from a link leading ## Setting the correct screen underneath RHP -RHP screens can usually be opened from a specific central screen. Of course there are cases where one RHP screen can be used in different tabs (then using `backTo` parameter comes in handy). However, most often one RHP screen has a specific central screen assigned underneath. +RHP screens can usually be opened from a specific central screen. Of course there are cases where one RHP screen can be used in different tabs. However, most often one RHP screen has a specific central screen assigned underneath. > [!WARNING] > **Deprecated**: The `backTo` parameter is deprecated and should not be used in new implementations. Most problems that `backTo` solved can be resolved by adding one or more routes for a single screen. If you don't know how to solve your problem, contact someone from the navigation team. diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 6c547f60e9df..e5f9bb8ee8a4 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1292,6 +1292,7 @@ type TwoFactorAuthNavigatorParamList = { forwardTo?: string; }; [SCREENS.TWO_FACTOR_AUTH.VERIFY_ACCOUNT]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; forwardTo?: Routes; }; @@ -1335,6 +1336,7 @@ type ProfileNavigatorParamList = { type NewReportWorkspaceSelectionNavigatorParamList = { [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: { isMovingExpenses?: boolean; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; @@ -1804,6 +1806,7 @@ type WorkspaceConfirmationNavigatorParamList = { backTo?: Routes; }; [SCREENS.CURRENCY.SELECTION]: { + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo?: Routes; }; }; From ed5fedc38ee2fac12f4752c96a6e66d33194cf6a Mon Sep 17 00:00:00 2001 From: Yauheni Horbach Date: Thu, 9 Oct 2025 12:52:38 +0200 Subject: [PATCH 012/208] Fix bugs --- src/libs/API/parameters/SplitTransactionParams.ts | 2 +- src/libs/TransactionUtils/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/API/parameters/SplitTransactionParams.ts b/src/libs/API/parameters/SplitTransactionParams.ts index 463faa6b1136..3ffc0db281eb 100644 --- a/src/libs/API/parameters/SplitTransactionParams.ts +++ b/src/libs/API/parameters/SplitTransactionParams.ts @@ -23,6 +23,6 @@ type SplitTransactionParams = { [key: string]: string | boolean; }; -type RevertSplitTransactionParams = SplitTransactionSplitParam & {comment?: string}; +type RevertSplitTransactionParams = Omit & {comment?: string}; export type {SplitTransactionParams, SplitTransactionSplitsParam, RevertSplitTransactionParams}; diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 1a94bc13f008..6ac11bfc86eb 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -765,7 +765,7 @@ function isFetchingWaypointsFromServer(transaction: OnyxInputOrEntry, policyParam: OnyxEntry = undefined) { if (transaction && isDistanceRequest(transaction)) { @@ -773,7 +773,7 @@ function isUnreportedAndHasInvalidDistanceRateTransaction(transaction: OnyxInput // eslint-disable-next-line deprecation/deprecation const policy = policyParam ?? getPolicy(report?.policyID); const {rate} = DistanceRequestUtils.getRate({transaction, policy}); - const isUnreportedExpense = !transaction.reportID || transaction.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; + const isUnreportedExpense = !transaction.reportID || transaction.reportID === CONST.REPORT.UNREPORTED_REPORT_ID || String(transaction.reportID) === CONST.REPORT.SPLIT_REPORT_ID; if (isUnreportedExpense && !rate) { return true; From 82de263b5314a221e01b1ee361620b44a217382c Mon Sep 17 00:00:00 2001 From: Yauheni Horbach Date: Thu, 9 Oct 2025 14:06:28 +0200 Subject: [PATCH 013/208] Refactor updateSplitTransactions to reuse in other places --- src/libs/actions/IOU.ts | 56 +++++++++++++--------- src/pages/iou/SplitExpensePage.tsx | 16 ++++++- tests/actions/IOUTest.ts | 76 ++++++++++++++++++++++++++---- 3 files changed, 116 insertions(+), 32 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 54204aa44b43..5a733d20b2e7 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -13002,33 +13002,45 @@ function clearSplitTransactionDraftErrors(transactionID: string | undefined) { }); } -function saveSplitTransactions( - transactions: OnyxCollection, - reports: OnyxCollection, - draftTransaction: OnyxEntry, - hash: number, - policyCategories: OnyxTypes.PolicyCategories | undefined, - policy: OnyxTypes.Policy | undefined, -) { - const transactionReport = getReportOrDraftReport(draftTransaction?.reportID); +function updateSplitTransactions({ + allTransactionsList, + allReportsList, + transactionData, + hash, + policyCategories, + policy, +}: { + allTransactionsList: OnyxCollection; + allReportsList: OnyxCollection; + transactionData: { + reportID: string; + originalTransactionID: string; + splitExpenses: SplitExpense[]; + splitExpensesTotal?: number; + }; + hash: number; + policyCategories: OnyxTypes.PolicyCategories | undefined; + policy: OnyxTypes.Policy | undefined; +}) { + const transactionReport = getReportOrDraftReport(transactionData?.reportID); const parentTransactionReport = getReportOrDraftReport(transactionReport?.parentReportID); const expenseReport = transactionReport?.type === CONST.REPORT.TYPE.EXPENSE ? transactionReport : parentTransactionReport; - const originalTransactionID = draftTransaction?.comment?.originalTransactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID; - const originalTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]; + const originalTransactionID = transactionData?.originalTransactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID; + const originalTransaction = allTransactionsList?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]; const originalTransactionDetails = getTransactionDetails(originalTransaction); const iouActions = getIOUActionForTransactions([originalTransactionID], expenseReport?.reportID); const policyTags = getPolicyTagsData(expenseReport?.policyID); const participants = getMoneyRequestParticipantsFromReport(expenseReport); - const splitExpenses = draftTransaction?.comment?.splitExpenses ?? []; + const splitExpenses = transactionData?.splitExpenses ?? []; // List of all child transactions that have been created after split - const originalChildTransactions = getChildTransactions(transactions, reports, originalTransactionID); + const originalChildTransactions = getChildTransactions(allTransactionsList, allReportsList, originalTransactionID); const processedChildTransactionIDs: string[] = []; const reportTotal = transactionReport?.total ?? 0; - const splitExpensesTotal = draftTransaction?.comment?.splitExpensesTotal ?? 0; + const splitExpensesTotal = transactionData?.splitExpensesTotal ?? 0; const isCreationOfSplits = originalChildTransactions.length === 0; const isReverseSplitOperation = splitExpenses.length === 1 && originalChildTransactions.length > 0; @@ -13075,7 +13087,7 @@ function saveSplitTransactions( splitExpenses.forEach((splitExpense, index) => { const existingTransactionID = isReverseSplitOperation ? originalTransactionID : splitExpense.transactionID; - const splitTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${existingTransactionID}`]; + const splitTransaction = allTransactionsList?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${existingTransactionID}`]; if (splitTransaction) { processedChildTransactionIDs.push(splitTransaction.transactionID); } @@ -13100,19 +13112,19 @@ function saveSplitTransactions( transactionParams: { amount: Math.abs(originalTransaction?.amount ?? 0), modifiedAmount: splitExpense.amount ?? 0, - currency: draftTransaction?.currency ?? CONST.CURRENCY.USD, + currency: originalTransactionDetails?.currency ?? CONST.CURRENCY.USD, created: splitExpense.created, merchant: splitExpense.merchant ?? '', comment: splitExpense.description, category: splitExpense.category, tag: splitExpense.tags?.[0], originalTransactionID, - attendees: draftTransaction?.comment?.attendees, + attendees: originalTransactionDetails?.attendees, source: CONST.IOU.TYPE.SPLIT, linkedTrackedExpenseReportAction: currentReportAction, pendingAction: splitTransaction ? null : CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, pendingFields: splitTransaction ? splitTransaction.pendingFields : undefined, - reimbursable: draftTransaction?.reimbursable, + reimbursable: originalTransactionDetails?.reimbursable, }, parentChatReport: getReportOrDraftReport(getReportOrDraftReport(expenseReport?.chatReportID)?.parentReportID), existingTransaction: originalTransaction, @@ -13121,13 +13133,13 @@ function saveSplitTransactions( if (isReverseSplitOperation) { requestMoneyInformation.transactionParams = { amount: splitExpense.amount ?? 0, - currency: draftTransaction?.currency ?? CONST.CURRENCY.USD, + currency: originalTransactionDetails?.currency ?? CONST.CURRENCY.USD, created: splitExpense.created, merchant: splitExpense.merchant ?? '', comment: splitExpense.description, category: splitExpense.category, tag: splitExpense.tags?.[0], - attendees: draftTransaction?.comment?.attendees, + attendees: originalTransactionDetails?.attendees as Attendee[], linkedTrackedExpenseReportAction: currentReportAction, }; requestMoneyInformation.existingTransaction = undefined; @@ -13215,7 +13227,7 @@ function saveSplitTransactions( (currentTransaction) => !processedChildTransactionIDs.includes(currentTransaction?.transactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID), ); undeletedTransactions.forEach((undeletedTransaction) => { - const splitTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${undeletedTransaction?.transactionID}`]; + const splitTransaction = allTransactionsList?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${undeletedTransaction?.transactionID}`]; const splitReportActions = getAllReportActions(splitTransaction?.reportID); const currentReportAction = Object.values(splitReportActions).find((action) => { const transactionID = isMoneyRequestAction(action) ? (getOriginalMessage(action)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID) : CONST.DEFAULT_NUMBER_ID; @@ -13605,7 +13617,7 @@ export { initSplitExpenseItemData, addSplitExpenseField, updateSplitExpenseAmountField, - saveSplitTransactions, + updateSplitTransactions, initDraftSplitExpenseDataForEdit, removeSplitExpenseField, updateSplitExpenseField, diff --git a/src/pages/iou/SplitExpensePage.tsx b/src/pages/iou/SplitExpensePage.tsx index 2c61a130701e..7755d8501a87 100644 --- a/src/pages/iou/SplitExpensePage.tsx +++ b/src/pages/iou/SplitExpensePage.tsx @@ -24,8 +24,8 @@ import { clearSplitTransactionDraftErrors, initDraftSplitExpenseDataForEdit, initSplitExpenseItemData, - saveSplitTransactions, updateSplitExpenseAmountField, + updateSplitTransactions, } from '@libs/actions/IOU'; import {convertToBackendAmount, convertToDisplayString} from '@libs/CurrencyUtils'; import DateUtils from '@libs/DateUtils'; @@ -153,7 +153,19 @@ function SplitExpensePage({route}: SplitExpensePageProps) { return; } - saveSplitTransactions(allTransactions, allReports, draftTransaction, currentSearchHash, policyCategories, expenseReportPolicy); + updateSplitTransactions({ + allTransactionsList: allTransactions, + allReportsList: allReports, + transactionData: { + reportID: draftTransaction?.reportID ?? '', + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + splitExpenses, + splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal ?? 0, + }, + hash: currentSearchHash, + policyCategories, + policy: expenseReportPolicy, + }); }, [ splitExpenses, childTransactions.length, diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index a7f19030b31a..1cc2cfa78644 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -34,7 +34,6 @@ import { replaceReceipt, requestMoney, resolveDuplicates, - saveSplitTransactions, sendInvoice, setDraftSplitTransaction, setMoneyRequestCategory, @@ -47,6 +46,7 @@ import { updateMoneyRequestAttendees, updateMoneyRequestCategory, updateSplitExpenseAmountField, + updateSplitTransactions, } from '@libs/actions/IOU'; import initOnyxDerivedValues from '@libs/actions/OnyxDerived'; import {createWorkspace, deleteWorkspace, generatePolicyID, setWorkspaceApprovalMode} from '@libs/actions/Policy/Policy'; @@ -2667,7 +2667,7 @@ describe('actions/IOU', () => { }); }); - describe('saveSplitTransactions', () => { + describe('updateSplitTransactions', () => { it('should delete the original transaction thread report', async () => { const expenseReport: Report = { ...createRandomReport(1), @@ -2726,7 +2726,19 @@ describe('actions/IOU', () => { }, }); - saveSplitTransactions(allTransactions, allReports, draftTransaction, 1, undefined, undefined); + updateSplitTransactions({ + allTransactionsList: allTransactions, + allReportsList: allReports, + transactionData: { + reportID: draftTransaction?.reportID ?? '', + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], + splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, + }, + hash: 1, + policyCategories: undefined, + policy: undefined, + }); await waitForBatchedUpdates(); @@ -2804,7 +2816,19 @@ describe('actions/IOU', () => { }, }); - saveSplitTransactions(allTransactions, allReports, draftTransaction, hash, undefined, undefined); + updateSplitTransactions({ + allTransactionsList: allTransactions, + allReportsList: allReports, + transactionData: { + reportID: draftTransaction?.reportID ?? '', + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], + splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, + }, + hash, + policyCategories: undefined, + policy: undefined, + }); await waitForBatchedUpdates(); @@ -2897,7 +2921,19 @@ describe('actions/IOU', () => { }, }); - saveSplitTransactions(allTransactions, allReports, draftTransaction, hash, undefined, undefined); + updateSplitTransactions({ + allTransactionsList: allTransactions, + allReportsList: allReports, + transactionData: { + reportID: draftTransaction?.reportID ?? '', + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], + splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, + }, + hash, + policyCategories: undefined, + policy: undefined, + }); await waitForBatchedUpdates(); @@ -7212,7 +7248,7 @@ describe('actions/IOU', () => { expect(updatedExpenseReport?.unheldNonReimbursableTotal).toBe(-amount); }); - describe('saveSplitTransactions', () => { + describe('updateSplitTransactions', () => { it("should update split transaction's description correctly ", async () => { const amount = 10000; let expenseReport: OnyxEntry; @@ -7321,7 +7357,19 @@ describe('actions/IOU', () => { }, }); - saveSplitTransactions(allTransactions, allReports, draftTransaction, -2, undefined, undefined); + updateSplitTransactions({ + allTransactionsList: allTransactions, + allReportsList: allReports, + transactionData: { + reportID: draftTransaction?.reportID ?? '', + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], + splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, + }, + hash: -2, + policyCategories: undefined, + policy: undefined, + }); await waitForBatchedUpdates(); const split1 = await getOnyxValue(`${ONYXKEYS.COLLECTION.TRANSACTION}235`); @@ -7438,7 +7486,19 @@ describe('actions/IOU', () => { }, }); - saveSplitTransactions(allTransactions, allReports, draftTransaction, -2, undefined, undefined); + updateSplitTransactions({ + allTransactionsList: allTransactions, + allReportsList: allReports, + transactionData: { + reportID: draftTransaction?.reportID ?? '', + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], + splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, + }, + hash: -2, + policyCategories: undefined, + policy: undefined, + }); await waitForBatchedUpdates(); const split1 = await getOnyxValue(`${ONYXKEYS.COLLECTION.TRANSACTION}235`); From 7696de20dcbc549cf0e03a83e17ebd76d235dac2 Mon Sep 17 00:00:00 2001 From: Yauheni Horbach Date: Thu, 9 Oct 2025 17:08:36 +0200 Subject: [PATCH 014/208] Fix eslint issues --- src/pages/iou/SplitExpensePage.tsx | 4 ++-- tests/actions/IOUTest.ts | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/iou/SplitExpensePage.tsx b/src/pages/iou/SplitExpensePage.tsx index 7755d8501a87..38d14d8207fd 100644 --- a/src/pages/iou/SplitExpensePage.tsx +++ b/src/pages/iou/SplitExpensePage.tsx @@ -157,8 +157,8 @@ function SplitExpensePage({route}: SplitExpensePageProps) { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses, splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal ?? 0, }, diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 1cc2cfa78644..9fbe03268707 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2730,8 +2730,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -2820,8 +2820,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -2925,8 +2925,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -7361,8 +7361,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -7490,8 +7490,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, From 0d589a746def5d4c2170c515b5039bfb06b1f403 Mon Sep 17 00:00:00 2001 From: Yauheni Horbach Date: Thu, 9 Oct 2025 17:17:19 +0200 Subject: [PATCH 015/208] Fix eslint issues --- src/pages/iou/SplitExpensePage.tsx | 4 ++-- tests/actions/IOUTest.ts | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/iou/SplitExpensePage.tsx b/src/pages/iou/SplitExpensePage.tsx index 7755d8501a87..38d14d8207fd 100644 --- a/src/pages/iou/SplitExpensePage.tsx +++ b/src/pages/iou/SplitExpensePage.tsx @@ -157,8 +157,8 @@ function SplitExpensePage({route}: SplitExpensePageProps) { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses, splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal ?? 0, }, diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 1cc2cfa78644..9fbe03268707 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2730,8 +2730,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -2820,8 +2820,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -2925,8 +2925,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -7361,8 +7361,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, @@ -7490,8 +7490,8 @@ describe('actions/IOU', () => { allTransactionsList: allTransactions, allReportsList: allReports, transactionData: { - reportID: draftTransaction?.reportID ?? '', - originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? '', + reportID: draftTransaction?.reportID ?? String(CONST.DEFAULT_NUMBER_ID), + originalTransactionID: draftTransaction?.comment?.originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID), splitExpenses: draftTransaction?.comment?.splitExpenses ?? [], splitExpensesTotal: draftTransaction?.comment?.splitExpensesTotal, }, From 8f47a3692d21f3dc75acb19dd7f7a51d87bbef92 Mon Sep 17 00:00:00 2001 From: Yauheni Horbach Date: Thu, 9 Oct 2025 17:33:47 +0200 Subject: [PATCH 016/208] Add small changes --- src/libs/actions/IOU.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 5a733d20b2e7..155927c60c2e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -13226,6 +13226,7 @@ function updateSplitTransactions({ const undeletedTransactions = originalChildTransactions.filter( (currentTransaction) => !processedChildTransactionIDs.includes(currentTransaction?.transactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID), ); + undeletedTransactions.forEach((undeletedTransaction) => { const splitTransaction = allTransactionsList?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${undeletedTransaction?.transactionID}`]; const splitReportActions = getAllReportActions(splitTransaction?.reportID); From 181f46492d06e2588321f876023af6f5d976dc4b Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 10 Oct 2025 04:04:29 +0200 Subject: [PATCH 017/208] Block report submission when strict policy rules are enabled with violations --- src/components/AnimatedSubmitButton/index.tsx | 27 ++++++++---- src/components/MoneyReportHeader.tsx | 27 +++++++++++- .../MoneyRequestReportPreviewContent.tsx | 41 +++++++++++++++---- src/hooks/useStrictPolicyRules.ts | 37 +++++++++++++++++ src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/ReportPreviewActionUtils.ts | 9 ++++ src/libs/ReportUtils.ts | 17 ++++++++ 8 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 src/hooks/useStrictPolicyRules.ts diff --git a/src/components/AnimatedSubmitButton/index.tsx b/src/components/AnimatedSubmitButton/index.tsx index aabe97dca511..0eb0b929f0bf 100644 --- a/src/components/AnimatedSubmitButton/index.tsx +++ b/src/components/AnimatedSubmitButton/index.tsx @@ -2,6 +2,7 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import Animated, {Keyframe, runOnJS, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; +import Tooltip from '@components/Tooltip'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; @@ -22,9 +23,15 @@ type AnimatedSubmitButtonProps = { // Function to call when the animation finishes onAnimationFinish: () => void; + + // Whether the button should be disabled + isDisabled?: boolean; + + // Tooltip text to show on hover when button is disabled + tooltipText?: string; }; -function AnimatedSubmitButton({success, text, onPress, isSubmittingAnimationRunning, onAnimationFinish}: AnimatedSubmitButtonProps) { +function AnimatedSubmitButton({success, text, onPress, isSubmittingAnimationRunning, onAnimationFinish, isDisabled, tooltipText}: AnimatedSubmitButtonProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const isAnimationRunning = isSubmittingAnimationRunning; @@ -123,12 +130,18 @@ function AnimatedSubmitButton({success, text, onPress, isSubmittingAnimationRunn )} {!isAnimationRunning && ( -