Skip to content

Instantly share code, notes, and snippets.

@uqmessias
Last active July 23, 2019 18:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uqmessias/22dde7b94a2f64e2f5ff0edcf3bc7b37 to your computer and use it in GitHub Desktop.
Save uqmessias/22dde7b94a2f64e2f5ff0edcf3bc7b37 to your computer and use it in GitHub Desktop.
Function to select the current screen on an react-navigation.navigation.routes
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
node_modules
import {
NavigationInsertRouteNameAtAction,
NavigationInsertRouteNameAtActionPayload,
NavigationRemoveAtRouteNameAction
} from "./types";
const CustomNavigateBackToActionType = "CustomNavigation/INSERT_ROUTE_NAME_AT";
const CustomRemoveAtRouteNameActionType =
"CustomNavigation/REMOVE_AT_ROUTE_NAME";
export const CustomNavigationActionTypes = {
insertRouteNameAtActionType: CustomNavigateBackToActionType,
removeAtRouteNameActionType: CustomRemoveAtRouteNameActionType
};
export const CustomNavigationActions = {
/**
* It creates an action that is handled by the navigator router and navigates
* to a given route name, but instead of being added onto the top of the stack, it's
* inserted into a given index.
*/
insertRouteNameAt: (
options: NavigationInsertRouteNameAtActionPayload
): NavigationInsertRouteNameAtAction => ({
...options,
type: CustomNavigateBackToActionType
}),
/**
* It creates an action that is handled by the navigator router and removes a route
* (without any animation) by a given route name from the current navigation state
* @param routeName Name of the route to be removed
*/
removeAtRouteName: (
routeName: string
): NavigationRemoveAtRouteNameAction => ({
routeName,
type: CustomRemoveAtRouteNameActionType
})
};
import { NavigationRoute } from "react-navigation";
const getCurrentRoute = (route: NavigationRoute): NavigationRoute => {
if (!route.routes || !route.routes.length) {
return route;
}
return getCurrentRoute(route.routes[route.index]);
};
export default getCurrentRoute;
import { NavigationRoute } from "react-navigation";
const getDumpNavigationState = (
{ routeName, routes, index }: NavigationRoute,
isSelected: boolean = true,
tabLevel: string = ""
): string => {
const selectionTag = isSelected ? "✔" : "";
const arrow = isSelected ? "↳" : "x";
const withIndex = index !== undefined ? `[${index}]` : "";
const routeLine = `${tabLevel} ${arrow} ${routeName}${withIndex}${selectionTag}`;
if (!routes) {
return routeLine;
}
const tabWidth = " ";
const routesLines = routes
.map((route, routeIndex) =>
getDumpNavigationState(
route,
isSelected && routeIndex === index,
`${tabLevel}${tabWidth}`
)
)
.join("\n");
return `${routeLine}\n${routesLines}`;
};
export default getDumpNavigationState;
import insertRouteNameAtIndex from "./insertRouteNameAtIndex";
import getCurrentRoute from "./getCurrentRoute";
import getDumpNavigationState from "./getDumpNavigationState";
import removeAtRouteName from "./removeAtRouteName";
export {
insertRouteNameAtIndex,
getCurrentRoute,
getDumpNavigationState,
removeAtRouteName
};
export {
CustomNavigationActionTypes,
CustomNavigationActions
} from "./actions";
import { NavigationRoute, StackActions } from "react-navigation";
import { NavigationInsertRouteNameAtActionPayload } from "./types";
/**
* It gets a state, does a proper navigation to a route name, but instead of being added
* onto the top of the navigation stack, it's inserted into a given index in the first navigation
* level.
* @param state navigation state that will have the new route inserted
* @param action navigation action that will be performed with the index to determine the insertion
* @returns A new state with the new route inserted on the correct index
*/
const insertRouteNameAtIndex = (
state: NavigationRoute,
action: NavigationInsertRouteNameAtActionPayload,
getStateForAction: (action: any, state: NavigationRoute) => NavigationRoute
): NavigationRoute => {
if (!state || !state.routes) {
return state;
}
const newState = getStateForAction(
{
...action,
type: StackActions.PUSH
},
state
);
const lastIndex = newState.routes.length - 1;
const newRoute = newState.routes[lastIndex];
const newIndex = Math.max(0, Math.min(lastIndex, action.index));
newState.routes.splice(lastIndex, 1);
newState.routes.splice(newIndex, 0, newRoute);
return {
...newState,
isTransitioning: false
};
};
export default insertRouteNameAtIndex;
{
"name": "react-navigation-utils",
"version": "0.0.1",
"description": "This is a compilation of some react-navigation utils",
"main": "index.ts",
"dependencies": {
"@types/react-navigation": "^3.0.7"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://gist.github.com/22dde7b94a2f64e2f5ff0edcf3bc7b37.git"
},
"keywords": [
"react-navigation",
"selectCurrentScreen",
"getCurrentRoute"
],
"author": "Uilque Messias",
"license": "MIT",
"bugs": {
"url": "https://gist.github.com/22dde7b94a2f64e2f5ff0edcf3bc7b37"
},
"homepage": "https://gist.github.com/22dde7b94a2f64e2f5ff0edcf3bc7b37"
}
import { NavigationRoute } from "react-navigation";
import getCurrentRoute from "./getCurrentRoute";
/**
* It gets a state and look for a route to remove recursively.
* If the parent state of the removed route has its index set to greater
* than 0 or greater than last index of the parent routes (after the route be removed)
* it should decrement its index. Otherwise it keeps it as it is.
* @param state navigation state that contains the route to be removed
* @param routeName Name of the route to be removed
* @returns A new state without the removed route if it was there before or the same state passed by argument
*/
const removeAtRouteName = (
state: NavigationRoute,
routeName: string
): NavigationRoute => {
if (!routeName || !state.routes || !state.routes.length) {
return state;
}
const newState = { ...state };
let removed = false;
state.routes.forEach((currentRoute, index) => {
if (currentRoute.routeName === routeName) {
const newRoutes = newState.routes.slice();
newRoutes.splice(index, 1);
newState.routes = newRoutes;
const lastIndex = newRoutes.length - 1;
if (newState.index > 0 || newState.index > lastIndex) {
newState.index--;
}
removed = true;
} else if (currentRoute.routes) {
const newRoute = removeAtRouteName(currentRoute, routeName);
if (newRoute !== currentRoute) {
const newRoutes = newState.routes.slice();
newRoutes[index] = newRoute;
newState.routes = newRoutes;
removed = true;
}
}
});
if (removed) {
const newCurrentRoute = getCurrentRoute(newState);
const routeValues = new Set(
!!newCurrentRoute ? [newCurrentRoute.key, newCurrentRoute.routeName] : []
);
const canBeRemoved = !routeValues.has("StackRouterRoot");
if (canBeRemoved) {
return newState;
}
}
return state;
};
export default removeAtRouteName;
const selectCurrentScreen = (routes) => {
if (!routes) {
return null
}
if (Array.isArray(routes)) {
return selectCurrentScreen(routes[routes.length - 1])
}
if (Array.isArray(routes.routes) && typeof routes.index === 'number') {
return selectCurrentScreen(routes.routes[routes.index])
}
return routes.routeName || null
}
const LoginOTPRequest = [{
index: 0,
routes: [{
index: 0,
routes: [{
routeName: 'LoginOTPRequest',
key: 'Init-id-1508188871032-8',
}],
routeName: 'LoginNavigation',
key: 'Init-id-1508188871032-9',
}],
routeName: 'ScreenNavigation',
key: 'Init-id-1508188871032-11',
}]
const LoginOTPCode = [{
index: 0,
routes: [{
index: 1,
routes: [{
routeName: 'LoginOTPRequest',
key: 'Init-id-1508188871032-8',
},
{
routeName: 'LoginOTPCode',
key: 'Init-id-1508188871032-9',
}],
routeName: 'LoginNavigation',
key: 'Init-id-1508188871032-9',
}],
routeName: 'ScreenNavigation',
key: 'Init-id-1508188871032-11',
}]
const Menu = [{
index: 1,
routes: [{
index: 0,
routes: [{
routeName: 'LoginOTPRequest',
key: 'Init-id-1508190306049-8',
}],
routeName: 'LoginNavigation',
key: 'Init-id-1508190306049-9',
},
{
key: 'id-1508190306049-12',
routeName: 'Menu',
}],
routeName: 'ScreenNavigation',
key: 'Init-id-1508190306049-11',
}]
const Embedded_Webview = [{
index: 1,
routes: [{
index: 0,
routes: [{
routeName: 'LoginOTPRequest',
key: 'Init-id-1508188871032-8',
}],
routeName: 'LoginNavigation',
key: 'Init-id-1508188871032-9',
},
{
key: 'id-1508188871032-12',
routeName: 'Menu',
}],
routeName: 'ScreenNavigation',
key: 'Init-id-1508188871032-11',
},
{
key: 'id-1508188871032-14',
routeName: 'Embedded_Webview',
}]
console.log(`LoginOTPRequest -> ${selectCurrentScreen(LoginOTPRequest) === 'LoginOTPRequest'}`)
console.log(`LoginOTPCode -> ${selectCurrentScreen(LoginOTPCode) === 'LoginOTPCode'}`)
console.log(`Menu -> ${selectCurrentScreen(Menu) === 'Menu'}`)
console.log(`Embedded_Webview -> ${selectCurrentScreen(Embedded_Webview) === 'Embedded_Webview'}`)
import {
NavigationTransitionProps as NTP,
NavigationAction,
NavigationPushActionPayload,
NavigationParams,
NavigationState
} from "react-navigation";
type NavigationDispatch = (action: CustomNavigationAction) => boolean;
interface NavigationScreenProp<S, P = NavigationParams> {
dispatch: NavigationDispatch;
}
export type NavigationTransitionProps = NTP & {
navigation: NavigationScreenProp<NavigationState>;
};
export interface NavigationInsertRouteNameAtActionPayload
extends NavigationPushActionPayload {
index: number;
}
export interface NavigationInsertRouteNameAtAction
extends NavigationInsertRouteNameAtActionPayload {
type: "CustomNavigation/INSERT_ROUTE_NAME_AT";
}
interface NavigationRemoveAtRouteNameActionPayload {
routeName: string;
}
export interface NavigationRemoveAtRouteNameAction
extends NavigationRemoveAtRouteNameActionPayload {
type: "CustomNavigation/REMOVE_AT_ROUTE_NAME";
}
export type CustomNavigationAction =
| NavigationAction
| NavigationInsertRouteNameAtAction
| NavigationRemoveAtRouteNameAction;
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/prop-types@*":
version "15.7.1"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6"
integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==
"@types/react-native@*":
version "0.60.2"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.2.tgz#2dca78481a904419c2a5907288dd97d1090c6e3c"
integrity sha512-K4+/etirpv52xu24xAc++OdhbD0VQEt0Kq0h6dZrLU82OlA+I7BhpKF3JBvx9tbmrFaZDxhHp8N4TEvRYS4fdQ==
dependencies:
"@types/prop-types" "*"
"@types/react" "*"
"@types/react-navigation@^3.0.7":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@types/react-navigation/-/react-navigation-3.0.7.tgz#5d327b00de2bb58203977002c3ea21302da72034"
integrity sha512-JFsNeCAQEQGTpObiD1QczDyCyQkFBaQA/F85Fo2W8QML1b6UNYHlUDtEYJA6jcfXqPoL15dVPBVj9NBJlHToqA==
dependencies:
"@types/react" "*"
"@types/react-native" "*"
"@types/react@*":
version "16.8.23"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.23.tgz#ec6be3ceed6353a20948169b6cb4c97b65b97ad2"
integrity sha512-abkEOIeljniUN9qB5onp++g0EY38h7atnDHxwKUFz1r3VH1+yG1OKi2sNPTyObL40goBmfKFpdii2lEzwLX1cA==
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
csstype@^2.2.0:
version "2.6.6"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41"
integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment