about summary refs log tree commit diff
path: root/src/view/shell
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/shell')
-rw-r--r--src/view/shell/createNativeStackNavigatorWithAuth.tsx150
-rw-r--r--src/view/shell/index.web.tsx29
2 files changed, 152 insertions, 27 deletions
diff --git a/src/view/shell/createNativeStackNavigatorWithAuth.tsx b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
new file mode 100644
index 000000000..c7b5d1d2e
--- /dev/null
+++ b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
@@ -0,0 +1,150 @@
+import * as React from 'react'
+import {View} from 'react-native'
+
+// Based on @react-navigation/native-stack/src/createNativeStackNavigator.ts
+// MIT License
+// Copyright (c) 2017 React Navigation Contributors
+
+import {
+  createNavigatorFactory,
+  EventArg,
+  ParamListBase,
+  StackActionHelpers,
+  StackActions,
+  StackNavigationState,
+  StackRouter,
+  StackRouterOptions,
+  useNavigationBuilder,
+} from '@react-navigation/native'
+import type {
+  NativeStackNavigationEventMap,
+  NativeStackNavigationOptions,
+} from '@react-navigation/native-stack'
+import type {NativeStackNavigatorProps} from '@react-navigation/native-stack/src/types'
+import {NativeStackView} from '@react-navigation/native-stack'
+
+import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
+import {DesktopLeftNav} from './desktop/LeftNav'
+import {DesktopRightNav} from './desktop/RightNav'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {useOnboardingState} from '#/state/shell'
+import {
+  useLoggedOutView,
+  useLoggedOutViewControls,
+} from '#/state/shell/logged-out'
+import {useSession} from '#/state/session'
+import {isWeb} from 'platform/detection'
+import {LoggedOut} from '../com/auth/LoggedOut'
+import {Onboarding} from '../com/auth/Onboarding'
+
+type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & {
+  requireAuth?: boolean
+}
+
+function NativeStackNavigator({
+  id,
+  initialRouteName,
+  children,
+  screenListeners,
+  screenOptions,
+  ...rest
+}: NativeStackNavigatorProps) {
+  // --- this is copy and pasted from the original native stack navigator ---
+  const {state, descriptors, navigation, NavigationContent} =
+    useNavigationBuilder<
+      StackNavigationState<ParamListBase>,
+      StackRouterOptions,
+      StackActionHelpers<ParamListBase>,
+      NativeStackNavigationOptionsWithAuth,
+      NativeStackNavigationEventMap
+    >(StackRouter, {
+      id,
+      initialRouteName,
+      children,
+      screenListeners,
+      screenOptions,
+    })
+  React.useEffect(
+    () =>
+      // @ts-expect-error: there may not be a tab navigator in parent
+      navigation?.addListener?.('tabPress', (e: any) => {
+        const isFocused = navigation.isFocused()
+
+        // Run the operation in the next frame so we're sure all listeners have been run
+        // This is necessary to know if preventDefault() has been called
+        requestAnimationFrame(() => {
+          if (
+            state.index > 0 &&
+            isFocused &&
+            !(e as EventArg<'tabPress', true>).defaultPrevented
+          ) {
+            // When user taps on already focused tab and we're inside the tab,
+            // reset the stack to replicate native behaviour
+            navigation.dispatch({
+              ...StackActions.popToTop(),
+              target: state.key,
+            })
+          }
+        })
+      }),
+    [navigation, state.index, state.key],
+  )
+
+  // --- our custom logic starts here ---
+  const {hasSession} = useSession()
+  const activeRoute = state.routes[state.index]
+  const activeDescriptor = descriptors[activeRoute.key]
+  const activeRouteRequiresAuth = activeDescriptor.options.requireAuth ?? false
+  const onboardingState = useOnboardingState()
+  const {showLoggedOut} = useLoggedOutView()
+  const {setShowLoggedOut} = useLoggedOutViewControls()
+  const {isMobile} = useWebMediaQueries()
+  if (activeRouteRequiresAuth && !hasSession) {
+    return <LoggedOut />
+  }
+  if (showLoggedOut) {
+    return <LoggedOut onDismiss={() => setShowLoggedOut(false)} />
+  }
+  if (onboardingState.isActive) {
+    return <Onboarding />
+  }
+  const newDescriptors: typeof descriptors = {}
+  for (let key in descriptors) {
+    const descriptor = descriptors[key]
+    const requireAuth = descriptor.options.requireAuth ?? false
+    newDescriptors[key] = {
+      ...descriptor,
+      render() {
+        if (requireAuth && !hasSession) {
+          return <View />
+        } else {
+          return descriptor.render()
+        }
+      },
+    }
+  }
+  return (
+    <NavigationContent>
+      <NativeStackView
+        {...rest}
+        state={state}
+        navigation={navigation}
+        descriptors={newDescriptors}
+      />
+      {isWeb && isMobile && <BottomBarWeb />}
+      {isWeb && !isMobile && (
+        <>
+          <DesktopLeftNav />
+          <DesktopRightNav />
+        </>
+      )}
+    </NavigationContent>
+  )
+}
+
+export const createNativeStackNavigatorWithAuth = createNavigatorFactory<
+  StackNavigationState<ParamListBase>,
+  NativeStackNavigationOptionsWithAuth,
+  NativeStackNavigationEventMap,
+  typeof NativeStackNavigator
+>(NativeStackNavigator)
diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx
index 74477243d..38da860bd 100644
--- a/src/view/shell/index.web.tsx
+++ b/src/view/shell/index.web.tsx
@@ -1,7 +1,5 @@
 import React, {useEffect} from 'react'
 import {View, StyleSheet, TouchableOpacity} from 'react-native'
-import {DesktopLeftNav} from './desktop/LeftNav'
-import {DesktopRightNav} from './desktop/RightNav'
 import {ErrorBoundary} from '../com/util/ErrorBoundary'
 import {Lightbox} from '../com/lightbox/Lightbox'
 import {ModalsContainer} from '../com/modals/Modal'
@@ -11,27 +9,19 @@ import {s, colors} from 'lib/styles'
 import {RoutesContainer, FlatNavigator} from '../../Navigation'
 import {DrawerContent} from './Drawer'
 import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
-import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
 import {useNavigation} from '@react-navigation/native'
 import {NavigationProp} from 'lib/routes/types'
 import {useAuxClick} from 'lib/hooks/useAuxClick'
 import {t} from '@lingui/macro'
-import {
-  useIsDrawerOpen,
-  useSetDrawerOpen,
-  useOnboardingState,
-} from '#/state/shell'
+import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
 import {useCloseAllActiveElements} from '#/state/util'
-import {useLoggedOutView} from '#/state/shell/logged-out'
 
 function ShellInner() {
   const isDrawerOpen = useIsDrawerOpen()
   const setDrawerOpen = useSetDrawerOpen()
-  const onboardingState = useOnboardingState()
-  const {isDesktop, isMobile} = useWebMediaQueries()
+  const {isDesktop} = useWebMediaQueries()
   const navigator = useNavigation<NavigationProp>()
   const closeAllActiveElements = useCloseAllActiveElements()
-  const {showLoggedOut} = useLoggedOutView()
 
   useAuxClick()
 
@@ -42,8 +32,6 @@ function ShellInner() {
     return unsubscribe
   }, [navigator, closeAllActiveElements])
 
-  const showBottomBar = isMobile && !onboardingState.isActive
-  const showSideNavs = !isMobile && !onboardingState.isActive && !showLoggedOut
   return (
     <View style={[s.hContentRegion, {overflow: 'hidden'}]}>
       <View style={s.hContentRegion}>
@@ -51,22 +39,9 @@ function ShellInner() {
           <FlatNavigator />
         </ErrorBoundary>
       </View>
-
-      {showSideNavs && (
-        <>
-          <DesktopLeftNav />
-          <DesktopRightNav />
-        </>
-      )}
-
       <Composer winHeight={0} />
-
-      {showBottomBar && <BottomBarWeb />}
-
       <ModalsContainer />
-
       <Lightbox />
-
       {!isDesktop && isDrawerOpen && (
         <TouchableOpacity
           onPress={() => setDrawerOpen(false)}