494 lines
24 KiB
JavaScript
494 lines
24 KiB
JavaScript
"use strict";
|
|
'use client';
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.StackRouter = exports.stackRouterOverride = void 0;
|
|
const native_1 = require("@react-navigation/native");
|
|
const non_secure_1 = require("nanoid/non-secure");
|
|
const react_1 = __importStar(require("react"));
|
|
const withLayoutContext_1 = require("./withLayoutContext");
|
|
const createNativeStackNavigator_1 = require("../fork/native-stack/createNativeStackNavigator");
|
|
const LinkPreviewContext_1 = require("../link/preview/LinkPreviewContext");
|
|
const navigationParams_1 = require("../navigationParams");
|
|
const useScreens_1 = require("../useScreens");
|
|
const stack_utils_1 = require("./stack-utils");
|
|
const children_1 = require("../utils/children");
|
|
const Protected_1 = require("../views/Protected");
|
|
const NativeStackNavigator = (0, createNativeStackNavigator_1.createNativeStackNavigator)().Navigator;
|
|
const RNStack = (0, withLayoutContext_1.withLayoutContext)(NativeStackNavigator);
|
|
function isStackAction(action) {
|
|
return (action.type === 'PUSH' ||
|
|
action.type === 'NAVIGATE' ||
|
|
action.type === 'POP' ||
|
|
action.type === 'POP_TO_TOP' ||
|
|
action.type === 'REPLACE' ||
|
|
action.type === 'PRELOAD');
|
|
}
|
|
const isPreviewAction = (action) => !!action.payload &&
|
|
'params' in action.payload &&
|
|
typeof action.payload.params === 'object' &&
|
|
!!(0, navigationParams_1.getInternalExpoRouterParams)(action.payload?.params ?? undefined)[navigationParams_1.INTERNAL_EXPO_ROUTER_IS_PREVIEW_NAVIGATION_PARAM_NAME];
|
|
const getZoomTransitionIdFromAction = (action) => {
|
|
const allParams = !!action.payload && 'params' in action.payload && typeof action.payload.params === 'object'
|
|
? action.payload.params
|
|
: undefined;
|
|
const internalParams = (0, navigationParams_1.getInternalExpoRouterParams)(allParams ?? undefined);
|
|
const val = internalParams[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME];
|
|
if (val && typeof val === 'string') {
|
|
return val;
|
|
}
|
|
return undefined;
|
|
};
|
|
/**
|
|
* React Navigation matches a screen by its name or a 'getID' function that uniquely identifies a screen.
|
|
* When a screen has been uniquely identified, the Stack can only have one instance of that screen.
|
|
*
|
|
* Expo Router allows for a screen to be matched by name and path params, a 'getID' function or a singular id.
|
|
*
|
|
* Instead of reimplementing the entire StackRouter, we can override the getStateForAction method to handle the singular screen logic.
|
|
*
|
|
*/
|
|
const stackRouterOverride = (original) => {
|
|
return {
|
|
getStateForAction: (state, action, options) => {
|
|
if (action.target && action.target !== state.key) {
|
|
return null;
|
|
}
|
|
if (!isStackAction(action)) {
|
|
return original.getStateForAction(state, action, options);
|
|
}
|
|
// The dynamic getId added to an action, `router.push('screen', { singular: true })`
|
|
const actionSingularOptions = action.payload && 'singular' in action.payload
|
|
? action.payload.singular
|
|
: undefined;
|
|
// Handle if 'getID' or 'singular' is set.
|
|
function getIdFunction() {
|
|
// Actions can be fired by the user, so we do need to validate their structure.
|
|
if (!('payload' in action) ||
|
|
!action.payload ||
|
|
!('name' in action.payload) ||
|
|
typeof action.payload.name !== 'string') {
|
|
return;
|
|
}
|
|
const actionName = action.payload.name;
|
|
return (
|
|
// The dynamic singular added to an action, `router.push('screen', { singular: () => 'id' })`
|
|
getActionSingularIdFn(actionSingularOptions, actionName) ||
|
|
// The static getId added as a prop to `<Screen singular />` or `<Screen getId={} />`
|
|
options.routeGetIdList[actionName]);
|
|
}
|
|
const { routeParamList } = options;
|
|
switch (action.type) {
|
|
case 'PUSH':
|
|
case 'NAVIGATE': {
|
|
if (!state.routeNames.includes(action.payload.name)) {
|
|
return null;
|
|
}
|
|
// START FORK
|
|
const getId = getIdFunction();
|
|
// const getId = options.routeGetIdList[action.payload.name];
|
|
// END FORK
|
|
const id = getId?.({ params: action.payload.params });
|
|
let route;
|
|
if (id !== undefined) {
|
|
route = state.routes.findLast((route) => route.name === action.payload.name && id === getId?.({ params: route.params }));
|
|
}
|
|
else if (action.type === 'NAVIGATE') {
|
|
const currentRoute = state.routes[state.index];
|
|
// If the route matches the current one, then navigate to it
|
|
if (action.payload.name === currentRoute.name && !isPreviewAction(action)) {
|
|
route = currentRoute;
|
|
}
|
|
else if (action.payload.pop) {
|
|
route = state.routes.findLast((route) => route.name === action.payload.name);
|
|
}
|
|
}
|
|
// START FORK
|
|
let isPreloadedRoute = false;
|
|
if (isPreviewAction(action) && !route) {
|
|
route = state.preloadedRoutes.find((route) => route.name === action.payload.name && id === route.key);
|
|
isPreloadedRoute = !!route;
|
|
}
|
|
// END FORK
|
|
if (!route) {
|
|
route = state.preloadedRoutes.find((route) => route.name === action.payload.name && id === getId?.({ params: route.params }));
|
|
// START FORK
|
|
isPreloadedRoute = !!route;
|
|
// END FORK
|
|
}
|
|
let params;
|
|
if (action.type === 'NAVIGATE' && action.payload.merge && route) {
|
|
params =
|
|
action.payload.params !== undefined ||
|
|
routeParamList[action.payload.name] !== undefined
|
|
? {
|
|
...routeParamList[action.payload.name],
|
|
...route.params,
|
|
...action.payload.params,
|
|
}
|
|
: route.params;
|
|
}
|
|
else {
|
|
params =
|
|
routeParamList[action.payload.name] !== undefined
|
|
? {
|
|
...routeParamList[action.payload.name],
|
|
...action.payload.params,
|
|
}
|
|
: action.payload.params;
|
|
}
|
|
let routes;
|
|
if (route) {
|
|
if (action.type === 'NAVIGATE' && action.payload.pop) {
|
|
routes = [];
|
|
// Get all routes until the matching one
|
|
for (const r of state.routes) {
|
|
if (r.key === route.key) {
|
|
routes.push({
|
|
...route,
|
|
path: action.payload.path !== undefined ? action.payload.path : route.path,
|
|
params,
|
|
});
|
|
break;
|
|
}
|
|
routes.push(r);
|
|
}
|
|
}
|
|
else {
|
|
// START FORK
|
|
// If there is an id, then filter out the existing route with the same id.
|
|
// THIS ACTION IS DANGEROUS. This can cause React Native Screens to freeze
|
|
if (id !== undefined) {
|
|
routes = state.routes.filter((r) => r.key !== route.key);
|
|
}
|
|
else if (action.type === 'NAVIGATE' && state.routes.length > 0) {
|
|
// The navigation action should only replace the last route if it has the same name and path params.
|
|
const lastRoute = state.routes[state.routes.length - 1];
|
|
if ((0, useScreens_1.getSingularId)(lastRoute.name, { params: lastRoute.params }) ===
|
|
(0, useScreens_1.getSingularId)(route.name, { params })) {
|
|
routes = state.routes.slice(0, -1);
|
|
}
|
|
else {
|
|
routes = [...state.routes];
|
|
}
|
|
}
|
|
else {
|
|
routes = [...state.routes];
|
|
}
|
|
// If the routes length is the same as the state routes length, then we are navigating to a new route.
|
|
// Otherwise we are replacing an existing route.
|
|
// For preloaded route, we want to use the same key, so that preloaded screen is used.
|
|
const key = routes.length === state.routes.length && !isPreloadedRoute
|
|
? `${action.payload.name}-${(0, non_secure_1.nanoid)()}`
|
|
: route.key;
|
|
routes.push({
|
|
...route,
|
|
key,
|
|
path: action.type === 'NAVIGATE' && action.payload.path !== undefined
|
|
? action.payload.path
|
|
: route.path,
|
|
params,
|
|
});
|
|
// routes = state.routes.filter((r) => r.key !== route.key);
|
|
// routes.push({
|
|
// ...route,
|
|
// path:
|
|
// action.type === 'NAVIGATE' && action.payload.path !== undefined
|
|
// ? action.payload.path
|
|
// : route.path,
|
|
// params,
|
|
// });
|
|
// END FORK
|
|
}
|
|
}
|
|
else {
|
|
routes = [
|
|
...state.routes,
|
|
{
|
|
key: `${action.payload.name}-${(0, non_secure_1.nanoid)()}`,
|
|
name: action.payload.name,
|
|
path: action.type === 'NAVIGATE' ? action.payload.path : undefined,
|
|
params,
|
|
},
|
|
];
|
|
}
|
|
// START FORK
|
|
// return filterSingular(
|
|
const result = {
|
|
...state,
|
|
index: routes.length - 1,
|
|
preloadedRoutes: state.preloadedRoutes.filter((route) => routes[routes.length - 1].key !== route.key),
|
|
routes,
|
|
};
|
|
if (actionSingularOptions) {
|
|
return filterSingular(result, getId);
|
|
}
|
|
const zoomTransitionId = getZoomTransitionIdFromAction(action);
|
|
if (zoomTransitionId) {
|
|
const lastRoute = result.routes[result.routes.length - 1];
|
|
const key = lastRoute.key;
|
|
const modifiedLastRoute = {
|
|
...lastRoute,
|
|
params: {
|
|
...lastRoute.params,
|
|
[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME]: key,
|
|
},
|
|
};
|
|
return {
|
|
...result,
|
|
routes: [...result.routes.slice(0, -1), modifiedLastRoute],
|
|
};
|
|
}
|
|
return result;
|
|
// return {
|
|
// ...state,
|
|
// index: routes.length - 1,
|
|
// preloadedRoutes: state.preloadedRoutes.filter(
|
|
// (route) => routes[routes.length - 1].key !== route.key
|
|
// ),
|
|
// routes,
|
|
// };
|
|
// END FORK
|
|
}
|
|
case 'PRELOAD': {
|
|
// START FORK
|
|
// This will be the case for example for protected route
|
|
if (!state.routeNames.includes(action.payload.name)) {
|
|
return null;
|
|
}
|
|
// END FORK
|
|
const getId = options.routeGetIdList[action.payload.name];
|
|
const id = getId?.({ params: action.payload.params });
|
|
let route;
|
|
if (id !== undefined) {
|
|
route = state.routes.find((route) => route.name === action.payload.name && id === getId?.({ params: route.params }));
|
|
}
|
|
if (route) {
|
|
return {
|
|
...state,
|
|
routes: state.routes.map((r) => {
|
|
if (r.key !== route?.key) {
|
|
return r;
|
|
}
|
|
return {
|
|
...r,
|
|
params: routeParamList[action.payload.name] !== undefined
|
|
? {
|
|
...routeParamList[action.payload.name],
|
|
...action.payload.params,
|
|
}
|
|
: action.payload.params,
|
|
};
|
|
}),
|
|
};
|
|
}
|
|
else {
|
|
// START FORK
|
|
const currentPreloadedRoute = {
|
|
key: `${action.payload.name}-${(0, non_secure_1.nanoid)()}`,
|
|
name: action.payload.name,
|
|
params: routeParamList[action.payload.name] !== undefined
|
|
? {
|
|
...routeParamList[action.payload.name],
|
|
...action.payload.params,
|
|
}
|
|
: action.payload.params,
|
|
};
|
|
// END FORK
|
|
return {
|
|
...state,
|
|
// START FORK
|
|
// Adding the current preloaded route to the beginning of the preloadedRoutes array
|
|
// This ensures that the preloaded route will be the next one after the visible route
|
|
// and when navigation will happen, there will be no reshuffling
|
|
// This is a workaround for the link preview navigation issue, when screen would freeze after navigation from native side
|
|
// and reshuffling from react-navigation
|
|
preloadedRoutes: [currentPreloadedRoute].concat(state.preloadedRoutes.filter((r) => r.name !== action.payload.name || id !== getId?.({ params: r.params }))),
|
|
// preloadedRoutes: state.preloadedRoutes
|
|
// .filter(
|
|
// (r) =>
|
|
// r.name !== action.payload.name ||
|
|
// id !== getId?.({ params: r.params })
|
|
// )
|
|
// .concat({
|
|
// key: `${action.payload.name}-${nanoid()}`,
|
|
// name: action.payload.name,
|
|
// params:
|
|
// routeParamList[action.payload.name] !== undefined
|
|
// ? {
|
|
// ...routeParamList[action.payload.name],
|
|
// ...action.payload.params,
|
|
// }
|
|
// : action.payload.params,
|
|
// }),
|
|
// END FORK
|
|
};
|
|
}
|
|
}
|
|
default: {
|
|
return original.getStateForAction(state, action, options);
|
|
}
|
|
}
|
|
},
|
|
};
|
|
};
|
|
exports.stackRouterOverride = stackRouterOverride;
|
|
function getActionSingularIdFn(actionGetId, name) {
|
|
if (typeof actionGetId === 'function') {
|
|
return (options) => actionGetId(name, options.params ?? {});
|
|
}
|
|
else if (actionGetId === true) {
|
|
return (options) => (0, useScreens_1.getSingularId)(name, options);
|
|
}
|
|
return undefined;
|
|
}
|
|
/**
|
|
* If there is a dynamic singular on an action, then we need to filter the state to only have singular screens.
|
|
* As multiples may have been added before we did the singular navigation.
|
|
*/
|
|
function filterSingular(state, getId) {
|
|
if (!state) {
|
|
return state;
|
|
}
|
|
if (!state.routes) {
|
|
return state;
|
|
}
|
|
const currentIndex = state.index || state.routes.length - 1;
|
|
const current = state.routes[currentIndex];
|
|
const name = current.name;
|
|
const id = getId?.({ params: current.params });
|
|
if (!id) {
|
|
return state;
|
|
}
|
|
// TypeScript needs a type assertion here for the filter to work.
|
|
let routes = state.routes;
|
|
routes = routes.filter((route, index) => {
|
|
// If the route is the current route, keep it.
|
|
if (index === currentIndex) {
|
|
return true;
|
|
}
|
|
// Remove all other routes with the same name and id.
|
|
return name !== route.name || id !== getId?.({ params: route.params });
|
|
});
|
|
return {
|
|
...state,
|
|
index: routes.length - 1,
|
|
routes,
|
|
};
|
|
}
|
|
/**
|
|
* Renders a native stack navigator.
|
|
*
|
|
* @hideType
|
|
*/
|
|
const Stack = Object.assign((props) => {
|
|
const { isStackAnimationDisabled } = (0, LinkPreviewContext_1.useLinkPreviewContext)();
|
|
const screenOptionsWithCompositionAPIOptions = (0, react_1.useMemo)(() => {
|
|
const stackHeader = react_1.Children.toArray(props.children).find((child) => (0, children_1.isChildOfType)(child, stack_utils_1.StackHeader));
|
|
if (stackHeader) {
|
|
const screenStackProps = { children: stackHeader };
|
|
const currentOptions = props.screenOptions;
|
|
if (currentOptions) {
|
|
if (typeof currentOptions === 'function') {
|
|
return (...args) => {
|
|
const options = currentOptions(...args);
|
|
return (0, stack_utils_1.appendScreenStackPropsToOptions)(options, screenStackProps);
|
|
};
|
|
}
|
|
return (0, stack_utils_1.appendScreenStackPropsToOptions)(currentOptions, screenStackProps);
|
|
}
|
|
else {
|
|
return (0, stack_utils_1.appendScreenStackPropsToOptions)({}, screenStackProps);
|
|
}
|
|
}
|
|
else if (props.screenOptions) {
|
|
const screenOptions = props.screenOptions;
|
|
if (typeof screenOptions === 'function') {
|
|
return (0, stack_utils_1.validateStackPresentation)(screenOptions);
|
|
}
|
|
return (0, stack_utils_1.validateStackPresentation)(screenOptions);
|
|
}
|
|
return props.screenOptions;
|
|
}, [props.screenOptions, props.children]);
|
|
const screenOptions = (0, react_1.useMemo)(() => {
|
|
const condition = isStackAnimationDisabled ? () => true : shouldDisableAnimationBasedOnParams;
|
|
return disableAnimationInScreenOptions(screenOptionsWithCompositionAPIOptions, condition);
|
|
}, [screenOptionsWithCompositionAPIOptions, isStackAnimationDisabled]);
|
|
const rnChildren = (0, react_1.useMemo)(() => (0, stack_utils_1.mapProtectedScreen)({ guard: true, children: props.children }).children, [props.children]);
|
|
return (<RNStack {...props} children={rnChildren} screenOptions={screenOptions} UNSTABLE_router={exports.stackRouterOverride}/>);
|
|
}, {
|
|
Screen: stack_utils_1.StackScreen,
|
|
Protected: Protected_1.Protected,
|
|
Header: stack_utils_1.StackHeader,
|
|
SearchBar: stack_utils_1.StackSearchBar,
|
|
Toolbar: stack_utils_1.StackToolbar,
|
|
});
|
|
function disableAnimationInScreenOptions(options, condition) {
|
|
if (options && typeof options === 'function') {
|
|
return (props) => {
|
|
const oldOptions = options(props);
|
|
if (condition(props.route)) {
|
|
return {
|
|
...oldOptions,
|
|
animation: 'none',
|
|
};
|
|
}
|
|
return oldOptions ?? {};
|
|
};
|
|
}
|
|
return (props) => {
|
|
if (condition(props.route)) {
|
|
return {
|
|
...(options ?? {}),
|
|
animation: 'none',
|
|
};
|
|
}
|
|
return options ?? {};
|
|
};
|
|
}
|
|
function shouldDisableAnimationBasedOnParams(route) {
|
|
const expoParams = (0, navigationParams_1.getInternalExpoRouterParams)(route.params);
|
|
return !!expoParams[navigationParams_1.INTERNAL_EXPO_ROUTER_NO_ANIMATION_PARAM_NAME];
|
|
}
|
|
exports.default = Stack;
|
|
const StackRouter = (options) => {
|
|
const router = (0, native_1.StackRouter)(options);
|
|
return {
|
|
...router,
|
|
...(0, exports.stackRouterOverride)(router),
|
|
};
|
|
};
|
|
exports.StackRouter = StackRouter;
|
|
//# sourceMappingURL=StackClient.js.map
|