From 70afa5b08b563092ebf755d32fe2b71a54641c88 Mon Sep 17 00:00:00 2001 From: Andy Schwob Date: Fri, 9 Apr 2021 13:28:10 -0400 Subject: [PATCH] feat: allow beta router to handle univeral links with need for prefix --- .../development/dev-menu.component.web.tsx | 6 +++--- .../src/beta-app/router/history/history.ts | 5 ++--- .../src/beta-app/router/history/utils.base.ts | 13 +++++-------- .../beta-app/router/history/utils.native.tsx | 17 +++++++++++------ packages/fsapp/src/beta-app/router/index.ts | 2 +- packages/fsapp/src/beta-app/router/router.tsx | 7 +++---- .../fsapp/src/beta-app/router/router.web.tsx | 7 +++---- packages/fsapp/src/beta-app/router/types.ts | 11 ++++++----- packages/fsapp/src/beta-app/router/utils.ts | 8 ++++---- packages/fsapp/src/beta-app/utils.base.tsx | 15 ++++++++------- 10 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/fsapp/src/beta-app/development/dev-menu.component.web.tsx b/packages/fsapp/src/beta-app/development/dev-menu.component.web.tsx index 9c7d4d1ebf..4ccfa092ab 100644 --- a/packages/fsapp/src/beta-app/development/dev-menu.component.web.tsx +++ b/packages/fsapp/src/beta-app/development/dev-menu.component.web.tsx @@ -70,7 +70,7 @@ const styles = StyleSheet.create({ } }); -const extractDevRoutes = (routes?: Routes, prefix: string= ''): string[] => { +const extractDevRoutes = (routes?: Routes): string[] => { return ( routes?.reduce( (prev, route) => [ @@ -78,9 +78,9 @@ const extractDevRoutes = (routes?: Routes, prefix: string= ''): string[] => { ...((('component' in route || 'loadComponent' in route) && route.quickDevMenu) || 'children' in route ? [ - `${prefix}/${route.path ?? ''}`, + 'initialPath' in route ? `/${route.initialPath ?? ''}` : `/${route.path ?? ''}`, ...('children' in route - ? extractDevRoutes(route.children, `${prefix}/${route.path ?? ''}`) + ? extractDevRoutes(route.children) : []) ] : []) diff --git a/packages/fsapp/src/beta-app/router/history/history.ts b/packages/fsapp/src/beta-app/router/history/history.ts index 9057cc32a6..815b8d3146 100644 --- a/packages/fsapp/src/beta-app/router/history/history.ts +++ b/packages/fsapp/src/beta-app/router/history/history.ts @@ -74,11 +74,10 @@ export class History implements FSRouterHistory { constructor(private readonly routes: Routes) { this.observeNavigation(); - const tabRoutes = this.routes.filter(isTabRoute); const universalRoutes = this.routes.filter(isNotTabRoute); - const stackMatchers = tabRoutes.map(({ children, tab, path }) => - buildMatchers(children, tab, path ? `/${path}` : undefined) + const stackMatchers = tabRoutes.map(({ children, tab }) => + buildMatchers(children, tab) ); const promisedStacks = tabRoutes.map((route, i) => matchStack(route, stackMatchers[i])); diff --git a/packages/fsapp/src/beta-app/router/history/utils.base.ts b/packages/fsapp/src/beta-app/router/history/utils.base.ts index c222078f38..0507f6422a 100644 --- a/packages/fsapp/src/beta-app/router/history/utils.base.ts +++ b/packages/fsapp/src/beta-app/router/history/utils.base.ts @@ -66,16 +66,14 @@ const matchPath = (path: string | undefined, route: Route) => { const buildMatcher = async ( route: Route, - tab?: Tab, - prefix = '' + tab?: Tab ): Promise< (readonly [ (checkPath: string) => { params: RouteParams } | undefined, IndexedComponentRoute | RedirectRoute ])[] > => { - const { id, path } = buildPath(route, prefix); - + const { id, path } = buildPath(route); const matchingRoute = 'component' in route || 'loadComponent' in route ? ([ @@ -94,7 +92,7 @@ const buildMatcher = async ( const children = !matchingRoute && !matchingRedirect ? await mapPromisedChildren(route, childRoute => - buildMatcher(childRoute, 'tab' in route ? route.tab ?? tab : tab, path) + buildMatcher(childRoute, tab) ) : []; @@ -107,12 +105,11 @@ const buildMatcher = async ( export const buildMatchers = async ( routes: Routes, - tab?: Tab, - prefix?: string + tab?: Tab ) => { try { return routes - .map(route => buildMatcher(route, tab, prefix)) + .map(route => buildMatcher(route, 'tab' in route ? route.tab : tab)) .reduce(async (prev, next) => [...(await prev), ...(await next)], Promise.resolve([])); } catch (e) { return []; diff --git a/packages/fsapp/src/beta-app/router/history/utils.native.tsx b/packages/fsapp/src/beta-app/router/history/utils.native.tsx index 7246f57636..92e0ac77fc 100644 --- a/packages/fsapp/src/beta-app/router/history/utils.native.tsx +++ b/packages/fsapp/src/beta-app/router/history/utils.native.tsx @@ -3,7 +3,7 @@ import type { MatchingRoute, ParentRoute, Route, - TopLevelParentRoute + RouteCollection } from '../types'; import React from 'react'; @@ -15,9 +15,9 @@ import { defaultsDeep, uniqueId } from 'lodash-es'; import { Matchers, matchRoute } from './utils.base'; import { ROOT_STACK } from './constants'; -export const isTabRoute = (route: Route): route is TopLevelParentRoute => 'tab' in route; +export const isTabRoute = (route: Route): route is RouteCollection => 'tab' in route; -export const isNotTabRoute = (route: Route): route is Exclude => +export const isNotTabRoute = (route: Route): route is Exclude => !('tab' in route); export const createStack = ([route, title]: readonly [ @@ -65,8 +65,13 @@ export const createKey = () => { return Math.random().toString(36).substr(2, 8); }; -export const applyMatcher = async (matchers: Matchers, { path }: ParentRoute) => { - const component = await matchRoute(matchers, path ? `/${path}` : '/'); +export const applyMatcher = async (matchers: Matchers, route: RouteCollection | ParentRoute) => { + const component = + await matchRoute( + matchers, + 'initialPath' in route + ? `/${route.initialPath}` + : route.path ? `/${route.path}` : '/'); if (component) { const title = typeof component.title === 'function' @@ -84,7 +89,7 @@ export const applyMatcher = async (matchers: Matchers, { path }: ParentRoute) => return undefined; }; -export const matchStack = async (route: ParentRoute, matcher: Matchers) => { +export const matchStack = async (route: RouteCollection | ParentRoute, matcher: Matchers) => { const component = await applyMatcher(matcher, route); return component ? createStack(component) : undefined; }; diff --git a/packages/fsapp/src/beta-app/router/index.ts b/packages/fsapp/src/beta-app/router/index.ts index 1af5f28733..d29fb6abee 100644 --- a/packages/fsapp/src/beta-app/router/index.ts +++ b/packages/fsapp/src/beta-app/router/index.ts @@ -43,7 +43,7 @@ export type { MatchingRoute, Routes, Tab, - TopLevelParentRoute, + RouteCollection, TopBarStyle, ExternalRoute, ExternalRoutes, diff --git a/packages/fsapp/src/beta-app/router/router.tsx b/packages/fsapp/src/beta-app/router/router.tsx index c2f09e3e93..f1f12bb1bd 100644 --- a/packages/fsapp/src/beta-app/router/router.tsx +++ b/packages/fsapp/src/beta-app/router/router.tsx @@ -4,6 +4,7 @@ import type { FSRouterConstructor, InternalRouterConfig, Route, + RouteCollection, RouterConfig, Routes } from './types'; @@ -51,7 +52,6 @@ export class FSRouter extends FSRouterBase { private registerRoutes( routes: Routes | ExternalRoutes, - prefix: string = '', tab?: string | OptionsBottomTab ): void { let routeDetails = defaultActivatedRoute; @@ -60,8 +60,8 @@ export class FSRouter extends FSRouterBase { }); const addedRoutes = new Set(); - routes.forEach((route: Route | ExternalRoute) => { - const { path, id } = buildPath(route, prefix); + routes.forEach((route: RouteCollection | Route | ExternalRoute) => { + const { path, id } = buildPath(route); if (!addedRoutes.has(id)) { const LoadingPlaceholder = () => <>{this.options.loading}; if ('component' in route || 'loadComponent' in route) { @@ -113,7 +113,6 @@ export class FSRouter extends FSRouterBase { const tabAffinity = 'tab' in route ? route.tab : tab; this.registerRoutes( route.children, - path, 'tabAffinity' in route ? route.tabAffinity : tabAffinity ); } diff --git a/packages/fsapp/src/beta-app/router/router.web.tsx b/packages/fsapp/src/beta-app/router/router.web.tsx index 49340cd52f..20de2b8870 100644 --- a/packages/fsapp/src/beta-app/router/router.web.tsx +++ b/packages/fsapp/src/beta-app/router/router.web.tsx @@ -43,10 +43,9 @@ export class FSRouter extends FSRouterBase { private constructScreen = ( route: Route, loading: boolean, - routeDetails: ActivatedRoute, - prefix?: string + routeDetails: ActivatedRoute ): JSX.Element | JSX.Element[] => { - const { id, path } = useMemo(() => buildPath(route, prefix), []); + const { id, path } = useMemo(() => buildPath(route), []); if ('loadComponent' in route || 'component' in route) { const [filteredRoute, setFilteredRoute] = useState(() => routeDetails); @@ -101,7 +100,7 @@ export class FSRouter extends FSRouterBase { return ; } else if ('children' in route) { return route.children - .map(child => this.constructScreen(child, loading, routeDetails, path)) + .map(child => this.constructScreen(child, loading, routeDetails)) .reduce( (prev, next) => [...prev, ...(Array.isArray(next) ? next : [next])], [] diff --git a/packages/fsapp/src/beta-app/router/types.ts b/packages/fsapp/src/beta-app/router/types.ts index 9b8c398d31..14c2cd3cf0 100644 --- a/packages/fsapp/src/beta-app/router/types.ts +++ b/packages/fsapp/src/beta-app/router/types.ts @@ -109,9 +109,11 @@ export interface ParentRoute extends BaseRoute { readonly children: (Route & { tab?: never })[]; } -export interface TopLevelParentRoute extends ParentRoute { - readonly path: Exclude; +// initial path required +export interface RouteCollection { + readonly initialPath: Exclude; readonly tab: Tab; + readonly children: (Route & { tab?: never })[]; } export interface RedirectRoute extends BaseRoute { @@ -156,14 +158,13 @@ export type Route = | ComponentRoute | LazyComponentRoute | RedirectRoute - | ParentRoute - | TopLevelParentRoute; + | ParentRoute; /** * A list of routes * @see Route */ -export type Routes = readonly Route[]; +export type Routes = readonly (Route | RouteCollection)[]; export type ExternalRoute = Route & { readonly tabAffinity?: string }; export type ExternalRoutes = readonly ExternalRoute[]; diff --git a/packages/fsapp/src/beta-app/router/utils.ts b/packages/fsapp/src/beta-app/router/utils.ts index 92e94a81ff..afed79ab8b 100644 --- a/packages/fsapp/src/beta-app/router/utils.ts +++ b/packages/fsapp/src/beta-app/router/utils.ts @@ -18,7 +18,7 @@ export const resolveRoutes = async ({ (await (typeof externalRoutesFactory === 'function' ? externalRoutesFactory(api) : externalRoutesFactory)) ?? []; - + // tslint:disable-next-line: cyclomatic-complexity const findRoute = ( search: ExternalRoute, children = routes, @@ -28,7 +28,8 @@ export const resolveRoutes = async ({ for (const child of children) { // Replace Variables const searchPath = search.path?.replace(/:\w+(?=\/)?/, ':') ?? ''; - const childPath = child.path?.replace(/:\w+(?=\/)?/, ':') ?? ''; + const childPath = ('initialPath' in child ? child.initialPath : child.path) + ?.replace(/:\w+(?=\/)?/, ':') ?? ''; const prefixedPath = `${prefix}/${childPath}`; const tab = 'tab' in child ? child.tab : tabAffinity; @@ -72,8 +73,7 @@ export const resolveRoutes = async ({ ) .map(external => ({ ...external, - path: external.path?.replace(`${route.path}`, '') - .replace(/\/$/, '').replace(/^\//, '') + path: external.path?.replace(/\/$/, '').replace(/^\//, '') })), ...route.children ] diff --git a/packages/fsapp/src/beta-app/utils.base.tsx b/packages/fsapp/src/beta-app/utils.base.tsx index 657e141fc8..28f0340494 100644 --- a/packages/fsapp/src/beta-app/utils.base.tsx +++ b/packages/fsapp/src/beta-app/utils.base.tsx @@ -1,5 +1,5 @@ import type { Dictionary } from '@brandingbrand/fsfoundation'; -import type { Route } from './router'; +import type { Route, RouteCollection } from './router'; import loadable from '@loadable/component'; import { fromPairs } from 'lodash-es'; @@ -10,12 +10,13 @@ export const StaticImplements = () => (_constructor: export const isDefined = (value: T | undefined): value is T => value !== undefined; -export const buildPath = (route: Route, prefix?: string) => { - const path = - route.path !== undefined - ? `${prefix?.replace(/\/$/, '') ?? ''}/${route.path?.replace(/^\//, '') ?? ''}` - : prefix; - const id = path || `${prefix ?? ''}/undefined`; +export const buildPath = (route: Route | RouteCollection) => { + const path = 'initialPath' in route + ? `/${route.initialPath?.replace(/^\//, '') ?? ''}` + : route.path !== undefined + ? `/${route.path?.replace(/^\//, '') ?? ''}` + : '/'; + const id = path || `/undefined`; return { id, path }; };