/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule NavigationStateUtils * @flow */ 'use strict'; const invariant = require('fbjs/lib/invariant'); import type { NavigationRoute, NavigationState, } from 'NavigationTypeDefinition'; /** * Utilities to perform atomic operation with navigate state and routes. */ /** * Gets a route by key */ function get(state: NavigationState, key: string): ?NavigationRoute { return state.routes.find(route => route.key === key) || null; } /** * Returns the first index at which a given route's key can be found in the * routes of the navigation state, or -1 if it is not present. */ function indexOf(state: NavigationState, key: string): number { return state.routes.map(route => route.key).indexOf(key); } /** * Returns `true` at which a given route's key can be found in the * routes of the navigation state. */ function has(state: NavigationState, key: string): boolean { return !!state.routes.some(route => route.key === key); } /** * Pushes a new route into the navigation state. * Note that this moves the index to the positon to where the last route in the * stack is at. */ function push(state: NavigationState, route: NavigationRoute): NavigationState { invariant( indexOf(state, route.key) === -1, 'should not push route with duplicated key %s', route.key, ); const routes = [ ...state.routes, route, ]; return { ...state, index: routes.length - 1, routes, }; } /** * Pops out a route from the navigation state. * Note that this moves the index to the positon to where the last route in the * stack is at. */ function pop(state: NavigationState): NavigationState { if (state.index <= 0) { // [Note]: Over-popping does not throw error. Instead, it will be no-op. return state; } const routes = state.routes.slice(0, -1); return { ...state, index: routes.length - 1, routes, }; } /** * Sets the focused route of the navigation state by index. */ function jumpToIndex(state: NavigationState, index: number): NavigationState { if (index === state.index) { return state; } invariant(!!state.routes[index], 'invalid index %s to jump to', index); return { ...state, index, }; } /** * Sets the focused route of the navigation state by key. */ function jumpTo(state: NavigationState, key: string): NavigationState { const index = indexOf(state, key); return jumpToIndex(state, index); } /** * Sets the focused route to the previous route. */ function back(state: NavigationState): NavigationState { const index = state.index - 1; const route = state.routes[index]; return route ? jumpToIndex(state, index) : state; } /** * Sets the focused route to the next route. */ function forward(state: NavigationState): NavigationState { const index = state.index + 1; const route = state.routes[index]; return route ? jumpToIndex(state, index) : state; } /** * Replace a route by a key. * Note that this moves the index to the positon to where the new route in the * stack is at. */ function replaceAt( state: NavigationState, key: string, route: NavigationRoute, ): NavigationState { const index = indexOf(state, key); return replaceAtIndex(state, index, route); } /** * Replace a route by a index. * Note that this moves the index to the positon to where the new route in the * stack is at. */ function replaceAtIndex( state: NavigationState, index: number, route: NavigationRoute, ): NavigationState { invariant( !!state.routes[index], 'invalid index %s for replacing route %s', index, route.key, ); if (state.routes[index] === route) { return state; } const routes = state.routes.slice(); routes[index] = route; return { ...state, index, routes, }; } /** * Resets all routes. * Note that this moves the index to the positon to where the last route in the * stack is at if the param `index` isn't provided. */ function reset( state: NavigationState, routes: Array, index?: number, ): NavigationState { invariant( routes.length && Array.isArray(routes), 'invalid routes to replace', ); const nextIndex: number = index === undefined ? routes.length - 1 : index; if (state.routes.length === routes.length && state.index === nextIndex) { const compare = (route, ii) => routes[ii] === route; if (state.routes.every(compare)) { return state; } } invariant(!!routes[nextIndex], 'invalid index %s to reset', nextIndex); return { ...state, index: nextIndex, routes, }; } const NavigationStateUtils = { back, forward, get: get, has, indexOf, jumpTo, jumpToIndex, pop, push, replaceAt, replaceAtIndex, reset, }; module.exports = NavigationStateUtils;