diff --git a/contributingGuides/NAVIGATION.md b/contributingGuides/NAVIGATION.md index f0db6723b4e5..20d284b8e5a7 100644 --- a/contributingGuides/NAVIGATION.md +++ b/contributingGuides/NAVIGATION.md @@ -34,7 +34,7 @@ When creating RHP flows, you have to remember a couple of things: - We use a custom `goBack` function to handle the browser and the `react-navigation` history stack. Under the hood, it resolves to either replacing the current screen with the one we navigate to (deeplinking scenario) or just going back if we reached the current page by navigating in App (pops the screen). It ensures the requested behaviors on web, which is navigating back to the place from where you deeplinked when going into the RHP flow by it. -- If you want to navigate to a certain report after completing a flow related to it, e.g. `RequestMoney` flow with a certain group/user, you should use `Navigation.dismissModal` with this `reportID` as an argument. If, in the future, we would like to navigate to something different than the report after such flows, the API should be rather easy to change. We do it like that in order to replace the RHP flow with the new report instead of pushing it, so pressing the back button does not navigate back to the ending page of the flow. If we were to navigate to the same report, we just pop the RHP modal. +- If you want to navigate to a certain report after completing a flow related to it, e.g. `RequestMoney` flow with a certain group/user, you should use `Navigation.dismissModalWithReport` with this `reportID` as an argument. If, in the future, we would like to navigate to something different than the report after such flows, the API should be rather easy to change. We do it like that in order to replace the RHP flow with the new report instead of pushing it, so pressing the back button does not navigate back to the ending page of the flow. If we were to navigate to the same report, we just pop the RHP modal. ### Example of usage diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts index bb796c74eee1..1965d7a1bc0c 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts @@ -7,7 +7,7 @@ import type {RootNavigatorParamList, State} from '@libs/Navigation/types'; import * as SearchQueryUtils from '@libs/SearchQueryUtils'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; -import type {OpenWorkspaceSplitActionType, PushActionType, SwitchPolicyIdActionType} from './types'; +import type {OpenWorkspaceSplitActionType, PushActionType, ReplaceActionType, SwitchPolicyIdActionType} from './types'; const MODAL_ROUTES_TO_DISMISS: string[] = [ NAVIGATORS.WORKSPACE_SPLIT_NAVIGATOR, @@ -237,6 +237,29 @@ function handlePushSearchPageAction( return stackRouter.getStateForAction(state, updatedAction, configOptions); } +function handleReplaceReportsSplitNavigatorAction( + state: StackNavigationState, + action: ReplaceActionType, + configOptions: RouterConfigOptions, + stackRouter: Router, CommonActions.Action | StackActionType>, +) { + const stateWithReportsSplitNavigator = stackRouter.getStateForAction(state, action, configOptions); + + if (!stateWithReportsSplitNavigator) { + Log.hmmm('[handleReplaceReportsSplitNavigatorAction] ReportsSplitNavigator has not been found in the navigation state.'); + return null; + } + + const lastReportsSplitNavigator = stateWithReportsSplitNavigator.routes.at(-1); + + // ReportScreen should always be opened with an animation when replacing the navigator + if (lastReportsSplitNavigator?.key) { + reportsSplitsWithEnteringAnimation.add(lastReportsSplitNavigator.key); + } + + return stateWithReportsSplitNavigator; +} + /** * Handles the DISMISS_MODAL action. * If the last route is a modal route, it has to be popped from the navigation stack. @@ -275,6 +298,7 @@ export { handleDismissModalAction, handlePushReportSplitAction, handlePushSearchPageAction, + handleReplaceReportsSplitNavigatorAction, handleSwitchPolicyIDAction, handleSwitchPolicyIDFromSearchAction, handleNavigatingToModalFromModal, diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts index fcaad4ae232a..0e9b4b2c3669 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts @@ -14,10 +14,19 @@ import { handleOpenWorkspaceSplitAction, handlePushReportSplitAction, handlePushSearchPageAction, + handleReplaceReportsSplitNavigatorAction, handleSwitchPolicyIDAction, } from './GetStateForActionHandlers'; import syncBrowserHistory from './syncBrowserHistory'; -import type {DismissModalActionType, OpenWorkspaceSplitActionType, PushActionType, RootStackNavigatorAction, RootStackNavigatorRouterOptions, SwitchPolicyIdActionType} from './types'; +import type { + DismissModalActionType, + OpenWorkspaceSplitActionType, + PushActionType, + ReplaceActionType, + RootStackNavigatorAction, + RootStackNavigatorRouterOptions, + SwitchPolicyIdActionType, +} from './types'; function isOpenWorkspaceSplitAction(action: RootStackNavigatorAction): action is OpenWorkspaceSplitActionType { return action.type === CONST.NAVIGATION.ACTION_TYPE.OPEN_WORKSPACE_SPLIT; @@ -31,6 +40,10 @@ function isPushAction(action: RootStackNavigatorAction): action is PushActionTyp return action.type === CONST.NAVIGATION.ACTION_TYPE.PUSH; } +function isReplaceAction(action: RootStackNavigatorAction): action is ReplaceActionType { + return action.type === CONST.NAVIGATION.ACTION_TYPE.REPLACE; +} + function isDismissModalAction(action: RootStackNavigatorAction): action is DismissModalActionType { return action.type === CONST.NAVIGATION.ACTION_TYPE.DISMISS_MODAL; } @@ -82,6 +95,10 @@ function RootStackRouter(options: RootStackNavigatorRouterOptions) { return handleDismissModalAction(state, configOptions, stackRouter); } + if (isReplaceAction(action) && action.payload.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { + return handleReplaceReportsSplitNavigatorAction(state, action, configOptions, stackRouter); + } + if (isPushAction(action)) { if (action.payload.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR) { return handlePushReportSplitAction(state, action, configOptions, stackRouter, setActiveWorkspaceID); diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts index 6dae82db35b5..67d69f90a205 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/types.ts @@ -29,6 +29,8 @@ type SwitchPolicyIdActionType = RootStackNavigatorActionType & { type PushActionType = StackActionType & {type: typeof CONST.NAVIGATION.ACTION_TYPE.PUSH}; +type ReplaceActionType = StackActionType & {type: typeof CONST.NAVIGATION.ACTION_TYPE.REPLACE}; + type DismissModalActionType = RootStackNavigatorActionType & { type: typeof CONST.NAVIGATION.ACTION_TYPE.DISMISS_MODAL; }; @@ -49,6 +51,7 @@ export type { OpenWorkspaceSplitActionType, SwitchPolicyIdActionType, PushActionType, + ReplaceActionType, DismissModalActionType, RootStackNavigatorAction, RootStackNavigatorActionType, diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 26399ca62664..1798f19b5347 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -5,7 +5,7 @@ import {CommonActions, getPathFromState, StackActions} from '@react-navigation/n import omit from 'lodash/omit'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import type {Writable} from 'type-fest'; +import type {MergeExclusive, Writable} from 'type-fest'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import {shallowCompare} from '@libs/ObjectUtils'; @@ -24,6 +24,7 @@ import originalCloseRHPFlow from './helpers/closeRHPFlow'; import getPolicyIDFromState from './helpers/getPolicyIDFromState'; import getStateFromPath from './helpers/getStateFromPath'; import getTopmostReportParams from './helpers/getTopmostReportParams'; +import {isFullScreenName} from './helpers/isNavigatorName'; import isReportOpenInRHP from './helpers/isReportOpenInRHP'; import linkTo from './helpers/linkTo'; import getMinimalAction from './helpers/linkTo/getMinimalAction'; @@ -468,20 +469,25 @@ function waitForProtectedRoutes() { }); } -type NavigateToReportWithPolicyCheckPayload = {report?: OnyxEntry; reportID?: string; reportActionID?: string; referrer?: string; policyIDToCheck?: string}; +// It should not be possible to pass a report and a reportID at the same time. +type NavigateToReportWithPolicyCheckPayload = MergeExclusive<{report: OnyxEntry}, {reportID: string}> & { + reportActionID?: string; + referrer?: string; + policyIDToCheck?: string; +}; /** * Navigates to a report passed as a param (as an id or report object) and checks whether the target object belongs to the currently selected workspace. * If not, the current workspace is set to global. */ -function navigateToReportWithPolicyCheck({report, reportID, reportActionID, referrer, policyIDToCheck}: NavigateToReportWithPolicyCheckPayload, ref = navigationRef) { +function navigateToReportWithPolicyCheck({report, reportID, reportActionID, referrer, policyIDToCheck}: NavigateToReportWithPolicyCheckPayload, forceReplace = false, ref = navigationRef) { const targetReport = reportID ? {reportID, ...allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]} : report; const policyID = policyIDToCheck ?? getPolicyIDFromState(navigationRef.getRootState() as State); const policyMemberAccountIDs = getPolicyEmployeeAccountIDs(policyID); const shouldOpenAllWorkspace = isEmptyObject(targetReport) ? true : !doesReportBelongToWorkspace(targetReport, policyMemberAccountIDs, policyID); if ((shouldOpenAllWorkspace && !policyID) || !shouldOpenAllWorkspace) { - linkTo(ref.current, ROUTES.REPORT_WITH_ID.getRoute(targetReport?.reportID, reportActionID, referrer)); + linkTo(ref.current, ROUTES.REPORT_WITH_ID.getRoute(targetReport?.reportID, reportActionID, referrer), {forceReplace: !!forceReplace}); return; } @@ -497,6 +503,17 @@ function navigateToReportWithPolicyCheck({report, reportID, reportActionID, refe params.referrer = referrer; } + if (forceReplace) { + ref.dispatch( + StackActions.replace(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, { + policyID: undefined, + screen: SCREENS.REPORT, + params, + }), + ); + return; + } + ref.dispatch( StackActions.push(NAVIGATORS.REPORTS_SPLIT_NAVIGATOR, { policyID: undefined, @@ -527,23 +544,31 @@ function getReportRouteByID(reportID?: string, routes: NavigationRoute[] = navig /** * Closes the modal navigator (RHP, LHP, onboarding). */ -const dismissModal = (reportID?: string, ref = navigationRef) => { +const dismissModal = (ref = navigationRef) => { isNavigationReady().then(() => { ref.dispatch({type: CONST.NAVIGATION.ACTION_TYPE.DISMISS_MODAL}); - if (!reportID) { - return; - } - navigateToReportWithPolicyCheck({reportID}); }); }; /** * Dismisses the modal and opens the given report. */ -const dismissModalWithReport = (report: OnyxEntry, ref = navigationRef) => { +const dismissModalWithReport = (navigateToReportPayload: NavigateToReportWithPolicyCheckPayload, ref = navigationRef) => { isNavigationReady().then(() => { - ref.dispatch({type: CONST.NAVIGATION.ACTION_TYPE.DISMISS_MODAL}); - navigateToReportWithPolicyCheck({report}); + if (getIsNarrowLayout()) { + const topmostReportID = getTopmostReportId(); + const areReportsIDsDefined = !!topmostReportID && !!navigateToReportPayload.reportID; + const isReportsSplitTopmostFullScreen = ref.getRootState().routes.findLast((route) => isFullScreenName(route.name))?.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR; + if (topmostReportID === navigateToReportPayload.reportID && areReportsIDsDefined && isReportsSplitTopmostFullScreen) { + dismissModal(); + return; + } + const forceReplace = true; + navigateToReportWithPolicyCheck(navigateToReportPayload, forceReplace); + return; + } + dismissModal(); + navigateToReportWithPolicyCheck(navigateToReportPayload); }); }; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b9d1ce96e6a4..8bebcc86f748 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4717,7 +4717,11 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { } InteractionManager.runAfterInteractions(() => removeDraftTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID)); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : activeReportID); + if (isSearchTopmostFullScreenRoute() || !activeReportID) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: activeReportID}); + } const trackReport = Navigation.getReportRouteByID(linkedTrackedExpenseReportAction?.childReportID); if (trackReport?.key) { @@ -4801,7 +4805,12 @@ function submitPerDiemExpense(submitPerDiemExpenseInformation: PerDiemExpenseInf API.write(WRITE_COMMANDS.CREATE_PER_DIEM_REQUEST, parameters, onyxData); InteractionManager.runAfterInteractions(() => removeDraftTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID)); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : activeReportID); + if (isSearchTopmostFullScreenRoute() || !activeReportID) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: activeReportID}); + } + if (activeReportID) { notifyNewAction(activeReportID, payeeAccountID); } @@ -4868,7 +4877,7 @@ function sendInvoice( if (isSearchTopmostFullScreenRoute()) { Navigation.dismissModal(); } else { - Navigation.dismissModalWithReport(invoiceRoom); + Navigation.dismissModalWithReport({report: invoiceRoom}); } notifyNewAction(invoiceRoom.reportID, receiver.accountID); @@ -5094,7 +5103,11 @@ function trackExpense(params: CreateTrackExpenseParams) { } } InteractionManager.runAfterInteractions(() => removeDraftTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID)); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : activeReportID); + if (isSearchTopmostFullScreenRoute() || !activeReportID) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: activeReportID}); + } if (action === CONST.IOU.ACTION.SHARE) { if (isSearchTopmostFullScreenRoute() && activeReportID) { @@ -5676,7 +5689,12 @@ function splitBill({ API.write(WRITE_COMMANDS.SPLIT_BILL, parameters, onyxData); InteractionManager.runAfterInteractions(() => removeDraftTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID)); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : existingSplitChatReportID); + if (isSearchTopmostFullScreenRoute() || !existingSplitChatReportID) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: existingSplitChatReportID}); + } + notifyNewAction(splitData.chatReportID, currentUserAccountID); } @@ -5749,7 +5767,11 @@ function splitBillAndOpenReport({ API.write(WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT, parameters, onyxData); InteractionManager.runAfterInteractions(() => removeDraftTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID)); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : splitData.chatReportID); + if (isSearchTopmostFullScreenRoute()) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: splitData.chatReportID}); + } notifyNewAction(splitData.chatReportID, currentUserAccountID); } @@ -6072,7 +6094,7 @@ function startSplitBill({ API.write(WRITE_COMMANDS.START_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); - Navigation.dismissModalWithReport(splitChatReport); + Navigation.dismissModalWithReport({report: splitChatReport}); notifyNewAction(splitChatReport.reportID, currentUserAccountID); } @@ -6331,7 +6353,11 @@ function completeSplitBill( API.write(WRITE_COMMANDS.COMPLETE_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); InteractionManager.runAfterInteractions(() => removeDraftTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID)); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : chatReportID); + if (isSearchTopmostFullScreenRoute()) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: chatReportID}); + } notifyNewAction(chatReportID, sessionAccountID); } @@ -6514,7 +6540,11 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest API.write(WRITE_COMMANDS.CREATE_DISTANCE_REQUEST, parameters, onyxData); InteractionManager.runAfterInteractions(() => removeDraftTransaction(CONST.IOU.OPTIMISTIC_TRANSACTION_ID)); const activeReportID = isMoneyRequestReport && report?.reportID ? report.reportID : parameters.chatReportID; - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : activeReportID); + if (isSearchTopmostFullScreenRoute() || !activeReportID) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: activeReportID}); + } notifyNewAction(activeReportID, userAccountID); } @@ -8202,7 +8232,11 @@ function sendMoneyElsewhere(report: OnyxEntry, amount: number, API.write(WRITE_COMMANDS.SEND_MONEY_ELSEWHERE, params, {optimisticData, successData, failureData}); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : params.chatReportID); + if (isSearchTopmostFullScreenRoute()) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: params.chatReportID}); + } notifyNewAction(params.chatReportID, managerID); } @@ -8215,7 +8249,11 @@ function sendMoneyWithWallet(report: OnyxEntry, amount: number API.write(WRITE_COMMANDS.SEND_MONEY_WITH_WALLET, params, {optimisticData, successData, failureData}); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : params.chatReportID); + if (isSearchTopmostFullScreenRoute()) { + Navigation.dismissModal(); + } else { + Navigation.dismissModalWithReport({reportID: params.chatReportID}); + } notifyNewAction(params.chatReportID, managerID); } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9f725a3a2a02..cc8fc7febbee 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -62,6 +62,7 @@ import getEnvironment from '@libs/Environment/getEnvironment'; import type EnvironmentType from '@libs/Environment/getEnvironment/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import fileDownload from '@libs/fileDownload'; +import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import HttpUtils from '@libs/HttpUtils'; import isPublicScreenRoute from '@libs/isPublicScreenRoute'; import * as Localize from '@libs/Localize'; @@ -1187,6 +1188,11 @@ function navigateToAndOpenReport( // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server openReport(report?.reportID, '', userLogins, newChat, undefined, undefined, undefined, avatarFile); if (shouldDismissModal) { + if (getIsNarrowLayout()) { + Navigation.dismissModalWithReport({report}); + return; + } + Navigation.dismissModal(); } @@ -2394,7 +2400,7 @@ function navigateToConciergeChat(shouldDismissModal = false, checkIfCurrentPageA navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], shouldDismissModal); }); } else if (shouldDismissModal) { - Navigation.dismissModal(conciergeChatReportID); + Navigation.dismissModalWithReport({reportID: conciergeChatReportID}); } else { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeChatReportID), linkToOptions); } @@ -2503,7 +2509,7 @@ function addPolicyReport(policyReport: OptimisticChatReport) { }; API.write(WRITE_COMMANDS.ADD_WORKSPACE_ROOM, parameters, {optimisticData, successData, failureData}); - Navigation.dismissModalWithReport(policyReport); + Navigation.dismissModalWithReport({report: policyReport}); } /** Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. */ diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 0c3fc2b251a2..fd08ea92bb84 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -339,7 +339,7 @@ function createTaskAndNavigate( InteractionManager.runAfterInteractions(() => { clearOutTaskInfo(); }); - Navigation.dismissModal(parentReportID); + Navigation.dismissModalWithReport({reportID: parentReportID}); } notifyNewAction(parentReportID, currentUserAccountID); } diff --git a/src/libs/actions/TeachersUnite.ts b/src/libs/actions/TeachersUnite.ts index 44420d21a7db..42da9df63983 100644 --- a/src/libs/actions/TeachersUnite.ts +++ b/src/libs/actions/TeachersUnite.ts @@ -27,7 +27,7 @@ Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { sessionEmail = value?.email ?? ''; - sessionAccountID = value?.accountID ?? -1; + sessionAccountID = value?.accountID ?? CONST.DEFAULT_NUMBER_ID; }, }); @@ -69,7 +69,7 @@ function referTeachersUniteVolunteer(partnerUserID: string, firstName: string, l }; API.write(WRITE_COMMANDS.REFER_TEACHERS_UNITE_VOLUNTEER, parameters, {optimisticData}); - Navigation.dismissModal(publicRoomReportID); + Navigation.dismissModalWithReport({reportID: publicRoomReportID}); } /** @@ -193,7 +193,7 @@ function addSchoolPrincipal(firstName: string, partnerUserID: string, lastName: }; API.write(WRITE_COMMANDS.ADD_SCHOOL_PRINCIPAL, parameters, {optimisticData, successData, failureData}); - Navigation.dismissModal(expenseChatReportID); + Navigation.dismissModalWithReport({reportID: expenseChatReportID}); } export default {referTeachersUniteVolunteer, addSchoolPrincipal}; diff --git a/src/pages/AddPersonalBankAccountPage.tsx b/src/pages/AddPersonalBankAccountPage.tsx index c441174a21e5..c71b72b4ce2f 100644 --- a/src/pages/AddPersonalBankAccountPage.tsx +++ b/src/pages/AddPersonalBankAccountPage.tsx @@ -65,7 +65,7 @@ function AddPersonalBankAccountPage() { const onSuccessFallbackRoute = personalBankAccount?.onSuccessFallbackRoute ?? ''; if (exitReportID) { - Navigation.dismissModal(exitReportID); + Navigation.dismissModalWithReport({reportID: exitReportID}); } else if (shouldContinue && onSuccessFallbackRoute) { continueSetup(onSuccessFallbackRoute); } else { diff --git a/src/pages/EditReportFieldPage.tsx b/src/pages/EditReportFieldPage.tsx index fd617998da98..d0ca5b5e9c16 100644 --- a/src/pages/EditReportFieldPage.tsx +++ b/src/pages/EditReportFieldPage.tsx @@ -15,7 +15,7 @@ import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTop import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {EditRequestNavigatorParamList} from '@libs/Navigation/types'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getReportFieldKey, isInvoiceReport, isPaidGroupPolicyExpenseReport, isReportFieldDisabled, isReportFieldOfTypeTitle} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import * as ReportActions from '@src/libs/actions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -31,16 +31,16 @@ function EditReportFieldPage({route}: EditReportFieldPageProps) { const {windowWidth} = useWindowDimensions(); const styles = useThemeStyles(); const {backTo, reportID, policyID} = route.params; - const fieldKey = ReportUtils.getReportFieldKey(route.params.fieldID); + const fieldKey = getReportFieldKey(route.params.fieldID); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); const reportField = report?.fieldList?.[fieldKey] ?? policy?.fieldList?.[fieldKey]; const policyField = policy?.fieldList?.[fieldKey] ?? reportField; - const isDisabled = ReportUtils.isReportFieldDisabled(report, reportField, policy); + const isDisabled = isReportFieldDisabled(report, reportField, policy); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const {translate} = useLocalize(); - const isReportFieldTitle = ReportUtils.isReportFieldOfTypeTitle(reportField); - const reportFieldsEnabled = ((ReportUtils.isPaidGroupPolicyExpenseReport(report) || ReportUtils.isInvoiceReport(report)) && !!policy?.areReportFieldsEnabled) || isReportFieldTitle; + const isReportFieldTitle = isReportFieldOfTypeTitle(reportField); + const reportFieldsEnabled = ((isPaidGroupPolicyExpenseReport(report) || isInvoiceReport(report)) && !!policy?.areReportFieldsEnabled) || isReportFieldTitle; if (!reportFieldsEnabled || !reportField || !policyField || !report || isDisabled) { return ( @@ -71,14 +71,22 @@ function EditReportFieldPage({route}: EditReportFieldPageProps) { if (value !== '') { ReportActions.updateReportField(report.reportID, {...reportField, value}, reportField); } - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : report?.reportID); + if (isSearchTopmostFullScreenRoute()) { + Navigation.dismissModal(); + return; + } + Navigation.dismissModalWithReport({reportID: report?.reportID}); } }; const handleReportFieldDelete = () => { ReportActions.deleteReportField(report.reportID, reportField); setIsDeleteModalVisible(false); - Navigation.dismissModal(isSearchTopmostFullScreenRoute() ? undefined : report?.reportID); + if (isSearchTopmostFullScreenRoute()) { + Navigation.dismissModal(); + return; + } + Navigation.dismissModalWithReport({reportID: report?.reportID}); }; const fieldValue = isReportFieldTitle ? report.reportName ?? '' : reportField.value ?? reportField.defaultValue; diff --git a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx index a273b210efa9..5b38a557ad2c 100644 --- a/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx +++ b/src/pages/EnablePayments/AddBankAccount/AddBankAccount.tsx @@ -8,10 +8,10 @@ import useLocalize from '@hooks/useLocalize'; import useSubStep from '@hooks/useSubStep'; import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; +import {addPersonalBankAccount, clearPersonalBankAccount} from '@libs/actions/BankAccounts'; +import {continueSetup} from '@libs/actions/PaymentMethods'; +import {updateCurrentStep} from '@libs/actions/Wallet'; import Navigation from '@navigation/Navigation'; -import * as BankAccounts from '@userActions/BankAccounts'; -import * as PaymentMethods from '@userActions/PaymentMethods'; -import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -32,7 +32,7 @@ function AddBankAccount() { const selectedPlaidBankAccount = bankAccounts.find((bankAccount) => bankAccount.plaidAccountID === personalBankAccountDraft?.plaidAccountID); if (selectedPlaidBankAccount) { - BankAccounts.addPersonalBankAccount(selectedPlaidBankAccount); + addPersonalBankAccount(selectedPlaidBankAccount); } }, [personalBankAccountDraft?.plaidAccountID, plaidData?.bankAccounts]); @@ -45,11 +45,11 @@ function AddBankAccount() { const onSuccessFallbackRoute = personalBankAccount?.onSuccessFallbackRoute ?? ''; if (exitReportID) { - Navigation.dismissModal(exitReportID); + Navigation.dismissModalWithReport({reportID: exitReportID}); return; } if (shouldContinue && onSuccessFallbackRoute) { - PaymentMethods.continueSetup(onSuccessFallbackRoute); + continueSetup(onSuccessFallbackRoute); return; } Navigation.goBack(ROUTES.SETTINGS_WALLET); @@ -61,8 +61,8 @@ function AddBankAccount() { return; } if (screenIndex === 0) { - BankAccounts.clearPersonalBankAccount(); - Wallet.updateCurrentStep(null); + clearPersonalBankAccount(); + updateCurrentStep(null); Navigation.goBack(ROUTES.SETTINGS_WALLET); return; } diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index f49b50da3c6a..33ce838a2918 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -229,7 +229,11 @@ function NewChatPage() { const selectOption = useCallback( (option?: Option) => { if (option?.isSelfDM) { - Navigation.dismissModal(option.reportID); + if (!option.reportID) { + Navigation.dismissModal(); + return; + } + Navigation.dismissModalWithReport({reportID: option.reportID}); return; } if (selectedOptions.length && option) { diff --git a/src/pages/RoomInvitePage.tsx b/src/pages/RoomInvitePage.tsx index 700e2eaa68e7..9f77ec87fdee 100644 --- a/src/pages/RoomInvitePage.tsx +++ b/src/pages/RoomInvitePage.tsx @@ -16,21 +16,23 @@ import type {WithNavigationTransitionEndProps} from '@components/withNavigationT import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ReportActions from '@libs/actions/Report'; -import * as UserSearchPhraseActions from '@libs/actions/RoomMembersUserSearchPhrase'; +import {inviteToRoom, searchInServer} from '@libs/actions/Report'; +import {clearUserSearchPhrase, updateUserSearchPhrase} from '@libs/actions/RoomMembersUserSearchPhrase'; import {READ_COMMANDS} from '@libs/API/types'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import HttpUtils from '@libs/HttpUtils'; -import * as LoginUtils from '@libs/LoginUtils'; +import {appendCountryCode} from '@libs/LoginUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {RoomMembersNavigatorParamList} from '@libs/Navigation/types'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; -import * as PhoneNumber from '@libs/PhoneNumber'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as Report from '@userActions/Report'; +import type {MemberForList} from '@libs/OptionsListUtils'; +import {filterAndOrderOptions, formatMemberForList, getHeaderMessage, getMemberInviteOptions} from '@libs/OptionsListUtils'; +import {getLoginsByAccountIDs} from '@libs/PersonalDetailsUtils'; +import {addSMSDomainIfPhoneNumber, parsePhoneNumber} from '@libs/PhoneNumber'; +import {isPolicyEmployee} from '@libs/PolicyUtils'; +import type {MemberEmailsToAccountIDs} from '@libs/PolicyUtils'; +import type {OptionData} from '@libs/ReportUtils'; +import {getReportName, isHiddenForCurrentUser} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -42,7 +44,7 @@ import withReportOrNotFound from './home/report/withReportOrNotFound'; type RoomInvitePageProps = WithReportOrNotFoundProps & WithNavigationTransitionEndProps & PlatformStackScreenProps; -type Sections = Array>>; +type Sections = Array>>; function RoomInvitePage({ betas, report, @@ -55,7 +57,7 @@ function RoomInvitePage({ const {translate} = useLocalize(); const [userSearchPhrase] = useOnyx(ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(userSearchPhrase ?? ''); - const [selectedOptions, setSelectedOptions] = useState([]); + const [selectedOptions, setSelectedOptions] = useState([]); const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false}); const {options, areOptionsInitialized} = useOptionsList(); @@ -66,10 +68,10 @@ function RoomInvitePage({ ...CONST.EXPENSIFY_EMAILS_OBJECT, }; const visibleParticipantAccountIDs = Object.entries(report.participants ?? {}) - .filter(([, participant]) => participant && !ReportUtils.isHiddenForCurrentUser(participant.notificationPreference)) + .filter(([, participant]) => participant && !isHiddenForCurrentUser(participant.notificationPreference)) .map(([accountID]) => Number(accountID)); - PersonalDetailsUtils.getLoginsByAccountIDs(visibleParticipantAccountIDs).forEach((participant) => { - const smsDomain = PhoneNumber.addSMSDomainIfPhoneNumber(participant); + getLoginsByAccountIDs(visibleParticipantAccountIDs).forEach((participant) => { + const smsDomain = addSMSDomainIfPhoneNumber(participant); res[smsDomain] = true; }); @@ -81,16 +83,16 @@ function RoomInvitePage({ return {recentReports: [], personalDetails: [], userToInvite: null, currentUserOption: null}; } - const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], excludedUsers); + const inviteOptions = getMemberInviteOptions(options.personalDetails, betas ?? [], excludedUsers); // Update selectedOptions with the latest personalDetails information - const detailsMap: Record = {}; + const detailsMap: Record = {}; inviteOptions.personalDetails.forEach((detail) => { if (!detail.login) { return; } - detailsMap[detail.login] = OptionsListUtils.formatMemberForList(detail); + detailsMap[detail.login] = formatMemberForList(detail); }); - const newSelectedOptions: ReportUtils.OptionData[] = []; + const newSelectedOptions: OptionData[] = []; selectedOptions.forEach((option) => { newSelectedOptions.push(option.login && option.login in detailsMap ? {...detailsMap[option.login], isSelected: true} : option); }); @@ -108,7 +110,7 @@ function RoomInvitePage({ if (debouncedSearchTerm.trim() === '') { return defaultOptions; } - const filteredOptions = OptionsListUtils.filterAndOrderOptions(defaultOptions, debouncedSearchTerm, {excludeLogins: excludedUsers}); + const filteredOptions = filterAndOrderOptions(defaultOptions, debouncedSearchTerm, {excludeLogins: excludedUsers}); return filteredOptions; }, [debouncedSearchTerm, defaultOptions, excludedUsers]); @@ -127,13 +129,13 @@ function RoomInvitePage({ filterSelectedOptions = selectedOptions.filter((option) => { const accountID = option?.accountID; const isOptionInPersonalDetails = personalDetails ? personalDetails.some((personalDetail) => accountID && personalDetail?.accountID === accountID) : false; - const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(debouncedSearchTerm))); + const parsedPhoneNumber = parsePhoneNumber(appendCountryCode(Str.removeSMSDomain(debouncedSearchTerm))); const searchValue = parsedPhoneNumber.possible && parsedPhoneNumber.number ? parsedPhoneNumber.number.e164 : debouncedSearchTerm.toLowerCase(); const isPartOfSearchTerm = (option.text?.toLowerCase() ?? '').includes(searchValue) || (option.login?.toLowerCase() ?? '').includes(searchValue); return isPartOfSearchTerm || isOptionInPersonalDetails; }); } - const filterSelectedOptionsFormatted = filterSelectedOptions.map((selectedOption) => OptionsListUtils.formatMemberForList(selectedOption)); + const filterSelectedOptionsFormatted = filterSelectedOptions.map((selectedOption) => formatMemberForList(selectedOption)); sectionsArr.push({ title: undefined, @@ -143,7 +145,7 @@ function RoomInvitePage({ // Filtering out selected users from the search results const selectedLogins = selectedOptions.map(({login}) => login); const personalDetailsWithoutSelected = personalDetails ? personalDetails.filter(({login}) => !selectedLogins.includes(login)) : []; - const personalDetailsFormatted = personalDetailsWithoutSelected.map((personalDetail) => OptionsListUtils.formatMemberForList(personalDetail)); + const personalDetailsFormatted = personalDetailsWithoutSelected.map((personalDetail) => formatMemberForList(personalDetail)); const hasUnselectedUserToInvite = userToInvite && !selectedLogins.includes(userToInvite.login); sectionsArr.push({ @@ -154,7 +156,7 @@ function RoomInvitePage({ if (hasUnselectedUserToInvite) { sectionsArr.push({ title: undefined, - data: [OptionsListUtils.formatMemberForList(userToInvite)], + data: [formatMemberForList(userToInvite)], }); } @@ -162,10 +164,10 @@ function RoomInvitePage({ }, [inviteOptions, areOptionsInitialized, selectedOptions, debouncedSearchTerm, translate]); const toggleOption = useCallback( - (option: OptionsListUtils.MemberForList) => { + (option: MemberForList) => { const isOptionInList = selectedOptions.some((selectedOption) => selectedOption.login === option.login); - let newSelectedOptions: ReportUtils.OptionData[]; + let newSelectedOptions: OptionData[]; if (isOptionInList) { newSelectedOptions = selectedOptions.filter((selectedOption) => selectedOption.login !== option.login); } else { @@ -181,21 +183,21 @@ function RoomInvitePage({ // Non policy members should not be able to view the participants of a room const reportID = report?.reportID; - const isPolicyEmployee = useMemo(() => (report?.policyID ? PolicyUtils.isPolicyEmployee(report.policyID, policies as Record) : false), [report?.policyID, policies]); + const isPolicyEmployeeValue = useMemo(() => (report?.policyID ? isPolicyEmployee(report.policyID, policies as Record) : false), [report?.policyID, policies]); const backRoute = useMemo(() => { if (role === CONST.IOU.SHARE.ROLE.ACCOUNTANT) { return ROUTES.REPORT_WITH_ID.getRoute(reportID); } - return reportID && (isPolicyEmployee ? ROUTES.ROOM_MEMBERS.getRoute(reportID, backTo) : ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, backTo)); - }, [isPolicyEmployee, reportID, role, backTo]); - const reportName = useMemo(() => ReportUtils.getReportName(report), [report]); + return reportID && (isPolicyEmployeeValue ? ROUTES.ROOM_MEMBERS.getRoute(reportID, backTo) : ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, backTo)); + }, [isPolicyEmployeeValue, reportID, role, backTo]); + const reportName = useMemo(() => getReportName(report), [report]); const inviteUsers = useCallback(() => { HttpUtils.cancelPendingRequests(READ_COMMANDS.SEARCH_FOR_REPORTS); if (!validate()) { return; } - const invitedEmailsToAccountIDs: PolicyUtils.MemberEmailsToAccountIDs = {}; + const invitedEmailsToAccountIDs: MemberEmailsToAccountIDs = {}; selectedOptions.forEach((option) => { const login = option.login ?? ''; const accountID = option.accountID; @@ -205,15 +207,15 @@ function RoomInvitePage({ invitedEmailsToAccountIDs[login] = Number(accountID); }); if (reportID) { - Report.inviteToRoom(reportID, invitedEmailsToAccountIDs); + inviteToRoom(reportID, invitedEmailsToAccountIDs); } - UserSearchPhraseActions.clearUserSearchPhrase(); + clearUserSearchPhrase(); Navigation.navigate(backRoute); }, [selectedOptions, backRoute, reportID, validate]); const goBack = useCallback(() => { if (role === CONST.IOU.SHARE.ROLE.ACCOUNTANT) { - Navigation.dismissModal(reportID); + Navigation.dismissModalWithReport({reportID}); return; } Navigation.goBack(backRoute); @@ -227,20 +229,16 @@ function RoomInvitePage({ } if ( !inviteOptions.userToInvite && - excludedUsers[ - PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(searchValue)).possible - ? PhoneNumber.addSMSDomainIfPhoneNumber(LoginUtils.appendCountryCode(searchValue)) - : searchValue - ] + excludedUsers[parsePhoneNumber(appendCountryCode(searchValue)).possible ? addSMSDomainIfPhoneNumber(appendCountryCode(searchValue)) : searchValue] ) { return translate('messages.userIsAlreadyMember', {login: searchValue, name: reportName}); } - return OptionsListUtils.getHeaderMessage((inviteOptions.personalDetails ?? []).length !== 0, !!inviteOptions.userToInvite, debouncedSearchTerm); + return getHeaderMessage((inviteOptions.personalDetails ?? []).length !== 0, !!inviteOptions.userToInvite, debouncedSearchTerm); }, [debouncedSearchTerm, inviteOptions.userToInvite, inviteOptions.personalDetails, excludedUsers, translate, reportName]); useEffect(() => { - UserSearchPhraseActions.updateUserSearchPhrase(debouncedSearchTerm); - ReportActions.searchInServer(debouncedSearchTerm); + updateUserSearchPhrase(debouncedSearchTerm); + searchInServer(debouncedSearchTerm); }, [debouncedSearchTerm]); return ( @@ -272,7 +270,7 @@ function RoomInvitePage({ onSelectRow={toggleOption} onConfirm={inviteUsers} showScrollIndicator - shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} + shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()} showLoadingPlaceholder={!areOptionsInitialized} isLoadingNewOptions={!!isSearchingForReports} /> diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 805fda5c298a..e6d19a4d9815 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -66,7 +66,11 @@ function Confirmation() { const resolveDuplicates = useCallback(() => { IOU.resolveDuplicates(transactionsMergeParams); - Navigation.dismissModal(reportAction?.childReportID); + if (!reportAction?.childReportID) { + Navigation.dismissModal(); + return; + } + Navigation.dismissModalWithReport({reportID: reportAction.childReportID}); }, [transactionsMergeParams, reportAction?.childReportID]); const contextValue = useMemo( diff --git a/src/pages/TransactionReceiptPage.tsx b/src/pages/TransactionReceiptPage.tsx index 132d1607fb52..78a35fcd163b 100644 --- a/src/pages/TransactionReceiptPage.tsx +++ b/src/pages/TransactionReceiptPage.tsx @@ -59,7 +59,12 @@ function TransactionReceipt({route}: TransactionReceiptProps) { Navigation.dismissModal(); } else { const isOneTransactionThread = isOneTransactionThreadReportUtils(report?.reportID, report?.parentReportID, parentReportAction); - Navigation.dismissModal(isOneTransactionThread ? report?.parentReportID : report?.reportID); + const reportID = isOneTransactionThread ? report?.parentReportID : report?.reportID; + if (!reportID) { + Navigation.dismissModal(); + return; + } + Navigation.dismissModalWithReport({reportID}); } }; diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 5aaf02d8ba3e..7611f448dd7c 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -18,15 +18,15 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ReportActions from '@libs/actions/Report'; +import {searchInServer} from '@libs/actions/Report'; +import {canModifyTask, editTaskAssignee, setAssigneeValue} from '@libs/actions/Task'; import {READ_COMMANDS} from '@libs/API/types'; import HttpUtils from '@libs/HttpUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {filterAndOrderOptions, getHeaderMessage, getValidOptions, isCurrentUser} from '@libs/OptionsListUtils'; +import {isOpenTaskReport, isTaskReport} from '@libs/ReportUtils'; import type {TaskDetailsNavigatorParamList} from '@navigation/types'; -import * as TaskActions from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -40,7 +40,7 @@ function useOptions() { const {options: optionsList, areOptionsInitialized} = useOptionsList(); const defaultOptions = useMemo(() => { - const {recentReports, personalDetails, userToInvite, currentUserOption} = OptionsListUtils.getValidOptions( + const {recentReports, personalDetails, userToInvite, currentUserOption} = getValidOptions( { reports: optionsList.reports, personalDetails: optionsList.personalDetails, @@ -51,7 +51,7 @@ function useOptions() { }, ); - const headerMessage = OptionsListUtils.getHeaderMessage((recentReports?.length || 0) + (personalDetails?.length || 0) !== 0 || !!currentUserOption, !!userToInvite, ''); + const headerMessage = getHeaderMessage((recentReports?.length || 0) + (personalDetails?.length || 0) !== 0 || !!currentUserOption, !!userToInvite, ''); if (isLoading) { // eslint-disable-next-line react-compiler/react-compiler @@ -68,11 +68,11 @@ function useOptions() { }, [optionsList.reports, optionsList.personalDetails, betas, isLoading]); const options = useMemo(() => { - const filteredOptions = OptionsListUtils.filterAndOrderOptions(defaultOptions, debouncedSearchValue.trim(), { + const filteredOptions = filterAndOrderOptions(defaultOptions, debouncedSearchValue.trim(), { excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT, maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW, }); - const headerMessage = OptionsListUtils.getHeaderMessage( + const headerMessage = getHeaderMessage( (filteredOptions.recentReports?.length || 0) + (filteredOptions.personalDetails?.length || 0) !== 0 || !!filteredOptions.currentUserOption, !!filteredOptions.userToInvite, debouncedSearchValue, @@ -104,9 +104,9 @@ function TaskAssigneeSelectorModal() { return; } const reportOnyx = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.reportID}`]; - if (reportOnyx && !ReportUtils.isTaskReport(reportOnyx)) { + if (reportOnyx && !isTaskReport(reportOnyx)) { Navigation.isNavigationReady().then(() => { - Navigation.dismissModal(reportOnyx.reportID); + Navigation.dismissModalWithReport({reportID: reportOnyx.reportID}); }); } return reports?.[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.reportID}`]; @@ -167,27 +167,27 @@ function TaskAssigneeSelectorModal() { // Check to see if we're editing a task and if so, update the assignee if (report) { if (option.accountID !== report.managerID) { - const assigneeChatReport = TaskActions.setAssigneeValue( + const assigneeChatReport = setAssigneeValue( option?.login ?? '', - option?.accountID ?? -1, + option?.accountID ?? CONST.DEFAULT_NUMBER_ID, report.reportID, undefined, // passing null as report because for editing task the report will be task details report page not the actual report where task was created - OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1, login: option?.login ?? ''}), + isCurrentUser({...option, accountID: option?.accountID ?? CONST.DEFAULT_NUMBER_ID, login: option?.login ?? ''}), ); // Pass through the selected assignee - TaskActions.editTaskAssignee(report, session?.accountID ?? -1, option?.login ?? '', option?.accountID, assigneeChatReport); + editTaskAssignee(report, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, option?.login ?? '', option?.accountID, assigneeChatReport); } InteractionManager.runAfterInteractions(() => { - Navigation.dismissModal(report.reportID); + Navigation.dismissModalWithReport({reportID: report?.reportID}); }); // If there's no report, we're creating a new task } else if (option.accountID) { - TaskActions.setAssigneeValue( + setAssigneeValue( option?.login ?? '', - option.accountID ?? -1, + option.accountID ?? CONST.DEFAULT_NUMBER_ID, task?.shareDestination ?? '', undefined, // passing null as report is null in this condition - OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1, login: option?.login ?? undefined}), + isCurrentUser({...option, accountID: option?.accountID ?? CONST.DEFAULT_NUMBER_ID, login: option?.login ?? undefined}), ); InteractionManager.runAfterInteractions(() => { Navigation.goBack(ROUTES.NEW_TASK.getRoute(backTo)); @@ -199,12 +199,12 @@ function TaskAssigneeSelectorModal() { const handleBackButtonPress = useCallback(() => Navigation.goBack(!route.params?.reportID ? ROUTES.NEW_TASK.getRoute(backTo) : backTo), [route.params, backTo]); - const isOpen = ReportUtils.isOpenTaskReport(report); - const canModifyTask = TaskActions.canModifyTask(report, currentUserPersonalDetails.accountID); - const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen); + const isOpen = isOpenTaskReport(report); + const canModifyTaskValue = canModifyTask(report, currentUserPersonalDetails.accountID); + const isTaskNonEditable = isTaskReport(report) && (!canModifyTaskValue || !isOpen); useEffect(() => { - ReportActions.searchInServer(debouncedSearchValue); + searchInServer(debouncedSearchValue); }, [debouncedSearchValue]); return ( diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index 9fdf1757ca96..d855228e3c97 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -59,14 +59,14 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti editTask(report, {description: values.description}); } - Navigation.dismissModal(report?.reportID); + Navigation.dismissModalWithReport({reportID: report?.reportID}); }, [report], ); if (!isTaskReport(report)) { Navigation.isNavigationReady().then(() => { - Navigation.dismissModal(report?.reportID); + Navigation.dismissModalWithReport({reportID: report?.reportID}); }); } const inputRef = useRef(null); diff --git a/src/pages/tasks/TaskTitlePage.tsx b/src/pages/tasks/TaskTitlePage.tsx index 30beb0bd700d..8d2f33d2cd75 100644 --- a/src/pages/tasks/TaskTitlePage.tsx +++ b/src/pages/tasks/TaskTitlePage.tsx @@ -63,14 +63,14 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) editTask(report, {title: values.title}); } - Navigation.dismissModal(report?.reportID); + Navigation.dismissModalWithReport({reportID: report?.reportID}); }, [report], ); if (!isTaskReport(report)) { Navigation.isNavigationReady().then(() => { - Navigation.dismissModal(report?.reportID); + Navigation.dismissModalWithReport({reportID: report?.reportID}); }); } diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index 3f48e3c7a2f9..458395e47dbf 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -24,6 +24,7 @@ import {clearDraftValues} from '@libs/actions/FormActions'; import {openExternalLink} from '@libs/actions/Link'; import {addMembersToWorkspace} from '@libs/actions/Policy/Member'; import {setWorkspaceInviteMessageDraft} from '@libs/actions/Policy/Policy'; +import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getAvatarsForAccountIDs} from '@libs/OptionsListUtils'; @@ -112,6 +113,12 @@ function WorkspaceInviteMessagePage({policy, route, currentUserPersonalDetails}: Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.dismissModal()); return; } + + if (getIsNarrowLayout()) { + Navigation.navigate(ROUTES.WORKSPACE_MEMBERS.getRoute(route.params.policyID), {forceReplace: true}); + return; + } + Navigation.setNavigationActionToMicrotaskQueue(() => { Navigation.dismissModal(); InteractionManager.runAfterInteractions(() => { diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index a7f6cc329ff9..c16757e154a2 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -135,7 +135,11 @@ function WorkspaceNewRoomPage() { if (!(((wasLoading && !isLoading) || (isOffline && isLoading)) && isEmptyObject(errorFields))) { return; } - Navigation.dismissModal(newRoomReportID); + if (!newRoomReportID) { + Navigation.dismissModal(); + return; + } + Navigation.dismissModalWithReport({reportID: newRoomReportID}); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we just want this to update on changing the form State }, [isLoading, errorFields]); diff --git a/src/pages/workspace/WorkspaceOverviewSharePage.tsx b/src/pages/workspace/WorkspaceOverviewSharePage.tsx index 2eab58a65e78..fe8d87f73e85 100644 --- a/src/pages/workspace/WorkspaceOverviewSharePage.tsx +++ b/src/pages/workspace/WorkspaceOverviewSharePage.tsx @@ -20,9 +20,9 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getDefaultWorkspaceAvatar, getRoom} from '@libs/ReportUtils'; import shouldAllowDownloadQRCode from '@libs/shouldAllowDownloadQRCode'; -import * as Url from '@libs/Url'; +import {addTrailingForwardSlash} from '@libs/Url'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import AccessOrNotFoundWrapper from './AccessOrNotFoundWrapper'; @@ -39,16 +39,16 @@ function WorkspaceOverviewSharePage({policy}: WithPolicyProps) { const session = useSession(); const policyName = policy?.name ?? ''; - const policyID = policy?.id ?? '-1'; + const policyID = policy?.id; const adminEmail = session?.email ?? ''; - const urlWithTrailingSlash = Url.addTrailingForwardSlash(environmentURL); + const urlWithTrailingSlash = addTrailingForwardSlash(environmentURL); - const url = `${urlWithTrailingSlash}${ROUTES.WORKSPACE_JOIN_USER.getRoute(policyID, adminEmail)}`; + const url = policyID ? `${urlWithTrailingSlash}${ROUTES.WORKSPACE_JOIN_USER.getRoute(policyID, adminEmail)}` : ''; const hasAvatar = !!policy?.avatarURL; const logo = hasAvatar ? (policy?.avatarURL as ImageSourcePropType) : undefined; - const defaultWorkspaceAvatar = ReportUtils.getDefaultWorkspaceAvatar(policyName) || Expensicons.FallbackAvatar; + const defaultWorkspaceAvatar = getDefaultWorkspaceAvatar(policyName) || Expensicons.FallbackAvatar; const defaultWorkspaceAvatarColors = StyleUtils.getDefaultWorkspaceAvatarColor(policyID); const svgLogo = !hasAvatar ? defaultWorkspaceAvatar : undefined; @@ -59,7 +59,7 @@ function WorkspaceOverviewSharePage({policy}: WithPolicyProps) { if (!policy?.id) { return undefined; } - return ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, policy?.id); + return getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, policy?.id); }, [policy?.id]); return ( @@ -89,7 +89,7 @@ function WorkspaceOverviewSharePage({policy}: WithPolicyProps) { if (!adminRoom?.reportID) { return; } - Navigation.dismissModal(adminRoom.reportID); + Navigation.dismissModalWithReport({reportID: adminRoom.reportID}); }} > {CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS} diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 39d5e5ca959a..16ce1caf630a 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -275,8 +275,8 @@ function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle { /** * Helper method to return workspace avatar color styles */ -function getDefaultWorkspaceAvatarColor(text: string): ViewStyle { - const colorHash = hashText(text.trim(), workspaceColorOptions.length); +function getDefaultWorkspaceAvatarColor(text?: string): ViewStyle { + const colorHash = hashText((text ?? '').trim(), workspaceColorOptions.length); return workspaceColorOptions.at(colorHash) ?? {backgroundColor: colors.blue200, fill: colors.blue700}; }