summaryrefslogtreecommitdiff
path: root/app_expo/navigators/navigation-utilities.tsx
diff options
context:
space:
mode:
authorKirill Rogovoy <[email protected]>2021-07-20 21:24:52 +0300
committerKirill Rogovoy <[email protected]>2021-07-20 21:24:52 +0300
commit5f4611d65e40eae3ca6191a15f68d69ea5a1c4cb (patch)
tree273dfc086444533d86d580961c92ba8d14781a67 /app_expo/navigators/navigation-utilities.tsx
parentf0bf4e7afdcd8b02a62be45ab3e7d047ed865a79 (diff)
WIP
Diffstat (limited to 'app_expo/navigators/navigation-utilities.tsx')
-rw-r--r--app_expo/navigators/navigation-utilities.tsx127
1 files changed, 127 insertions, 0 deletions
diff --git a/app_expo/navigators/navigation-utilities.tsx b/app_expo/navigators/navigation-utilities.tsx
new file mode 100644
index 0000000..95137a4
--- /dev/null
+++ b/app_expo/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 }
+}