Bluesky app fork with some witchin' additions 馃挮
at main 194 lines 6.3 kB view raw
1import * as React from 'react' 2import {View} from 'react-native' 3// Based on @react-navigation/native-stack/src/navigators/createNativeStackNavigator.ts 4// MIT License 5// Copyright (c) 2017 React Navigation Contributors 6import { 7 createNavigatorFactory, 8 type EventArg, 9 type NavigatorTypeBagBase, 10 type ParamListBase, 11 type StackActionHelpers, 12 StackActions, 13 type StackNavigationState, 14 StackRouter, 15 type StackRouterOptions, 16 type StaticConfig, 17 type TypedNavigator, 18 useNavigationBuilder, 19} from '@react-navigation/native' 20import {NativeStackView} from '@react-navigation/native-stack' 21import { 22 type NativeStackNavigationEventMap, 23 type NativeStackNavigationOptions, 24 type NativeStackNavigationProp, 25 type NativeStackNavigatorProps, 26} from '@react-navigation/native-stack' 27 28import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 29import {useSession} from '#/state/session' 30import {useOnboardingState} from '#/state/shell' 31import { 32 useLoggedOutView, 33 useLoggedOutViewControls, 34} from '#/state/shell/logged-out' 35import {LoggedOut} from '#/view/com/auth/LoggedOut' 36import {Onboarding} from '#/screens/Onboarding' 37import {SignupQueued} from '#/screens/SignupQueued' 38import {atoms as a, useLayoutBreakpoints} from '#/alf' 39import {PolicyUpdateOverlay} from '#/components/PolicyUpdateOverlay' 40import {IS_NATIVE, IS_WEB} from '#/env' 41import {BottomBarWeb} from './bottom-bar/BottomBarWeb' 42import {DesktopLeftNav} from './desktop/LeftNav' 43import {DesktopRightNav} from './desktop/RightNav' 44 45type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & { 46 requireAuth?: boolean 47} 48 49function NativeStackNavigator({ 50 id, 51 initialRouteName, 52 UNSTABLE_routeNamesChangeBehavior, 53 children, 54 layout, 55 screenListeners, 56 screenOptions, 57 screenLayout, 58 UNSTABLE_router, 59 ...rest 60}: NativeStackNavigatorProps) { 61 // --- this is copy and pasted from the original native stack navigator --- 62 const {state, describe, descriptors, navigation, NavigationContent} = 63 useNavigationBuilder< 64 StackNavigationState<ParamListBase>, 65 StackRouterOptions, 66 StackActionHelpers<ParamListBase>, 67 NativeStackNavigationOptionsWithAuth, 68 NativeStackNavigationEventMap 69 >(StackRouter, { 70 id, 71 initialRouteName, 72 UNSTABLE_routeNamesChangeBehavior, 73 children, 74 layout, 75 screenListeners, 76 screenOptions, 77 screenLayout, 78 UNSTABLE_router, 79 }) 80 81 React.useEffect( 82 () => 83 // @ts-expect-error: there may not be a tab navigator in parent 84 navigation?.addListener?.('tabPress', (e: any) => { 85 const isFocused = navigation.isFocused() 86 87 // Run the operation in the next frame so we're sure all listeners have been run 88 // This is necessary to know if preventDefault() has been called 89 requestAnimationFrame(() => { 90 if ( 91 state.index > 0 && 92 isFocused && 93 !(e as EventArg<'tabPress', true>).defaultPrevented 94 ) { 95 // When user taps on already focused tab and we're inside the tab, 96 // reset the stack to replicate native behaviour 97 navigation.dispatch({ 98 ...StackActions.popToTop(), 99 target: state.key, 100 }) 101 } 102 }) 103 }), 104 [navigation, state.index, state.key], 105 ) 106 107 // --- our custom logic starts here --- 108 const {hasSession, currentAccount} = useSession() 109 const activeRoute = state.routes[state.index] 110 const activeDescriptor = descriptors[activeRoute.key] 111 const activeRouteRequiresAuth = activeDescriptor.options.requireAuth ?? false 112 const onboardingState = useOnboardingState() 113 const {showLoggedOut} = useLoggedOutView() 114 const {setShowLoggedOut} = useLoggedOutViewControls() 115 const {isMobile} = useWebMediaQueries() 116 const {leftNavMinimal} = useLayoutBreakpoints() 117 if (!hasSession && (activeRouteRequiresAuth || IS_NATIVE)) { 118 return <LoggedOut /> 119 } 120 if (hasSession && currentAccount?.signupQueued) { 121 return <SignupQueued /> 122 } 123 if (showLoggedOut) { 124 return <LoggedOut onDismiss={() => setShowLoggedOut(false)} /> 125 } 126 if (onboardingState.isActive) { 127 return <Onboarding /> 128 } 129 const newDescriptors: typeof descriptors = {} 130 for (let key in descriptors) { 131 const descriptor = descriptors[key] 132 const requireAuth = descriptor.options.requireAuth ?? false 133 newDescriptors[key] = { 134 ...descriptor, 135 render() { 136 if (requireAuth && !hasSession) { 137 return <View /> 138 } else { 139 return descriptor.render() 140 } 141 }, 142 } 143 } 144 145 // 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 146 // on both tablet and mobile web so that we see the create account CTA. 147 const showBottomBar = hasSession ? isMobile : leftNavMinimal 148 149 return ( 150 <NavigationContent> 151 <View role="main" style={a.flex_1}> 152 <NativeStackView 153 {...rest} 154 state={state} 155 navigation={navigation} 156 descriptors={descriptors} 157 describe={describe} 158 /> 159 </View> 160 {IS_WEB && ( 161 <> 162 {showBottomBar ? <BottomBarWeb /> : <DesktopLeftNav />} 163 {!isMobile && <DesktopRightNav routeName={activeRoute.name} />} 164 </> 165 )} 166 167 {/* Only shown after logged in and onboaring etc are complete */} 168 {hasSession && <PolicyUpdateOverlay />} 169 </NavigationContent> 170 ) 171} 172 173export function createNativeStackNavigatorWithAuth< 174 const ParamList extends ParamListBase, 175 const NavigatorID extends string | undefined = string | undefined, 176 const TypeBag extends NavigatorTypeBagBase = { 177 ParamList: ParamList 178 NavigatorID: NavigatorID 179 State: StackNavigationState<ParamList> 180 ScreenOptions: NativeStackNavigationOptionsWithAuth 181 EventMap: NativeStackNavigationEventMap 182 NavigationList: { 183 [RouteName in keyof ParamList]: NativeStackNavigationProp< 184 ParamList, 185 RouteName, 186 NavigatorID 187 > 188 } 189 Navigator: typeof NativeStackNavigator 190 }, 191 const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>, 192>(config?: Config): TypedNavigator<TypeBag, Config> { 193 return createNavigatorFactory(NativeStackNavigator)(config) 194}