Skip to content

Commit 897ef5c

Browse files
authored
Merge pull request Expensify#87622 from software-mansion-labs/collectioneur/fix-flag-comment-page-in-thread
2 parents 9e543f2 + a44f476 commit 897ef5c

5 files changed

Lines changed: 100 additions & 17 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {normalizedConfigs} from '@libs/Navigation/linkingConfig/config';
2+
import type {DynamicRouteSuffix} from '@src/ROUTES';
3+
import type {Screen} from '@src/SCREENS';
4+
import {dynamicRoutePaths} from './isDynamicRouteSuffix';
5+
6+
/**
7+
* Checks if a screen name is a dynamic route screen
8+
* @param screenName - The name of the screen to check.
9+
* @returns True if the screen name is a dynamic route screen, false otherwise.
10+
*/
11+
function isDynamicRouteScreen(screenName: Screen): boolean {
12+
const screenPath = normalizedConfigs[screenName]?.path;
13+
14+
if (!screenPath) {
15+
return false;
16+
}
17+
18+
return dynamicRoutePaths.has(screenPath as DynamicRouteSuffix);
19+
}
20+
21+
export default isDynamicRouteScreen;

src/libs/Navigation/helpers/getAdaptedStateFromPath.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import NAVIGATORS from '@src/NAVIGATORS';
1010
import type {Route as RoutePath} from '@src/ROUTES';
1111
import ROUTES from '@src/ROUTES';
1212
import SCREENS from '@src/SCREENS';
13+
import type {Screen} from '@src/SCREENS';
1314
import findMatchingDynamicSuffix from './dynamicRoutesUtils/findMatchingDynamicSuffix';
1415
import getPathWithoutDynamicSuffix from './dynamicRoutesUtils/getPathWithoutDynamicSuffix';
16+
import isDynamicRouteScreen from './dynamicRoutesUtils/isDynamicRouteScreen';
1517
import findFocusedRouteWithOnyxTabGuard from './findFocusedRouteWithOnyxTabGuard';
1618
import getMatchingNewRoute from './getMatchingNewRoute';
1719
import getParamsFromRoute from './getParamsFromRoute';
@@ -54,8 +56,13 @@ function getSearchScreenNameForRoute(route: NavigationPartialRoute): string {
5456
}
5557

5658
function getMatchingFullScreenRoute(route: NavigationPartialRoute) {
59+
const isDynamicScreen = isDynamicRouteScreen(route.name as Screen);
60+
5761
// Check for backTo param. One screen with different backTo value may need different screens visible under the overlay.
58-
if (isRouteWithBackToParam(route)) {
62+
// Dynamic screens are skipped here because they never carry their own backTo - they only
63+
// inherit it from the screen underneath. Letting backTo dictate the full-screen route for
64+
// a dynamic screen would resolve the wrong page.
65+
if (isRouteWithBackToParam(route) && !isDynamicScreen) {
5966
const stateForBackTo = getStateFromPath(route.params.backTo as RoutePath);
6067

6168
// This may happen if the backTo url is invalid.

src/libs/Navigation/helpers/getPathFromState.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
import {findFocusedRoute, getPathFromState as RNGetPathFromState} from '@react-navigation/native';
22
import type {NavigationState, PartialState} from '@react-navigation/routers';
33
import {config, normalizedConfigs} from '@libs/Navigation/linkingConfig/config';
4-
import type {DynamicRouteSuffix} from '@src/ROUTES';
54
import type {Screen} from '@src/SCREENS';
65
import getDynamicRouteQueryParams from './dynamicRoutesUtils/getDynamicRouteQueryParams';
7-
import {dynamicRoutePaths} from './dynamicRoutesUtils/isDynamicRouteSuffix';
6+
import isDynamicRouteScreen from './dynamicRoutesUtils/isDynamicRouteScreen';
87
import splitPathAndQuery from './dynamicRoutesUtils/splitPathAndQuery';
98

109
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
1110

12-
/**
13-
* Checks if a screen name is a dynamic route screen
14-
*/
15-
function isDynamicRouteScreen(screenName: Screen): boolean {
16-
const screenPath = normalizedConfigs[screenName]?.path;
17-
18-
if (!screenPath) {
19-
return false;
20-
}
21-
22-
return dynamicRoutePaths.has(screenPath as DynamicRouteSuffix);
23-
}
24-
2511
/**
2612
* Resolves a single path segment: if it's a `:param` placeholder, replaces it
2713
* with the URL-encoded value from `params`; otherwise returns the segment as-is.

tests/navigation/getMatchingFullScreenRouteTests.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import getStateFromPath from '@libs/Navigation/helpers/getStateFromPath';
44
import SCREENS from '@src/SCREENS';
55

66
jest.mock('@libs/Navigation/linkingConfig/config', () => ({
7-
normalizedConfigs: {},
7+
normalizedConfigs: {
8+
DynamicScreen: {path: 'suffix-a'},
9+
},
810
screensWithOnyxTabNavigator: new Set(),
911
}));
1012

@@ -180,4 +182,30 @@ describe('getMatchingFullScreenRoute - dynamic suffix', () => {
180182
expect(mockGetStateFromPath).toHaveBeenCalledWith('/broken/path/suffix-a');
181183
expect(result).toBeUndefined();
182184
});
185+
186+
it('should ignore backTo for a dynamic screen and resolve full screen route via dynamic suffix instead', () => {
187+
const route = {
188+
name: 'DynamicScreen',
189+
path: '/base/suffix-a',
190+
params: {backTo: '/some/other/path'},
191+
};
192+
const fullScreenRoute = {name: SCREENS.HOME};
193+
const basePathState = {
194+
routes: [{name: 'BaseScreen'}, fullScreenRoute],
195+
index: 1,
196+
};
197+
198+
mockGetStateFromPath.mockImplementation((path: string) => {
199+
if (path === '/base') {
200+
return basePathState;
201+
}
202+
return {routes: [{name: 'WrongScreen'}], index: 0};
203+
});
204+
205+
const result = getMatchingFullScreenRoute(route);
206+
207+
expect(mockGetStateFromPath).toHaveBeenCalledWith('/base');
208+
expect(mockGetStateFromPath).not.toHaveBeenCalledWith('/some/other/path');
209+
expect(result).toEqual(fullScreenRoute);
210+
});
183211
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import isDynamicRouteScreen from '@libs/Navigation/helpers/dynamicRoutesUtils/isDynamicRouteScreen';
2+
import type {Screen} from '@src/SCREENS';
3+
import SCREENS from '@src/SCREENS';
4+
5+
describe('isDynamicRouteScreen', () => {
6+
it('should return true for a static dynamic route screen (DYNAMIC_VERIFY_ACCOUNT)', () => {
7+
expect(isDynamicRouteScreen(SCREENS.SETTINGS.DYNAMIC_VERIFY_ACCOUNT)).toBe(true);
8+
});
9+
10+
it('should return true for a multi-segment dynamic route screen (DYNAMIC_ADD_BANK_ACCOUNT_VERIFY_ACCOUNT)', () => {
11+
expect(isDynamicRouteScreen(SCREENS.SETTINGS.DYNAMIC_ADD_BANK_ACCOUNT_VERIFY_ACCOUNT)).toBe(true);
12+
});
13+
14+
it('should return true for a parametric dynamic route screen (DYNAMIC_FLAG_COMMENT)', () => {
15+
expect(isDynamicRouteScreen(SCREENS.DYNAMIC_FLAG_COMMENT)).toBe(true);
16+
});
17+
18+
it('should return true for DYNAMIC_KEYBOARD_SHORTCUTS', () => {
19+
expect(isDynamicRouteScreen(SCREENS.SETTINGS.DYNAMIC_KEYBOARD_SHORTCUTS)).toBe(true);
20+
});
21+
22+
it('should return true for DYNAMIC_ADDRESS_COUNTRY', () => {
23+
expect(isDynamicRouteScreen(SCREENS.SETTINGS.PROFILE.DYNAMIC_ADDRESS_COUNTRY)).toBe(true);
24+
});
25+
26+
it('should return false for a regular screen (HOME)', () => {
27+
expect(isDynamicRouteScreen(SCREENS.HOME)).toBe(false);
28+
});
29+
30+
it('should return false for a regular screen (REPORT)', () => {
31+
expect(isDynamicRouteScreen(SCREENS.REPORT)).toBe(false);
32+
});
33+
34+
it('should return false for a regular settings screen (Settings_Root)', () => {
35+
expect(isDynamicRouteScreen(SCREENS.SETTINGS.ROOT)).toBe(false);
36+
});
37+
38+
it('should return false for a screen name not present in normalizedConfigs', () => {
39+
expect(isDynamicRouteScreen('NonExistentScreen_12345' as Screen)).toBe(false);
40+
});
41+
});

0 commit comments

Comments
 (0)