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 {LoggedOut} from '../com/auth/LoggedOut'
36import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
37import {DesktopLeftNav} from './desktop/LeftNav'
38import {DesktopRightNav} from './desktop/RightNav'
39
40type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & {
41 requireAuth?: boolean
42}
43
44function NativeStackNavigator({
45 id,
46 initialRouteName,
47 children,
48 screenListeners,
49 screenOptions,
50 ...rest
51}: NativeStackNavigatorProps) {
52 // --- this is copy and pasted from the original native stack navigator ---
53 const {state, descriptors, navigation, NavigationContent} =
54 useNavigationBuilder<
55 StackNavigationState<ParamListBase>,
56 StackRouterOptions,
57 StackActionHelpers<ParamListBase>,
58 NativeStackNavigationOptionsWithAuth,
59 NativeStackNavigationEventMap
60 >(StackRouter, {
61 id,
62 initialRouteName,
63 children,
64 screenListeners,
65 screenOptions,
66 })
67 React.useEffect(
68 () =>
69 // @ts-expect-error: there may not be a tab navigator in parent
70 navigation?.addListener?.('tabPress', (e: any) => {
71 const isFocused = navigation.isFocused()
72
73 // Run the operation in the next frame so we're sure all listeners have been run
74 // This is necessary to know if preventDefault() has been called
75 requestAnimationFrame(() => {
76 if (
77 state.index > 0 &&
78 isFocused &&
79 !(e as EventArg<'tabPress', true>).defaultPrevented
80 ) {
81 // When user taps on already focused tab and we're inside the tab,
82 // reset the stack to replicate native behaviour
83 navigation.dispatch({
84 ...StackActions.popToTop(),
85 target: state.key,
86 })
87 }
88 })
89 }),
90 [navigation, state.index, state.key],
91 )
92
93 // --- our custom logic starts here ---
94 const {hasSession, currentAccount} = useSession()
95 const activeRoute = state.routes[state.index]
96 const activeDescriptor = descriptors[activeRoute.key]
97 const activeRouteRequiresAuth = activeDescriptor.options.requireAuth ?? false
98 const onboardingState = useOnboardingState()
99 const {showLoggedOut} = useLoggedOutView()
100 const {setShowLoggedOut} = useLoggedOutViewControls()
101 const {isMobile, isTabletOrMobile} = useWebMediaQueries()
102 if ((!PWI_ENABLED || activeRouteRequiresAuth) && !hasSession) {
103 return <LoggedOut />
104 }
105 if (hasSession && currentAccount?.deactivated) {
106 return <Deactivated />
107 }
108 if (showLoggedOut) {
109 return <LoggedOut onDismiss={() => setShowLoggedOut(false)} />
110 }
111 if (onboardingState.isActive) {
112 return <Onboarding />
113 }
114 const newDescriptors: typeof descriptors = {}
115 for (let key in descriptors) {
116 const descriptor = descriptors[key]
117 const requireAuth = descriptor.options.requireAuth ?? false
118 newDescriptors[key] = {
119 ...descriptor,
120 render() {
121 if (requireAuth && !hasSession) {
122 return <View />
123 } else {
124 return descriptor.render()
125 }
126 },
127 }
128 }
129
130 // 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
131 // on both tablet and mobile web so that we see the sign up CTA.
132 const showBottomBar = hasSession ? isMobile : isTabletOrMobile
133
134 return (
135 <NavigationContent>
136 <NativeStackView
137 {...rest}
138 state={state}
139 navigation={navigation}
140 descriptors={newDescriptors}
141 />
142 {isWeb && showBottomBar && <BottomBarWeb />}
143 {isWeb && !showBottomBar && (
144 <>
145 <DesktopLeftNav />
146 <DesktopRightNav routeName={activeRoute.name} />
147 </>
148 )}
149 </NavigationContent>
150 )
151}
152
153export const createNativeStackNavigatorWithAuth = createNavigatorFactory<
154 StackNavigationState<ParamListBase>,
155 NativeStackNavigationOptionsWithAuth,
156 NativeStackNavigationEventMap,
157 typeof NativeStackNavigator
158>(NativeStackNavigator)