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 {isWeb} from 'platform/detection'
33import {Deactivated} from '#/screens/Deactivated'
34import {Onboarding} from '#/screens/Onboarding'
35import {SignupQueued} from '#/screens/SignupQueued'
36import {LoggedOut} from '../com/auth/LoggedOut'
37import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
38import {DesktopLeftNav} from './desktop/LeftNav'
39import {DesktopRightNav} from './desktop/RightNav'
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, currentAccount} = 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, isTabletOrMobile} = useWebMediaQueries()
103 if ((!PWI_ENABLED || activeRouteRequiresAuth) && !hasSession) {
104 return <LoggedOut />
105 }
106 if (hasSession && currentAccount?.signupQueued) {
107 return <SignupQueued />
108 }
109 if (showLoggedOut) {
110 return <LoggedOut onDismiss={() => setShowLoggedOut(false)} />
111 }
112 if (currentAccount?.status === 'deactivated') {
113 return <Deactivated />
114 }
115 if (onboardingState.isActive) {
116 return <Onboarding />
117 }
118 const newDescriptors: typeof descriptors = {}
119 for (let key in descriptors) {
120 const descriptor = descriptors[key]
121 const requireAuth = descriptor.options.requireAuth ?? false
122 newDescriptors[key] = {
123 ...descriptor,
124 render() {
125 if (requireAuth && !hasSession) {
126 return <View />
127 } else {
128 return descriptor.render()
129 }
130 },
131 }
132 }
133
134 // 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
135 // on both tablet and mobile web so that we see the sign up CTA.
136 const showBottomBar = hasSession ? isMobile : isTabletOrMobile
137
138 return (
139 <NavigationContent>
140 <NativeStackView
141 {...rest}
142 state={state}
143 navigation={navigation}
144 descriptors={newDescriptors}
145 />
146 {isWeb && showBottomBar && <BottomBarWeb />}
147 {isWeb && !showBottomBar && (
148 <>
149 <DesktopLeftNav />
150 <DesktopRightNav routeName={activeRoute.name} />
151 </>
152 )}
153 </NavigationContent>
154 )
155}
156
157export const createNativeStackNavigatorWithAuth = createNavigatorFactory<
158 StackNavigationState<ParamListBase>,
159 NativeStackNavigationOptionsWithAuth,
160 NativeStackNavigationEventMap,
161 typeof NativeStackNavigator
162>(NativeStackNavigator)