mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import * as React from 'react'
2import {View} from 'react-native'
3import {PWI_ENABLED, NEW_ONBOARDING_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 {Deactivated} from '#/screens/Deactivated'
39import {LoggedOut} from '../com/auth/LoggedOut'
40import {Onboarding} from '../com/auth/Onboarding'
41import {Onboarding as NewOnboarding} from '#/screens/Onboarding'
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 ((!PWI_ENABLED || activeRouteRequiresAuth) && !hasSession) {
106 return <LoggedOut />
107 }
108 if (hasSession && currentAccount?.deactivated) {
109 return <Deactivated />
110 }
111 if (showLoggedOut) {
112 return <LoggedOut onDismiss={() => setShowLoggedOut(false)} />
113 }
114 if (onboardingState.isActive) {
115 if (NEW_ONBOARDING_ENABLED) {
116 return <NewOnboarding />
117 } else {
118 return <Onboarding />
119 }
120 }
121 const newDescriptors: typeof descriptors = {}
122 for (let key in descriptors) {
123 const descriptor = descriptors[key]
124 const requireAuth = descriptor.options.requireAuth ?? false
125 newDescriptors[key] = {
126 ...descriptor,
127 render() {
128 if (requireAuth && !hasSession) {
129 return <View />
130 } else {
131 return descriptor.render()
132 }
133 },
134 }
135 }
136
137 // 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
138 // on both tablet and mobile web so that we see the sign up CTA.
139 const showBottomBar = hasSession ? isMobile : isTabletOrMobile
140
141 return (
142 <NavigationContent>
143 <NativeStackView
144 {...rest}
145 state={state}
146 navigation={navigation}
147 descriptors={newDescriptors}
148 />
149 {isWeb && showBottomBar && <BottomBarWeb />}
150 {isWeb && !showBottomBar && (
151 <>
152 <DesktopLeftNav />
153 <DesktopRightNav routeName={activeRoute.name} />
154 </>
155 )}
156 </NavigationContent>
157 )
158}
159
160export const createNativeStackNavigatorWithAuth = createNavigatorFactory<
161 StackNavigationState<ParamListBase>,
162 NativeStackNavigationOptionsWithAuth,
163 NativeStackNavigationEventMap,
164 typeof NativeStackNavigator
165>(NativeStackNavigator)