UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

179 lines (178 loc) 10.4 kB
import { findFocusedRoute, getActionFromState as getActionFromStateDefault, getPathFromState as getPathFromStateDefault, getStateFromPath as getStateFromPathDefault, useNavigationIndependentTree } from "@react-navigation/core"; import isEqual from "fast-deep-equal"; import * as React from "react"; import { rootState as routerRootState } from "../router/router"; import { ServerLocationContext } from "../router/serverLocationContext"; import { createMemoryHistory } from "./createMemoryHistory"; import { appendBaseUrl } from "./getPathFromState-mods"; const findMatchingState = (a, b) => { if (a === void 0 || b === void 0 || a.key !== b.key) return [void 0, void 0]; const aHistoryLength = a.history ? a.history.length : a.routes.length, bHistoryLength = b.history ? b.history.length : b.routes.length, aRoute = a.routes[a.index], bRoute = b.routes[b.index], aChildState = aRoute.state, bChildState = bRoute.state; return aHistoryLength !== bHistoryLength || aRoute.key !== bRoute.key || aChildState === void 0 || bChildState === void 0 || aChildState.key !== bChildState.key ? [a, b] : findMatchingState(aChildState, bChildState); }, series = (cb) => { let queue = Promise.resolve(); return () => { queue = queue.then(cb); }; }, linkingHandlers = []; function useLinking(ref, { enabled = !0, config, getStateFromPath = getStateFromPathDefault, getPathFromState = getPathFromStateDefault, getActionFromState = getActionFromStateDefault }, onUnhandledLinking) { const independent = useNavigationIndependentTree(); React.useEffect(() => { if (process.env.NODE_ENV === "production" || independent) return; enabled !== !1 && linkingHandlers.length && console.error( [ "Looks like you have configured linking in multiple places. This is likely an error since deep links should only be handled in one place to avoid conflicts. Make sure that:", "- You don't have multiple NavigationContainers in the app each with 'linking' enabled", "- Only a single instance of the root component is rendered" ].join(` `).trim() ); const handler = Symbol(); return enabled !== !1 && linkingHandlers.push(handler), () => { const index = linkingHandlers.indexOf(handler); index > -1 && linkingHandlers.splice(index, 1); }; }, [enabled, independent]); const [history] = React.useState(createMemoryHistory), enabledRef = React.useRef(enabled), configRef = React.useRef(config), getStateFromPathRef = React.useRef(getStateFromPath), getPathFromStateRef = React.useRef(getPathFromState), getActionFromStateRef = React.useRef(getActionFromState); React.useEffect(() => { enabledRef.current = enabled, configRef.current = config, getStateFromPathRef.current = getStateFromPath, getPathFromStateRef.current = getPathFromState, getActionFromStateRef.current = getActionFromState; }); const validateRoutesNotExistInRootState = React.useCallback( (state) => { const routeNames = ref.current?.getRootState()?.routeNames; return routeNames ? state?.routes.some((r) => !routeNames.includes(r.name)) : !1; }, [ref] ), server = { location: React.useContext(ServerLocationContext) }, getInitialState = React.useCallback(() => { let value; if (enabledRef.current) { const location2 = server?.location ?? (typeof window < "u" ? window.location : void 0), path = location2 ? location2.pathname + location2.search : void 0; process.env.ONE_DEBUG_ROUTER && console.info(`[one] \u{1F50D} getInitialState path=${path}`), path && (value = getStateFromPathRef.current(path, configRef.current), process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F50D} getInitialState result:", JSON.stringify(value, null, 2))), onUnhandledLinking(path); } const thenable = { // biome-ignore lint/suspicious/noThenProperty: don't check copied code then(onfulfilled) { return Promise.resolve(onfulfilled ? onfulfilled(value) : value); }, catch() { return thenable; } }; return thenable; }, []), previousIndexRef = React.useRef(void 0), previousStateRef = React.useRef(void 0), pendingPopStatePathRef = React.useRef(void 0); return React.useEffect(() => (previousIndexRef.current = history.index, history.listen(() => { const navigation = ref.current; if (!navigation || !enabled) return; const { location: location2 } = window, path = location2.pathname + location2.search, index = history.index, previousIndex = previousIndexRef.current ?? 0; process.env.ONE_DEBUG_ROUTER && console.info( `[one] \u{1F4DC} history.listen path=${path} index=${index} prevIndex=${previousIndex}` ), previousIndexRef.current = index, pendingPopStatePathRef.current = path; const record = history.get(index); if (record?.path === path && record?.state) { process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} history record found, resetRoot to:", record.state), navigation.resetRoot(record.state); return; } const state = getStateFromPathRef.current(path, configRef.current); if (process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} getStateFromPath result:", state), state) { if (onUnhandledLinking(path), validateRoutesNotExistInRootState(state)) { process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} routes not in root state, skipping"); return; } if (index > previousIndex || index === previousIndex && (!record || `${record?.path}${location2.hash}` === path)) { const action = getActionFromStateRef.current(state, configRef.current); if (process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} dispatching action:", action), action !== void 0) try { navigation.dispatch(action); } catch (e) { console.warn( `An error occurred when trying to handle the link '${path}': ${typeof e == "object" && e != null && "message" in e ? e.message : e}` ); } else process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} no action, resetRoot"), navigation.resetRoot(state); } else process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} going back, resetRoot"), navigation.resetRoot(state); } else process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} no state for path, resetRoot to undefined"), navigation.resetRoot(state); })), [enabled, history, onUnhandledLinking, ref, validateRoutesNotExistInRootState]), React.useEffect(() => { if (!enabled) return; const getPathForRoute = (route, state) => { let path; if (process.env.ONE_DEBUG_ROUTER && (console.info("[one] \u{1F4DC} getPathForRoute - route:", route), console.info("[one] \u{1F4DC} getPathForRoute - state:", JSON.stringify(state, null, 2))), route?.path) { const stateForPath = getStateFromPathRef.current(route.path, configRef.current); if (stateForPath) { const focusedRoute = findFocusedRoute(stateForPath); focusedRoute && focusedRoute.name === route.name && isEqual(focusedRoute.params, route.params) && (path = appendBaseUrl(route.path)); } } return path == null && (path = getPathFromStateRef.current(state, configRef.current), process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} getPathForRoute - computed from state:", path)), path; }; if (ref.current) { const refState = ref.current.getRootState(), state = routerRootState || refState; if (process.env.ONE_DEBUG_ROUTER && (console.info( "[one] \u{1F4DC} useEffect initial state check - refState:", JSON.stringify(refState, null, 2) ), console.info( "[one] \u{1F4DC} useEffect initial state check - routerRootState:", JSON.stringify(routerRootState, null, 2) )), state) { const route = findFocusedRoute(state); process.env.ONE_DEBUG_ROUTER && console.info("[one] \u{1F4DC} useEffect focused route:", route); const path = getPathForRoute(route, state); process.env.ONE_DEBUG_ROUTER && (console.info("[one] \u{1F4DC} initial history.replace - state:", JSON.stringify(state, null, 2)), console.info("[one] \u{1F4DC} initial history.replace - focusedRoute:", route), console.info("[one] \u{1F4DC} initial history.replace - computed path:", path)), previousStateRef.current === void 0 && (previousStateRef.current = refState), history.replace({ path, state }); } } const onStateChange = async () => { const navigation = ref.current; if (!navigation || !enabled) return; const previousState = previousStateRef.current, refState = navigation.getRootState(), state = routerRootState || refState; if (!state) return; const pendingPath = pendingPopStatePathRef.current, route = findFocusedRoute(state), path = getPathForRoute(route, state); previousStateRef.current = refState, pendingPopStatePathRef.current = void 0; const [previousFocusedState, focusedState] = findMatchingState(previousState, state); if (previousFocusedState && focusedState && // We should only handle push/pop if path changed from what was in last `popstate` // Otherwise it's likely a change triggered by `popstate` path !== pendingPath) { const historyDelta = (focusedState.history ? focusedState.history.length : focusedState.routes.length) - (previousFocusedState.history ? previousFocusedState.history.length : previousFocusedState.routes.length); if (historyDelta > 0) history.push({ path, state }); else if (historyDelta < 0) { const nextIndex = history.backIndex({ path }), currentIndex = history.index; try { nextIndex !== -1 && nextIndex < currentIndex && // We should only go back if the entry exists and it's less than current index history.get(nextIndex - currentIndex) ? await history.go(nextIndex - currentIndex) : await history.go(historyDelta), history.replace({ path, state }); } catch { } } else history.replace({ path, state }); } else history.replace({ path, state }); }; return ref.current?.addListener("state", series(onStateChange)); }, [enabled, history, ref]), { getInitialState }; } export { series, useLinking }; //# sourceMappingURL=useLinking.js.map