-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[NO QA] Intercept and Validate Dynamic Suffixes (BATCH-3) #81304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
collectioneur
wants to merge
7
commits into
Expensify:main
Choose a base branch
from
software-mansion-labs:collectioneur/dynamic-routes-batch-3
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
eda27a9
add createDynamicRoute function
collectioneur 0f0d1e8
optimise isDynamicRouteSuffix function
collectioneur 443ab2d
add intercept and validate dynamic suffixes
collectioneur 0ceeb40
no need to add misspelled route name, there's no entry screens
collectioneur e1c6fa7
add a guard to prevent path = undefined
collectioneur 7417586
add filename to the error text
collectioneur ba436dc
Merge branch 'main' into collectioneur/dynamic-routes-batch-3
collectioneur File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import type {DynamicRouteSuffix, Route} from '@src/ROUTES'; | ||
| import isDynamicRouteSuffix from './isDynamicRouteSuffix'; | ||
|
|
||
| const combinePathAndSuffix = (path: string, suffix: string): Route => { | ||
| const [basePath, params] = path.split('?'); | ||
| let newPath = path.endsWith('/') ? `${basePath}${suffix}` : `${basePath}/${suffix}`; | ||
|
|
||
| if (params) { | ||
| newPath += `?${params}`; | ||
| } | ||
| return newPath as Route; | ||
| }; | ||
|
|
||
| /** Adds dynamic route name to the current URL and returns it */ | ||
| const createDynamicRoute = (dynamicRouteSuffix: DynamicRouteSuffix): Route => { | ||
| if (!isDynamicRouteSuffix(dynamicRouteSuffix)) { | ||
| // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
collectioneur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| throw new Error(`The route name ${dynamicRouteSuffix} is not supported in createDynamicRoute`); | ||
| } | ||
|
|
||
| const activeRoute = Navigation.getActiveRoute(); | ||
| return combinePathAndSuffix(activeRoute, dynamicRouteSuffix); | ||
| }; | ||
|
|
||
| export default createDynamicRoute; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /** | ||
| * Extracts the last segment from a URL path, removing query parameters and trailing slashes. | ||
| * | ||
| * @param path - The URL path to extract the suffix from (can be undefined) | ||
| * @returns The last segment of the path as a string | ||
| */ | ||
| function getLastSuffixFromPath(path: string | undefined): string { | ||
| const pathWithoutParams = path?.split('?').at(0); | ||
collectioneur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (!pathWithoutParams) { | ||
| throw new Error('[getLastSuffixFromPath.ts] Failed to parse the path, path is empty'); | ||
| } | ||
|
|
||
| const pathWithoutTrailingSlash = pathWithoutParams.endsWith('/') ? pathWithoutParams.slice(0, -1) : pathWithoutParams; | ||
|
|
||
| const lastSuffix = pathWithoutTrailingSlash.split('/').pop() ?? ''; | ||
collectioneur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return lastSuffix; | ||
| } | ||
|
|
||
| export default getLastSuffixFromPath; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import {normalizedConfigs} from '@libs/Navigation/linkingConfig/config'; | ||
| import {DYNAMIC_ROUTES} from '@src/ROUTES'; | ||
| import type {DynamicRouteSuffix} from '@src/ROUTES'; | ||
|
|
||
| type LeafRoute = { | ||
| name: string; | ||
| path: string; | ||
| }; | ||
|
|
||
| type NestedRoute = { | ||
| name: string; | ||
| state: { | ||
| routes: [RouteNode]; | ||
| index: 0; | ||
| }; | ||
| }; | ||
|
|
||
| type RouteNode = LeafRoute | NestedRoute; | ||
|
|
||
| function getRouteNamesForDynamicRoute(dynamicRouteName: DynamicRouteSuffix): string[] | null { | ||
| // Search through normalized configs to find matching path and extract navigation hierarchy | ||
| // routeNames contains the sequence of screen/navigator names that should be present in the navigation state | ||
| for (const [, config] of Object.entries(normalizedConfigs)) { | ||
| if (config.path === dynamicRouteName) { | ||
| return config.routeNames; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DYNAMIC_ROUTES) { | ||
| const routeConfig = getRouteNamesForDynamicRoute(DYNAMIC_ROUTES[dynamicRouteName].path); | ||
|
|
||
| if (!routeConfig) { | ||
| throw new Error(`No route configuration found for dynamic route '${dynamicRouteName}'`); | ||
| } | ||
|
|
||
| // Build navigation state by creating nested structure | ||
| const buildNestedState = (routes: string[], currentIndex: number): RouteNode => { | ||
| const currentRoute = routes.at(currentIndex); | ||
collectioneur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // If this is the last route, create leaf node with path | ||
| if (currentIndex === routes.length - 1) { | ||
| return { | ||
| name: currentRoute ?? '', | ||
collectioneur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| path, | ||
| }; | ||
| } | ||
|
|
||
| // Create intermediate node with nested state | ||
| return { | ||
| name: currentRoute ?? '', | ||
collectioneur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| state: { | ||
| routes: [buildNestedState(routes, currentIndex + 1)], | ||
collectioneur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| index: 0, | ||
| }, | ||
| }; | ||
| }; | ||
|
|
||
| // Start building from the first route | ||
| const rootRoute = {routes: [buildNestedState(routeConfig, 0)]}; | ||
|
|
||
| return rootRoute; | ||
| } | ||
|
|
||
| export default getStateForDynamicRoute; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import {DYNAMIC_ROUTES} from '@src/ROUTES'; | ||
| import type {DynamicRouteSuffix} from '@src/ROUTES'; | ||
|
|
||
| /** | ||
| * Checks if a suffix matches any dynamic route path in DYNAMIC_ROUTES. | ||
| */ | ||
| function isDynamicRouteSuffix(suffix: string): suffix is DynamicRouteSuffix { | ||
| return Object.values(DYNAMIC_ROUTES).some((route) => route.path === suffix); | ||
| } | ||
|
|
||
| export default isDynamicRouteSuffix; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.