From b3d1f130abf47dd4f4c78f75b2cbb66b45b3f083 Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:13:20 +0700 Subject: [PATCH 1/7] migrate REPORT_SETTINGS_NAME --- src/ROUTES.ts | 10 ++--- src/SCREENS.ts | 2 +- src/hooks/useReportFromDynamicRoute.ts | 37 +++++++++++++++++ .../ModalStackNavigators/index.tsx | 2 +- src/libs/Navigation/linkingConfig/config.ts | 4 +- src/libs/Navigation/types.ts | 6 +-- src/pages/ReportDetailsPage.tsx | 7 +++- src/pages/settings/Report/DynamicNamePage.tsx | 41 +++++++++++++++++++ src/pages/settings/Report/RoomNamePage.tsx | 14 +++---- 9 files changed, 97 insertions(+), 26 deletions(-) create mode 100644 src/hooks/useReportFromDynamicRoute.ts create mode 100644 src/pages/settings/Report/DynamicNamePage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 26576e6149cf9..493ac50b83d79 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -97,6 +97,10 @@ const DYNAMIC_ROUTES = { path: 'owner-selector', entryScreens: [], }, + REPORT_SETTINGS_NAME: { + path: 'settings/name', + entryScreens: [SCREENS.REPORT_DETAILS.ROOT, SCREENS.RIGHT_MODAL.REPORT_SETTINGS, SCREENS.REPORT, SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.SEARCH.ROOT], + }, } as const satisfies DynamicRoutes; const ROUTES = { @@ -783,12 +787,6 @@ const ROUTES = { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/settings` as const, backTo), }, - REPORT_SETTINGS_NAME: { - route: 'r/:reportID/settings/name', - - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/settings/name` as const, backTo), - }, REPORT_SETTINGS_NOTIFICATION_PREFERENCES: { route: 'r/:reportID/settings/notification-preferences', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index c005d3b6d5b87..5101f498a4b68 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -443,7 +443,7 @@ const SCREENS = { REPORT_SETTINGS: { ROOT: 'Report_Settings_Root', - NAME: 'Report_Settings_Name', + DYNAMIC_SETTINGS_NAME: 'Dynamic_Report_Settings_Name', NOTIFICATION_PREFERENCES: 'Report_Settings_Notification_Preferences', WRITE_CAPABILITY: 'Report_Settings_Write_Capability', VISIBILITY: 'Report_Settings_Visibility', diff --git a/src/hooks/useReportFromDynamicRoute.ts b/src/hooks/useReportFromDynamicRoute.ts new file mode 100644 index 0000000000000..97bb15985c767 --- /dev/null +++ b/src/hooks/useReportFromDynamicRoute.ts @@ -0,0 +1,37 @@ +import {useRoute} from '@react-navigation/native'; +import {useMemo} from 'react'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report} from '@src/types/onyx'; +import useOnyx from './useOnyx'; + +type UseReportFromDynamicRouteResult = { + report: Report | null | undefined; + reportID: string; + isLoading: boolean; +}; + +/** + * Hook to extract reportID from dynamic route path and fetch the report + * Use this for dynamic routes like /r/123/settings/name where reportID is in the URL path + */ +function useReportFromDynamicRoute(): UseReportFromDynamicRouteResult { + const route = useRoute(); + + // Extract reportID from the current path + const reportID = useMemo(() => { + const currentPath = route.path ?? ''; + const match = currentPath.match(/\/r\/([^/]+)/); + return match ? match[1] : ''; + }, [route.path]); + + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); + + return { + report, + reportID, + isLoading: !reportID || (!report && !!isLoadingReportData), + }; +} + +export default useReportFromDynamicRoute; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 9bc4968b63603..5d719a3b4ada2 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -277,7 +277,7 @@ const ReportChangeApproverModalStackNavigator = createModalStackNavigator({ [SCREENS.REPORT_SETTINGS.ROOT]: () => require('../../../../pages/settings/Report/ReportSettingsPage').default, - [SCREENS.REPORT_SETTINGS.NAME]: () => require('../../../../pages/settings/Report/NamePage').default, + [SCREENS.REPORT_SETTINGS.DYNAMIC_SETTINGS_NAME]: () => require('../../../../pages/settings/Report/DynamicNamePage').default, [SCREENS.REPORT_SETTINGS.NOTIFICATION_PREFERENCES]: () => require('../../../../pages/settings/Report/NotificationPreferencePage').default, [SCREENS.REPORT_SETTINGS.WRITE_CAPABILITY]: () => require('../../../../pages/settings/Report/WriteCapabilityPage').default, [SCREENS.REPORT_SETTINGS.VISIBILITY]: () => require('../../../../pages/settings/Report/VisibilityPage').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 517f361c2fedc..0289c3b7975b4 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1399,9 +1399,7 @@ const config: LinkingOptions['config'] = { [SCREENS.REPORT_SETTINGS.ROOT]: { path: ROUTES.REPORT_SETTINGS.route, }, - [SCREENS.REPORT_SETTINGS.NAME]: { - path: ROUTES.REPORT_SETTINGS_NAME.route, - }, + [SCREENS.REPORT_SETTINGS.DYNAMIC_SETTINGS_NAME]: DYNAMIC_ROUTES.REPORT_SETTINGS_NAME.path, [SCREENS.REPORT_SETTINGS.NOTIFICATION_PREFERENCES]: { path: ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 0e5ca1ef035ae..6913d32a7c994 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1685,11 +1685,7 @@ type ReportSettingsNavigatorParamList = { // eslint-disable-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.DYNAMIC_SETTINGS_NAME]: undefined; [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 diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index da4bb7a036755..dc7df3c9b1bec 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -43,6 +43,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import getBase62ReportID from '@libs/getBase62ReportID'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; +import createDynamicRoute from '@libs/Navigation/helpers/createDynamicRoute'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportDetailsNavigatorParamList, RightModalNavigatorParamList} from '@libs/Navigation/types'; @@ -125,7 +126,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; @@ -818,7 +819,9 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail furtherDetails={chatRoomSubtitle && !isGroupChat ? additionalRoomDetails : ''} furtherDetailsNumberOfLines={isWorkspaceChat ? 0 : undefined} furtherDetailsStyle={isWorkspaceChat ? [styles.textAlignCenter, styles.breakWord] : undefined} - onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NAME.getRoute(report.reportID, backTo))} + onPress={() => { + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.REPORT_SETTINGS_NAME.path)); + }} numberOfLinesTitle={isThread ? 2 : 0} shouldBreakWord /> diff --git a/src/pages/settings/Report/DynamicNamePage.tsx b/src/pages/settings/Report/DynamicNamePage.tsx new file mode 100644 index 0000000000000..6be477e31e3ff --- /dev/null +++ b/src/pages/settings/Report/DynamicNamePage.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; +import useReportFromDynamicRoute from '@hooks/useReportFromDynamicRoute'; +import {isGroupChat, isTripRoom} from '@libs/ReportUtils'; +import GroupChatNameEditPage from '@pages/GroupChatNameEditPage'; +import TripChatNameEditPage from '@pages/TripChatNameEditPage'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import RoomNamePage from './RoomNamePage'; + +function DynamicNamePage() { + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.REPORT_SETTINGS_NAME.path); + const {report, isLoading} = useReportFromDynamicRoute(); + + if (isLoading) { + return ; + } + + if (isEmptyObject(report)) { + return ; + } + + if (isTripRoom(report)) { + return ; + } + + if (isGroupChat(report)) { + return ; + } + + return ( + + ); +} + +export default DynamicNamePage; diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 9cea524517111..ad84feb9cf295 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -1,4 +1,4 @@ -import {useIsFocused, useRoute} from '@react-navigation/native'; +import {useIsFocused} from '@react-navigation/native'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -15,24 +15,22 @@ import useReportIsArchived from '@hooks/useReportIsArchived'; import useThemeStyles from '@hooks/useThemeStyles'; import {addErrorMessage} from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {ReportSettingsNavigatorParamList} from '@libs/Navigation/types'; import {shouldDisableRename} from '@libs/ReportUtils'; import {isExistingRoomName, isReservedRoomName, isValidRoomNameWithoutLimits} from '@libs/ValidationUtils'; import {updatePolicyRoomName as updatePolicyRoomNameReportAction} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; +import type {Route} from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/RoomNameForm'; import type {Report} from '@src/types/onyx'; type RoomNamePageProps = { report: Report; + navigateBackTo?: Route; }; -function RoomNamePage({report}: RoomNamePageProps) { - const route = useRoute>(); +function RoomNamePage({report, navigateBackTo}: RoomNamePageProps) { const styles = useThemeStyles(); const roomNameInputRef = useRef(null); const isFocused = useIsFocused(); @@ -42,8 +40,8 @@ function RoomNamePage({report}: RoomNamePageProps) { const isReportArchived = useReportIsArchived(report?.reportID); const goBack = useCallback(() => { - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, route.params.backTo))); - }, [reportID, route.params.backTo]); + Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, navigateBackTo))); + }, [reportID, navigateBackTo]); const validate = useCallback( (values: FormOnyxValues) => { From 585e4f32addfe820838e783180bba27e009d4fb5 Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:21:53 +0700 Subject: [PATCH 2/7] update navigation back to --- src/pages/settings/Report/RoomNamePage.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index ad84feb9cf295..b3da6b9fd460a 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -20,7 +20,6 @@ import {isExistingRoomName, isReservedRoomName, isValidRoomNameWithoutLimits} fr import {updatePolicyRoomName as updatePolicyRoomNameReportAction} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/RoomNameForm'; import type {Report} from '@src/types/onyx'; @@ -35,13 +34,12 @@ function RoomNamePage({report, navigateBackTo}: RoomNamePageProps) { const roomNameInputRef = useRef(null); const isFocused = useIsFocused(); const {translate} = useLocalize(); - const reportID = report?.reportID; const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const isReportArchived = useReportIsArchived(report?.reportID); const goBack = useCallback(() => { - Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID, navigateBackTo))); - }, [reportID, navigateBackTo]); + Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(navigateBackTo)); + }, [navigateBackTo]); const validate = useCallback( (values: FormOnyxValues) => { From f7c1410f47eb74e53079b40bf0349d6172ae5a55 Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:31:21 +0700 Subject: [PATCH 3/7] remove unused component --- src/pages/settings/Report/NamePage.tsx | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 src/pages/settings/Report/NamePage.tsx diff --git a/src/pages/settings/Report/NamePage.tsx b/src/pages/settings/Report/NamePage.tsx deleted file mode 100644 index f64465a24782e..0000000000000 --- a/src/pages/settings/Report/NamePage.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import {isGroupChat, isTripRoom} from '@libs/ReportUtils'; -import type {ReportSettingsNavigatorParamList} from '@navigation/types'; -import GroupChatNameEditPage from '@pages/GroupChatNameEditPage'; -import withReportOrNotFound from '@pages/inbox/report/withReportOrNotFound'; -import type {WithReportOrNotFoundProps} from '@pages/inbox/report/withReportOrNotFound'; -import TripChatNameEditPage from '@pages/TripChatNameEditPage'; -import type SCREENS from '@src/SCREENS'; -import RoomNamePage from './RoomNamePage'; - -type NamePageProps = WithReportOrNotFoundProps & PlatformStackScreenProps; - -function NamePage({report}: NamePageProps) { - if (isTripRoom(report)) { - return ; - } - - if (isGroupChat(report)) { - return ; - } - - return ; -} - -export default withReportOrNotFound()(NamePage); From b48c7d6fa01a27e501abdd030fb568ee340afd4f Mon Sep 17 00:00:00 2001 From: Yehor Kharchenko Date: Thu, 12 Mar 2026 11:45:22 +0100 Subject: [PATCH 4/7] refactor report setting name without hook --- src/hooks/useReportFromDynamicRoute.ts | 37 ------------------- .../getStateForDynamicRoute.ts | 8 ++-- .../Navigation/helpers/getStateFromPath.ts | 2 +- src/libs/Navigation/types.ts | 4 +- .../inbox/report/withReportOrNotFound.tsx | 1 + src/pages/settings/Report/DynamicNamePage.tsx | 24 +++++------- .../getStateForDynamicRouteTests.ts | 20 ++++++++++ tests/navigation/getStateFromPathTests.ts | 5 ++- 8 files changed, 41 insertions(+), 60 deletions(-) delete mode 100644 src/hooks/useReportFromDynamicRoute.ts diff --git a/src/hooks/useReportFromDynamicRoute.ts b/src/hooks/useReportFromDynamicRoute.ts deleted file mode 100644 index 97bb15985c767..0000000000000 --- a/src/hooks/useReportFromDynamicRoute.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {useRoute} from '@react-navigation/native'; -import {useMemo} from 'react'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report} from '@src/types/onyx'; -import useOnyx from './useOnyx'; - -type UseReportFromDynamicRouteResult = { - report: Report | null | undefined; - reportID: string; - isLoading: boolean; -}; - -/** - * Hook to extract reportID from dynamic route path and fetch the report - * Use this for dynamic routes like /r/123/settings/name where reportID is in the URL path - */ -function useReportFromDynamicRoute(): UseReportFromDynamicRouteResult { - const route = useRoute(); - - // Extract reportID from the current path - const reportID = useMemo(() => { - const currentPath = route.path ?? ''; - const match = currentPath.match(/\/r\/([^/]+)/); - return match ? match[1] : ''; - }, [route.path]); - - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); - const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); - - return { - report, - reportID, - isLoading: !reportID || (!report && !!isLoadingReportData), - }; -} - -export default useReportFromDynamicRoute; diff --git a/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts b/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts index ce35dc567c20a..34eb53ba64c4e 100644 --- a/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts +++ b/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts @@ -6,7 +6,7 @@ import splitPathAndQuery from './splitPathAndQuery'; type LeafRoute = { name: string; path: string; - params?: Record; + params?: Record; }; type NestedRoute = { @@ -54,7 +54,7 @@ function getRouteNamesForDynamicRoute(dynamicRouteName: DynamicRouteSuffix): str return null; } -function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DYNAMIC_ROUTES) { +function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DYNAMIC_ROUTES, parentRouteParams?: Record) { const routeConfig = getRouteNamesForDynamicRoute(DYNAMIC_ROUTES[dynamicRouteName].path); const [, query] = splitPathAndQuery(path); const params = getParamsFromQuery(query); @@ -67,12 +67,12 @@ function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DY const buildNestedState = (routes: string[], currentIndex: number): RouteNode => { const currentRoute = routes.at(currentIndex); - // If this is the last route, create leaf node with path + // If this is the last route, create leaf node with path and inherited params if (currentIndex === routes.length - 1) { return { name: currentRoute ?? '', path, - params, + ...(parentRouteParams ? {params: parentRouteParams} : {}), }; } diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts index 92119fdbf2281..2c4d7a11dcce2 100644 --- a/src/libs/Navigation/helpers/getStateFromPath.ts +++ b/src/libs/Navigation/helpers/getStateFromPath.ts @@ -39,7 +39,7 @@ function getStateFromPath(path: Route): PartialState { if (focusedRoute?.name) { if (entryScreens.includes(focusedRoute.name as Screen)) { // Generate navigation state for the dynamic route - const dynamicRouteState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey); + const dynamicRouteState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey, focusedRoute?.params as Record | undefined); return dynamicRouteState; } diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 951c51f646b89..b9ade21eec391 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1683,7 +1683,9 @@ type ReportSettingsNavigatorParamList = { // eslint-disable-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.DYNAMIC_SETTINGS_NAME]: undefined; + [SCREENS.REPORT_SETTINGS.DYNAMIC_SETTINGS_NAME]: { + reportID: string; + }; [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 diff --git a/src/pages/inbox/report/withReportOrNotFound.tsx b/src/pages/inbox/report/withReportOrNotFound.tsx index 22d47b46b8bf8..6d7ada5a736cc 100644 --- a/src/pages/inbox/report/withReportOrNotFound.tsx +++ b/src/pages/inbox/report/withReportOrNotFound.tsx @@ -52,6 +52,7 @@ type ScreenProps = | PlatformStackScreenProps | PlatformStackScreenProps | PlatformStackScreenProps + | PlatformStackScreenProps | PlatformStackScreenProps | PlatformStackScreenProps | PlatformStackScreenProps; diff --git a/src/pages/settings/Report/DynamicNamePage.tsx b/src/pages/settings/Report/DynamicNamePage.tsx index 6be477e31e3ff..8395590444673 100644 --- a/src/pages/settings/Report/DynamicNamePage.tsx +++ b/src/pages/settings/Report/DynamicNamePage.tsx @@ -1,26 +1,20 @@ import React from 'react'; -import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import useDynamicBackPath from '@hooks/useDynamicBackPath'; -import useReportFromDynamicRoute from '@hooks/useReportFromDynamicRoute'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {isGroupChat, isTripRoom} from '@libs/ReportUtils'; +import type {ReportSettingsNavigatorParamList} from '@navigation/types'; import GroupChatNameEditPage from '@pages/GroupChatNameEditPage'; +import withReportOrNotFound from '@pages/inbox/report/withReportOrNotFound'; +import type {WithReportOrNotFoundProps} from '@pages/inbox/report/withReportOrNotFound'; import TripChatNameEditPage from '@pages/TripChatNameEditPage'; import {DYNAMIC_ROUTES} from '@src/ROUTES'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import type SCREENS from '@src/SCREENS'; import RoomNamePage from './RoomNamePage'; -function DynamicNamePage() { - const backPath = useDynamicBackPath(DYNAMIC_ROUTES.REPORT_SETTINGS_NAME.path); - const {report, isLoading} = useReportFromDynamicRoute(); - - if (isLoading) { - return ; - } +type DynamicNamePageProps = WithReportOrNotFoundProps & PlatformStackScreenProps; - if (isEmptyObject(report)) { - return ; - } +function DynamicNamePage({report}: DynamicNamePageProps) { + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.REPORT_SETTINGS_NAME.path); if (isTripRoom(report)) { return ; @@ -38,4 +32,4 @@ function DynamicNamePage() { ); } -export default DynamicNamePage; +export default withReportOrNotFound()(DynamicNamePage); diff --git a/tests/navigation/getStateForDynamicRouteTests.ts b/tests/navigation/getStateForDynamicRouteTests.ts index cf3dc7c588f0e..b1f337ed6c859 100644 --- a/tests/navigation/getStateForDynamicRouteTests.ts +++ b/tests/navigation/getStateForDynamicRouteTests.ts @@ -31,6 +31,7 @@ jest.mock('@src/ROUTES', () => ({ type LeafRoute = { name: string; path: string; + params?: Record; }; type NestedRoute = { @@ -111,4 +112,23 @@ describe('getStateForDynamicRoute', () => { expect(state?.index).toBe(0); expect(Array.isArray(state?.routes)).toBe(true); }); + + it('should inherit parent route params on the leaf node', () => { + const path = '/r/12345/settings/name'; + const parentParams = {reportID: '12345'}; + const result = getStateForDynamicRoute(path, KEY_TEST as unknown as keyof typeof DYNAMIC_ROUTES, parentParams); + + const rootRoute = result.routes.at(0) as NestedRoute | undefined; + const leafRoute = rootRoute?.state.routes.at(0) as LeafRoute | undefined; + expect(leafRoute?.params).toEqual(parentParams); + }); + + it('should not include params on the leaf node when parentRouteParams is undefined', () => { + const path = '/some/path/test-path'; + const result = getStateForDynamicRoute(path, KEY_TEST as unknown as keyof typeof DYNAMIC_ROUTES); + + const rootRoute = result.routes.at(0) as NestedRoute | undefined; + const leafRoute = rootRoute?.state.routes.at(0) as LeafRoute | undefined; + expect(leafRoute?.params).toBeUndefined(); + }); }); diff --git a/tests/navigation/getStateFromPathTests.ts b/tests/navigation/getStateFromPathTests.ts index ffa5cb2ace840..5808b6f164e4d 100644 --- a/tests/navigation/getStateFromPathTests.ts +++ b/tests/navigation/getStateFromPathTests.ts @@ -57,9 +57,10 @@ describe('getStateFromPath', () => { it('should generate dynamic state when authorized screen is focused', () => { const fullPath = '/settings/wallet/verify-account'; const baseRouteState = {routes: [{name: 'Wallet'}]}; + const focusedRouteParams = {walletID: '456'}; mockRNGetStateFromPath.mockReturnValue(baseRouteState); - mockFindFocusedRoute.mockReturnValue({name: 'Wallet'}); + mockFindFocusedRoute.mockReturnValue({name: 'Wallet', params: focusedRouteParams}); const expectedDynamicState = {routes: [{name: 'DynamicRoot'}]}; mockGetStateForDynamicRoute.mockReturnValue(expectedDynamicState); @@ -67,7 +68,7 @@ describe('getStateFromPath', () => { const result = getStateFromPath(fullPath as unknown as Route); expect(result).toBe(expectedDynamicState); - expect(mockGetStateForDynamicRoute).toHaveBeenCalledWith(fullPath, 'VERIFY_ACCOUNT'); + expect(mockGetStateForDynamicRoute).toHaveBeenCalledWith(fullPath, 'VERIFY_ACCOUNT', focusedRouteParams); }); it('should fallback to standard RN parsing if focused screen is NOT authorized for dynamic route', () => { From b4a6ecfb58df000bcb3b689707d6b726a2bd2985 Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Thu, 12 Mar 2026 22:22:42 +0700 Subject: [PATCH 5/7] update path --- contributingGuides/NAVIGATION.md | 4 ++-- src/pages/ReportDetailsPage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contributingGuides/NAVIGATION.md b/contributingGuides/NAVIGATION.md index 9cee8c3b9f0a9..2d1e35c4ab1e8 100644 --- a/contributingGuides/NAVIGATION.md +++ b/contributingGuides/NAVIGATION.md @@ -711,10 +711,10 @@ Do not use dynamic routes when: - `path`: The URL suffix (e.g. `'verify-account'`). - `entryScreens`: List of screen names that are allowed to have this suffix appended (access control; see [Entry Screens (Access Control)](#entry-screens-access-control)). -`createDynamicRoute(suffix)` — [`createDynamicRoute.ts`](src/libs/Navigation/helpers/createDynamicRoute.ts). Accepts a `DynamicRouteSuffix` (from `DYNAMIC_ROUTES`), appends it to the current active route and returns the full route. Use the following when navigating to a dynamic route: +`createDynamicRoute(suffix)` — [`createDynamicRoute.ts`](src/libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute.ts). Accepts a `DynamicRouteSuffix` (from `DYNAMIC_ROUTES`), appends it to the current active route and returns the full route. Use the following when navigating to a dynamic route: ```ts -import createDynamicRoute from '@libs/Navigation/helpers/createDynamicRoute'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index dc7df3c9b1bec..3b94341c71c1e 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -43,7 +43,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import getBase62ReportID from '@libs/getBase62ReportID'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; -import createDynamicRoute from '@libs/Navigation/helpers/createDynamicRoute'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportDetailsNavigatorParamList, RightModalNavigatorParamList} from '@libs/Navigation/types'; From 38cfbc5f01ec7f105f88206271b202ae7b53a4ab Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Thu, 12 Mar 2026 22:28:50 +0700 Subject: [PATCH 6/7] update params --- .../helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts b/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts index 34eb53ba64c4e..ed257c9f9fb69 100644 --- a/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts +++ b/src/libs/Navigation/helpers/dynamicRoutesUtils/getStateForDynamicRoute.ts @@ -72,7 +72,10 @@ function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DY return { name: currentRoute ?? '', path, - ...(parentRouteParams ? {params: parentRouteParams} : {}), + params: { + ...params, + ...(parentRouteParams ?? {}), + }, }; } From 68f157f0cfa288ecfc49a14e5786fc1ed5553562 Mon Sep 17 00:00:00 2001 From: huutech <20178761+huult@users.noreply.github.com> Date: Sun, 15 Mar 2026 23:20:27 +0700 Subject: [PATCH 7/7] update entry screen --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 7f5e5f9edb4e7..ce9250bd67432 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -101,7 +101,7 @@ const DYNAMIC_ROUTES = { }, REPORT_SETTINGS_NAME: { path: 'settings/name', - entryScreens: [SCREENS.REPORT_DETAILS.ROOT, SCREENS.RIGHT_MODAL.REPORT_SETTINGS, SCREENS.REPORT, SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.SEARCH.ROOT], + entryScreens: [SCREENS.REPORT_DETAILS.ROOT], }, ADDRESS_COUNTRY: { path: 'country',