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