import * as React from 'react' ; import { camelCase } from 'lodash' ; import { StackActions , NavigationContainerRef } from '@react-navigation/native' ; import { getTabBarPages , setTabInitRoute , handleUrl } from './utils/index' ; import { CallbackResult , BaseOption } from './utils/types' ; type NavigateMethod = 'navigateTo' | 'redirectTo' | 'navigateBack' | 'switchTab' | 'reLaunch' ; interface NavigateOption extends BaseOption { url : string ; events ? : Record < string , any > ; } interface NavigateBackOption extends BaseOption { delta ? : number ; } interface NavigateRef extends NavigationContainerRef { setOptions : ( obj : any ) => void ; } let routeEvtChannel ; export const isReadyRef = React . createRef ( ) ; export const navigationRef = React . createRef < NavigateRef > ( ) ; export function navigateTo ( option ) { return navigate ( option , 'navigateTo' ) ; } export function redirectTo ( option ) { return navigate ( option , 'redirectTo' ) ; } export function navigateBack ( option = { } ) { return navigate ( option , 'navigateBack' ) ; } export function switchTab ( option ) { return navigate ( option , 'switchTab' ) ; } export function reLaunch ( option ) { return navigate ( option , 'reLaunch' ) ; } export function navigate ( option , method ) { const { success , complete , fail } = option ; let errMsg ; let routeParam ; const path = ( option as NavigateOption ) . url ; if ( path ) { routeParam = handleUrl ( path ) ; } try { if ( method === 'navigateTo' ) { navigationRef . current ?. dispatch ( StackActions . push ( routeParam . pageName , routeParam . params ) ) ; } else if ( method === 'redirectTo' ) { navigationRef . current ?. dispatch ( StackActions . replace ( routeParam . pageName , routeParam . params ) ) ; } else if ( method === 'switchTab' || ( method === 'reLaunch' && isTabPage ( path ) ) ) { const states = navigationRef . current ?. getRootState ( ) ; if ( states ?. routes [ 0 ] . name !== 'tabNav' ) { states && states ?. routes . length > 1 && navigationRef . current ?. dispatch ( StackActions . popToTop ( ) ) ; navigationRef . current ?. dispatch ( StackActions . replace ( 'tabNav' ) ) ; setTabInitRoute ( routeParam . pageName ) ; } else { navigationRef . current ?. navigate ( routeParam . pageName , routeParam . params ) ; } } else if ( method === 'navigateBack' ) { const number = ( option as NavigateBackOption ) . delta ? ( option as NavigateBackOption ) . delta : 1 ; const states = navigationRef . current ?. getRootState ( ) ; if ( states ?. index === 0 ) { errMsg = 'navigateBack:fail cannot navigate back at first page.' ; } else { const index = number && ( ( states && states . index < number ) ? states ?. index : number ) ; navigationRef . current ?. dispatch ( StackActions . pop ( index ) ) ; } } else if ( method === 'reLaunch' ) { if ( isTabPage ( ) ) { navigationRef . current ?. dispatch ( StackActions . replace ( routeParam . pageName , routeParam . params ) ) ; } else { const states = navigationRef . current ?. getRootState ( ) ; if ( states ?. index !== 0 ) { navigationRef . current ?. dispatch ( StackActions . popToTop ( ) ) ; } navigationRef . current ?. dispatch ( StackActions . replace ( routeParam . pageName , routeParam . params ) ) ; } } } catch ( error ) { errMsg = error ; } return new Promise ( ( resolve , reject ) => { if ( errMsg ) { fail && fail ( { errMsg } ) ; complete && complete ( { errMsg } ) ; reject ( new Error ( errMsg ) ) ; } else { const msg = { errMsg : `${ method }:ok` } ; if ( method === 'navigateTo' ) { routeEvtChannel . addEvents ( ( option as NavigateOption ) . events ) ; msg . eventChannel = routeEvtChannel ; } success && success ( msg ) ; complete && complete ( msg ) ; resolve ( msg ) ; } } ) ; } export function isTabPage ( path = '' ) { const tabPages = getTabBarPages ( ) ; let pageName = '' ; if ( path ) { pageName = camelCase ( ( path . startsWith ( '/' ) ? path : `/${ path }` ) . split ( '?' ) [ 0 ] ) ; } else { const route = navigationRef . current ?. getCurrentRoute ( ) || { } ; pageName = route ?. name || '' ; } return tabPages . indexOf ( pageName ) !== - 1 ; } export function getCurrentRoute ( ) { const routeState = navigationRef . current ?. getRootState ( ) ; const routes = routeState ?. routes ; const routeKeys = [ ] ; if ( routes ) { routes . forEach ( item => { if ( item . name === 'tabNav' ) { const index = item . state ?. index ?? 0 ; const tabRoutes = item . state ?. routes ?? [ ] ; tabRoutes ?. [ index ] && routeKeys . push ( tabRoutes [ index ] . key ) ; } else { routeKeys . push ( item . key ) ; } } ) ; } return routeKeys ; } export const getRouteEventChannel = ( routeChannel ) => { routeEvtChannel = routeChannel ; } ;