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