diff options
author | Thomas F. K. Jorna <[email protected]> | 2021-07-14 15:10:31 +0200 |
---|---|---|
committer | Thomas F. K. Jorna <[email protected]> | 2021-07-14 15:10:31 +0200 |
commit | e5021187e96b78b53203bd95d08d6818aea47d17 (patch) | |
tree | 37ec45d00eb963db53cd4bb4f04a770414b351cc /app/navigators/navigation-utilities.tsx |
New Ignite 7.0.6 app
Diffstat (limited to 'app/navigators/navigation-utilities.tsx')
-rw-r--r-- | app/navigators/navigation-utilities.tsx | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/app/navigators/navigation-utilities.tsx b/app/navigators/navigation-utilities.tsx new file mode 100644 index 0000000..de1ea05 --- /dev/null +++ b/app/navigators/navigation-utilities.tsx @@ -0,0 +1,127 @@ +import React, { useState, useEffect, useRef } from "react" +import { BackHandler } from "react-native" +import { PartialState, NavigationState, NavigationContainerRef } from "@react-navigation/native" + +export const RootNavigation = { + navigate(name: string) { + name // eslint-disable-line no-unused-expressions + }, + goBack() {}, // eslint-disable-line @typescript-eslint/no-empty-function + resetRoot(state?: PartialState<NavigationState> | NavigationState) {}, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function + getRootState(): NavigationState { + return {} as any + }, +} + +export const setRootNavigation = (ref: React.RefObject<NavigationContainerRef>) => { + for (const method in RootNavigation) { + RootNavigation[method] = (...args: any) => { + if (ref.current) { + return ref.current[method](...args) + } + } + } +} + +/** + * Gets the current screen from any navigation state. + */ +export function getActiveRouteName(state: NavigationState | PartialState<NavigationState>) { + const route = state.routes[state.index] + + // Found the active route -- return the name + if (!route.state) return route.name + + // Recursive call to deal with nested routers + return getActiveRouteName(route.state) +} + +/** + * Hook that handles Android back button presses and forwards those on to + * the navigation or allows exiting the app. + */ +export function useBackButtonHandler( + ref: React.RefObject<NavigationContainerRef>, + canExit: (routeName: string) => boolean, +) { + const canExitRef = useRef(canExit) + + useEffect(() => { + canExitRef.current = canExit + }, [canExit]) + + useEffect(() => { + // We'll fire this when the back button is pressed on Android. + const onBackPress = () => { + const navigation = ref.current + + if (navigation == null) { + return false + } + + // grab the current route + const routeName = getActiveRouteName(navigation.getRootState()) + + // are we allowed to exit? + if (canExitRef.current(routeName)) { + // let the system know we've not handled this event + return false + } + + // we can't exit, so let's turn this into a back action + if (navigation.canGoBack()) { + navigation.goBack() + + return true + } + + return false + } + + // Subscribe when we come to life + BackHandler.addEventListener("hardwareBackPress", onBackPress) + + // Unsubscribe when we're done + return () => BackHandler.removeEventListener("hardwareBackPress", onBackPress) + }, [ref]) +} + +/** + * Custom hook for persisting navigation state. + */ +export function useNavigationPersistence(storage: any, persistenceKey: string) { + const [initialNavigationState, setInitialNavigationState] = useState() + const [isRestoringNavigationState, setIsRestoringNavigationState] = useState(true) + + const routeNameRef = useRef() + const onNavigationStateChange = (state) => { + const previousRouteName = routeNameRef.current + const currentRouteName = getActiveRouteName(state) + + if (previousRouteName !== currentRouteName) { + // track screens. + __DEV__ && console.tron.log(currentRouteName) + } + + // Save the current route name for later comparision + routeNameRef.current = currentRouteName + + // Persist state to storage + storage.save(persistenceKey, state) + } + + const restoreState = async () => { + try { + const state = await storage.load(persistenceKey) + if (state) setInitialNavigationState(state) + } finally { + setIsRestoringNavigationState(false) + } + } + + useEffect(() => { + if (isRestoringNavigationState) restoreState() + }, [isRestoringNavigationState]) + + return { onNavigationStateChange, restoreState, initialNavigationState } +} |