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 {useSession} from '#/state/session'
27import {useOnboardingState} from '#/state/shell'
28import {
29 useLoggedOutView,
30 useLoggedOutViewControls,
31} from '#/state/shell/logged-out'
32import {useGate} from 'lib/statsig/statsig'
33import {isNative, isWeb} from 'platform/detection'
34import {Deactivated} from '#/screens/Deactivated'
35import {Onboarding} from '#/screens/Onboarding'
36import {SignupQueued} from '#/screens/SignupQueued'
37import {LoggedOut} from '../com/auth/LoggedOut'
38import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
39import {DesktopLeftNav} from './desktop/LeftNav'
40import {DesktopRightNav} from './desktop/RightNav'
41
42type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & {
43 requireAuth?: boolean
44}
45
46function NativeStackNavigator({
47 id,
48 initialRouteName,
49 children,
50 screenListeners,
51 screenOptions,
52 ...rest
53}: NativeStackNavigatorProps) {
54 const gate = useGate()
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 (
106 !hasSession &&
107 (!PWI_ENABLED ||
108 activeRouteRequiresAuth ||
109 (isNative && gate('native_pwi_disabled')))
110 ) {
111 return <LoggedOut />
112 }
113 if (hasSession && currentAccount?.signupQueued) {
114 return <SignupQueued />
115 }
116 if (showLoggedOut) {
117 return <LoggedOut onDismiss={() => setShowLoggedOut(false)} />
118 }
119 if (currentAccount?.status === 'deactivated') {
120 return <Deactivated />
121 }
122 if (onboardingState.isActive) {
123 return <Onboarding />
124 }
125 const newDescriptors: typeof descriptors = {}
126 for (let key in descriptors) {
127 const descriptor = descriptors[key]
128 const requireAuth = descriptor.options.requireAuth ?? false
129 newDescriptors[key] = {
130 ...descriptor,
131 render() {
132 if (requireAuth && !hasSession) {
133 return <View />
134 } else {
135 return descriptor.render()
136 }
137 },
138 }
139 }
140
141 // 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
142 // on both tablet and mobile web so that we see the sign up CTA.
143 const showBottomBar = hasSession ? isMobile : isTabletOrMobile
144
145 return (
146 <NavigationContent>
147 <NativeStackView
148 {...rest}
149 state={state}
150 navigation={navigation}
151 descriptors={newDescriptors}
152 />
153 {isWeb && showBottomBar && <BottomBarWeb />}
154 {isWeb && !showBottomBar && (
155 <>
156 <DesktopLeftNav />
157 <DesktopRightNav routeName={activeRoute.name} />
158 </>
159 )}
160 </NavigationContent>
161 )
162}
163
164export const createNativeStackNavigatorWithAuth = createNavigatorFactory<
165 StackNavigationState<ParamListBase>,
166 NativeStackNavigationOptionsWithAuth,
167 NativeStackNavigationEventMap,
168 typeof NativeStackNavigator
169>(NativeStackNavigator)