mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import * as React from 'react'
2import {View} from 'react-native'
3import {PWI_ENABLED} from '#/lib/build-flags'
4
5// Based on @react-navigation/native-stack/src/createNativeStackNavigator.ts
6// MIT License
7// Copyright (c) 2017 React Navigation Contributors
8
9import {
10 createNavigatorFactory,
11 EventArg,
12 ParamListBase,
13 StackActionHelpers,
14 StackActions,
15 StackNavigationState,
16 StackRouter,
17 StackRouterOptions,
18 useNavigationBuilder,
19} from '@react-navigation/native'
20import type {
21 NativeStackNavigationEventMap,
22 NativeStackNavigationOptions,
23} from '@react-navigation/native-stack'
24import type {NativeStackNavigatorProps} from '@react-navigation/native-stack/src/types'
25import {NativeStackView} from '@react-navigation/native-stack'
26
27import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
28import {DesktopLeftNav} from './desktop/LeftNav'
29import {DesktopRightNav} from './desktop/RightNav'
30import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
31import {useOnboardingState} from '#/state/shell'
32import {
33 useLoggedOutView,
34 useLoggedOutViewControls,
35} from '#/state/shell/logged-out'
36import {useSession} from '#/state/session'
37import {isWeb} from 'platform/detection'
38import {LoggedOut} from '../com/auth/LoggedOut'
39import {Onboarding} from '../com/auth/Onboarding'
40
41type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & {
42 requireAuth?: boolean
43}
44
45function NativeStackNavigator({
46 id,
47 initialRouteName,
48 children,
49 screenListeners,
50 screenOptions,
51 ...rest
52}: NativeStackNavigatorProps) {
53 // --- this is copy and pasted from the original native stack navigator ---
54 const {state, descriptors, navigation, NavigationContent} =
55 useNavigationBuilder<
56 StackNavigationState<ParamListBase>,
57 StackRouterOptions,
58 StackActionHelpers<ParamListBase>,
59 NativeStackNavigationOptionsWithAuth,
60 NativeStackNavigationEventMap
61 >(StackRouter, {
62 id,
63 initialRouteName,
64 children,
65 screenListeners,
66 screenOptions,
67 })
68 React.useEffect(
69 () =>
70 // @ts-expect-error: there may not be a tab navigator in parent
71 navigation?.addListener?.('tabPress', (e: any) => {
72 const isFocused = navigation.isFocused()
73
74 // Run the operation in the next frame so we're sure all listeners have been run
75 // This is necessary to know if preventDefault() has been called
76 requestAnimationFrame(() => {
77 if (
78 state.index > 0 &&
79 isFocused &&
80 !(e as EventArg<'tabPress', true>).defaultPrevented
81 ) {
82 // When user taps on already focused tab and we're inside the tab,
83 // reset the stack to replicate native behaviour
84 navigation.dispatch({
85 ...StackActions.popToTop(),
86 target: state.key,
87 })
88 }
89 })
90 }),
91 [navigation, state.index, state.key],
92 )
93
94 // --- our custom logic starts here ---
95 const {hasSession} = useSession()
96 const activeRoute = state.routes[state.index]
97 const activeDescriptor = descriptors[activeRoute.key]
98 const activeRouteRequiresAuth = activeDescriptor.options.requireAuth ?? false
99 const onboardingState = useOnboardingState()
100 const {showLoggedOut} = useLoggedOutView()
101 const {setShowLoggedOut} = useLoggedOutViewControls()
102 const {isMobile} = useWebMediaQueries()
103 if ((!PWI_ENABLED || activeRouteRequiresAuth) && !hasSession) {
104 return <LoggedOut />
105 }
106 if (showLoggedOut) {
107 return <LoggedOut onDismiss={() => setShowLoggedOut(false)} />
108 }
109 if (onboardingState.isActive) {
110 return <Onboarding />
111 }
112 const newDescriptors: typeof descriptors = {}
113 for (let key in descriptors) {
114 const descriptor = descriptors[key]
115 const requireAuth = descriptor.options.requireAuth ?? false
116 newDescriptors[key] = {
117 ...descriptor,
118 render() {
119 if (requireAuth && !hasSession) {
120 return <View />
121 } else {
122 return descriptor.render()
123 }
124 },
125 }
126 }
127 return (
128 <NavigationContent>
129 <NativeStackView
130 {...rest}
131 state={state}
132 navigation={navigation}
133 descriptors={newDescriptors}
134 />
135 {isWeb && isMobile && <BottomBarWeb />}
136 {isWeb && !isMobile && (
137 <>
138 <DesktopLeftNav />
139 <DesktopRightNav />
140 </>
141 )}
142 </NavigationContent>
143 )
144}
145
146export const createNativeStackNavigatorWithAuth = createNavigatorFactory<
147 StackNavigationState<ParamListBase>,
148 NativeStackNavigationOptionsWithAuth,
149 NativeStackNavigationEventMap,
150 typeof NativeStackNavigator
151>(NativeStackNavigator)