import getChildEventSubscriber from './getChildEventSubscriber'; import getChildRouter from './getChildRouter'; import getNavigationActionCreators from './routers/getNavigationActionCreators'; import getChildrenNavigationCache from './getChildrenNavigationCache'; const createParamGetter = route => (paramName, defaultValue) => { const params = route.params; if (params && paramName in params) { return params[paramName]; } return defaultValue; }; function getChildNavigation(navigation, childKey, getCurrentParentNavigation) { const children = getChildrenNavigationCache(navigation); const childRoute = navigation.state.routes.find(r => r.key === childKey); if (!childRoute) { return null; } if (children[childKey] && children[childKey].state === childRoute) { return children[childKey]; } const childRouter = getChildRouter(navigation.router, childRoute.routeName); // If the route has children, we'll use this to pass in to the action creators // for the childRouter so that any action that depends on the active route will // behave as expected. We don't explicitly require that routers implement routes // and index properties, but if we did then we would put an invariant here to // ensure that a focusedGrandChildRoute exists if childRouter is defined. const focusedGrandChildRoute = childRoute.routes && typeof childRoute.index === 'number' ? childRoute.routes[childRoute.index] : null; const actionCreators = { ...navigation.actions, ...navigation.router.getActionCreators(childRoute, navigation.state.key), ...(childRouter ? childRouter.getActionCreators(focusedGrandChildRoute, childRoute.key) : {}), ...getNavigationActionCreators(childRoute), }; const actionHelpers = {}; Object.keys(actionCreators).forEach(actionName => { actionHelpers[actionName] = (...args) => { const actionCreator = actionCreators[actionName]; const action = actionCreator(...args); return navigation.dispatch(action); }; }); let isFirstRouteInParent = true; const parentNavigation = getCurrentParentNavigation(); if (parentNavigation) { isFirstRouteInParent = parentNavigation.state.routes.indexOf(childRoute) === 0; } if ( children[childKey] && children[childKey].isFirstRouteInParent() === isFirstRouteInParent ) { children[childKey] = { ...children[childKey], ...actionHelpers, state: childRoute, router: childRouter, actions: actionCreators, getParam: createParamGetter(childRoute), }; return children[childKey]; } else { const childSubscriber = getChildEventSubscriber( navigation.addListener, childKey ); children[childKey] = { ...actionHelpers, state: childRoute, router: childRouter, actions: actionCreators, getParam: createParamGetter(childRoute), getChildNavigation: grandChildKey => getChildNavigation(children[childKey], grandChildKey, () => { const nav = getCurrentParentNavigation(); return nav && nav.getChildNavigation(childKey); }), isFocused: () => { const currentNavigation = getCurrentParentNavigation(); if (!currentNavigation) { return false; } const { routes, index } = currentNavigation.state; if (!currentNavigation.isFocused()) { return false; } if (routes[index].key === childKey) { return true; } return false; }, isFirstRouteInParent: () => isFirstRouteInParent, dispatch: navigation.dispatch, getScreenProps: navigation.getScreenProps, dangerouslyGetParent: getCurrentParentNavigation, addListener: childSubscriber.addListener, emit: childSubscriber.emit, }; return children[childKey]; } } export default getChildNavigation;