diff --git a/src/Navigation.tsx b/src/Navigation.tsx index e71148a2cd..70ddd3bc00 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -15,6 +15,7 @@ import { NavigationContainer, StackActions, } from '@react-navigation/native' +import {createNativeStackNavigator} from '@react-navigation/native-stack' import {timeout} from '#/lib/async/timeout' import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' @@ -30,6 +31,7 @@ import {buildStateObject} from '#/lib/routes/helpers' import { type AllNavigatorParams, type BottomTabNavigatorParams, + type CoreNavigatorParams, type FlatNavigatorParams, type HomeTabNavigatorParams, type MessagesTabNavigatorParams, @@ -71,6 +73,16 @@ import {SupportScreen} from '#/view/screens/Support' import {TermsOfServiceScreen} from '#/view/screens/TermsOfService' import {BottomBar} from '#/view/shell/bottom-bar/BottomBar' import {createNativeStackNavigatorWithAuth} from '#/view/shell/createNativeStackNavigatorWithAuth' +import {ForgotPasswordScreen} from '#/screens/Authentication/ForgotPasswordScreen' +import {LandingScreen} from '#/screens/Authentication/LandingScreen' +import {PasswordUpdatedScreen} from '#/screens/Authentication/PasswordUpdatedScreen' +import {SelectAccountScreen} from '#/screens/Authentication/SelectAccountScreen' +import {SetNewPasswordScreen} from '#/screens/Authentication/SetNewPasswordScreen' +import {SignInScreen} from '#/screens/Authentication/SignInScreen' +import {SignUpCaptchaScreen} from '#/screens/Authentication/SignUpCaptchaScreen' +import {SignUpHandleScreen} from '#/screens/Authentication/SignUpHandleScreen' +import {SignUpInfoScreen} from '#/screens/Authentication/SignUpInfoScreen' +import {StarterPackLandingScreen} from '#/screens/Authentication/StarterPackLandingScreen' import {SharedPreferencesTesterScreen} from '#/screens/E2E/SharedPreferencesTesterScreen' import HashtagScreen from '#/screens/Hashtag' import {MessagesScreen} from '#/screens/Messages/ChatList' @@ -135,10 +147,12 @@ import {Referrer} from '../modules/expo-bluesky-swiss-army' import {useAccountSwitcher} from './lib/hooks/useAccountSwitcher' import {useNonReactiveCallback} from './lib/hooks/useNonReactiveCallback' import {useLoggedOutViewControls} from './state/shell/logged-out' +import {useLoggedOutView} from './state/shell/logged-out' import {useCloseAllActiveElements} from './state/util' const navigationRef = createNavigationContainerRef() +const Core = createNativeStackNavigator() const HomeTab = createNativeStackNavigatorWithAuth() const SearchTab = createNativeStackNavigatorWithAuth() const NotificationsTab = @@ -768,6 +782,71 @@ const FlatNavigator = () => { ) } +/** + * The core navigator on native handles authentication, switching between + * the main tab navigator when logged in and the auth screen when logged out. + */ +export const NativeNavigator = () => { + const t = useTheme() + const {hasSession} = useSession() + const {showLoggedOut} = useLoggedOutView() + + return ( + + {hasSession && !showLoggedOut ? ( + TabsNavigator} + options={{animation: 'fade', animationDuration: 200}} + /> + ) : ( + <> + LandingScreen} + options={{animation: 'fade', animationDuration: 200}} + /> + StarterPackLandingScreen} + /> + + SelectAccountScreen} + /> + SignInScreen} /> + ForgotPasswordScreen} + /> + SetNewPasswordScreen} + /> + PasswordUpdatedScreen} + /> + SignUpInfoScreen} + /> + SignUpHandleScreen} + /> + SignUpCaptchaScreen} + /> + + + )} + + ) +} + /** * The RoutesContainer should wrap all components which need access * to the navigation context. diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index b1db5caa64..c1a63c48d2 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -1,6 +1,7 @@ import {type NavigationState, type PartialState} from '@react-navigation/native' import {type NativeStackNavigationProp} from '@react-navigation/native-stack' +import {type SessionAccount} from '#/state/session' import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types' export type {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -133,6 +134,33 @@ export type AllNavigatorParams = CommonNavigatorParams & { Messages: {animation?: 'push' | 'pop'} } +/** + * Authentication screens are separate from the main navigator -sfn + */ +export type AuthNavigatorParams = { + Landing: undefined + StarterPackLanding: {uri: string} + SelectAccount: undefined + // TODO: Confirm this is not via query params on web + // Move to context instead if so + SignIn?: {account?: SessionAccount} + ForgotPassword: undefined + SetNewPassword: undefined + PasswordUpdated: undefined + SignUpInfo: undefined + SignUpHandle: undefined + SignUpCaptcha: undefined +} + +/** + * On native, the root navigator is a stack navigator that switches between + * the tab navigator, in the `App` screen, and the auth screens. + * which screens are mounted depends on the auth state -sfn + */ +export type CoreNavigatorParams = { + App: undefined +} & AuthNavigatorParams + // NOTE // this isn't strictly correct but it should be close enough // a TS wizard might be able to get this 100% diff --git a/src/screens/Authentication/ForgotPasswordScreen.tsx b/src/screens/Authentication/ForgotPasswordScreen.tsx new file mode 100644 index 0000000000..33a3aadf2b --- /dev/null +++ b/src/screens/Authentication/ForgotPasswordScreen.tsx @@ -0,0 +1,22 @@ +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function ForgotPasswordScreen({}: Props) { + return ( + + + + + + + + TODO + + + ) +} diff --git a/src/screens/Authentication/LandingScreen.tsx b/src/screens/Authentication/LandingScreen.tsx new file mode 100644 index 0000000000..ed56f4bd9a --- /dev/null +++ b/src/screens/Authentication/LandingScreen.tsx @@ -0,0 +1,109 @@ +import {View} from 'react-native' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {useSession} from '#/state/session' +import { + useLoggedOutView, + useLoggedOutViewControls, +} from '#/state/shell/logged-out' +import {Logo} from '#/view/icons/Logo' +import {Logotype} from '#/view/icons/Logotype' +import {atoms as a, useTheme} from '#/alf' +import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {TimesLarge_Stroke2_Corner0_Rounded as CloseIcon} from '#/components/icons/Times' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function LandingScreen({navigation}: Props) { + const {_} = useLingui() + const t = useTheme() + const insets = useSafeAreaInsets() + + const {accounts} = useSession() + const {showLoggedOut, requestedAccountSwitchTo} = useLoggedOutView() + const {setShowLoggedOut} = useLoggedOutViewControls() + const requestedAccount = accounts.find( + acc => acc.did === requestedAccountSwitchTo, + ) + + return ( + + {showLoggedOut && ( + + + + + + + + )} + + + + + + + + + What's up? + + + + + + + + + + + + + ) +} diff --git a/src/screens/Authentication/PasswordUpdatedScreen.tsx b/src/screens/Authentication/PasswordUpdatedScreen.tsx new file mode 100644 index 0000000000..cc44be5da2 --- /dev/null +++ b/src/screens/Authentication/PasswordUpdatedScreen.tsx @@ -0,0 +1,22 @@ +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function PasswordUpdatedScreen({}: Props) { + return ( + + + + + + + + TODO + + + ) +} diff --git a/src/screens/Authentication/SelectAccountScreen.tsx b/src/screens/Authentication/SelectAccountScreen.tsx new file mode 100644 index 0000000000..ad955d9a7b --- /dev/null +++ b/src/screens/Authentication/SelectAccountScreen.tsx @@ -0,0 +1,78 @@ +import {useState} from 'react' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {logger} from '#/logger' +import {type SessionAccount, useSession, useSessionApi} from '#/state/session' +import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import * as Toast from '#/view/com/util/Toast' +import {atoms as a, native} from '#/alf' +import {AccountList} from '#/components/AccountList' +import * as TextField from '#/components/forms/TextField' +import * as Layout from '#/components/Layout' + +type Props = NativeStackScreenProps +export function SelectAccountScreen({navigation}: Props) { + const [pendingDid, setPendingDid] = useState(null) + const {_} = useLingui() + const {currentAccount} = useSession() + const {resumeSession} = useSessionApi() + const {setShowLoggedOut} = useLoggedOutViewControls() + + const onSelect = async (account: SessionAccount) => { + if (pendingDid) { + // The session API isn't resilient to race conditions so let's just ignore this. + return + } + if (!account.accessJwt) { + // Move to login form. + navigation.push('SignIn', {account}) + return + } + if (account.did === currentAccount?.did) { + setShowLoggedOut(false) + return + } + try { + setPendingDid(account.did) + await resumeSession(account) + logger.metric('account:loggedIn', { + logContext: 'ChooseAccountForm', + withPassword: false, + }) + Toast.show(_(msg`Signed in as @${account.handle}`)) + } catch (e: any) { + logger.error('choose account: initSession failed', { + message: e.message, + }) + // Move to login form. + navigation.push('SignIn', {account}) + } finally { + setPendingDid(null) + } + } + + return ( + + + + + + + + + Sign in as... + + navigation.push('SignIn')} + pendingDid={pendingDid} + /> + + + ) +} diff --git a/src/screens/Authentication/SetNewPasswordScreen.tsx b/src/screens/Authentication/SetNewPasswordScreen.tsx new file mode 100644 index 0000000000..7912616614 --- /dev/null +++ b/src/screens/Authentication/SetNewPasswordScreen.tsx @@ -0,0 +1,22 @@ +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function SetNewPasswordScreen({}: Props) { + return ( + + + + + + + + TODO + + + ) +} diff --git a/src/screens/Authentication/SignInScreen.tsx b/src/screens/Authentication/SignInScreen.tsx new file mode 100644 index 0000000000..367f45f3b4 --- /dev/null +++ b/src/screens/Authentication/SignInScreen.tsx @@ -0,0 +1,105 @@ +import {useEffect, useRef, useState} from 'react' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {DEFAULT_SERVICE} from '#/lib/constants' +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {logger} from '#/logger' +import {useServiceQuery} from '#/state/queries/service' +import {atoms as a, useTheme} from '#/alf' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' +import {SignInForm} from './components/SignInForm' + +type Props = NativeStackScreenProps +export function SignInScreen({ + navigation, + route: {params: {account: requestedAccount} = {}}, +}: Props) { + const t = useTheme() + const {_} = useLingui() + const failedAttemptCountRef = useRef(0) + const startTimeRef = useRef(Date.now()) + + const [error, setError] = useState('') + const [serviceUrl, setServiceUrl] = useState( + requestedAccount?.service || DEFAULT_SERVICE, + ) + const { + data: serviceDescription, + error: serviceError, + refetch: refetchService, + } = useServiceQuery(serviceUrl) + + useEffect(() => { + if (serviceError) { + setError( + _( + msg`Unable to contact your service. Please check your Internet connection.`, + ), + ) + logger.warn(`Failed to fetch service description for ${serviceUrl}`, { + error: String(serviceError), + }) + logger.metric('signin:hostingProviderFailedResolution', {}) + } else { + setError('') + } + }, [serviceError, serviceUrl, _]) + + const onPressForgotPassword = () => { + navigation.push('ForgotPassword') + logger.metric('signin:forgotPasswordPressed', {}) + } + + const onAttemptSuccess = () => { + logger.metric('signin:success', { + isUsingCustomProvider: serviceUrl !== DEFAULT_SERVICE, + timeTakenSeconds: Math.round((Date.now() - startTimeRef.current) / 1000), + failedAttemptsCount: failedAttemptCountRef.current, + }) + } + + const onAttemptFailed = () => { + failedAttemptCountRef.current += 1 + } + + return ( + + + + + + + + Log in + + + + New to Bluesky?{' '} + navigation.push('SignUpInfo')} + style={[a.text_md, {color: t.palette.primary_500}]}> + Create account + + + + + + ) +} diff --git a/src/screens/Authentication/SignUpCaptchaScreen.tsx b/src/screens/Authentication/SignUpCaptchaScreen.tsx new file mode 100644 index 0000000000..bba703dce2 --- /dev/null +++ b/src/screens/Authentication/SignUpCaptchaScreen.tsx @@ -0,0 +1,22 @@ +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function SignUpCaptchaScreen({}: Props) { + return ( + + + + + + + + TODO + + + ) +} diff --git a/src/screens/Authentication/SignUpHandleScreen.tsx b/src/screens/Authentication/SignUpHandleScreen.tsx new file mode 100644 index 0000000000..d4bdfba197 --- /dev/null +++ b/src/screens/Authentication/SignUpHandleScreen.tsx @@ -0,0 +1,22 @@ +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function SignUpHandleScreen({}: Props) { + return ( + + + + + + + + TODO + + + ) +} diff --git a/src/screens/Authentication/SignUpInfoScreen.tsx b/src/screens/Authentication/SignUpInfoScreen.tsx new file mode 100644 index 0000000000..09c59c0404 --- /dev/null +++ b/src/screens/Authentication/SignUpInfoScreen.tsx @@ -0,0 +1,22 @@ +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function SignUpInfoScreen({}: Props) { + return ( + + + + + + + + TODO + + + ) +} diff --git a/src/screens/Authentication/StarterPackLandingScreen.tsx b/src/screens/Authentication/StarterPackLandingScreen.tsx new file mode 100644 index 0000000000..1c42660e1e --- /dev/null +++ b/src/screens/Authentication/StarterPackLandingScreen.tsx @@ -0,0 +1,17 @@ +import { + type AuthNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps +export function StarterPackLandingScreen({}: Props) { + return ( + + + TODO + + + ) +} diff --git a/src/screens/Authentication/components/SignInForm.tsx b/src/screens/Authentication/components/SignInForm.tsx new file mode 100644 index 0000000000..2ac6a86a01 --- /dev/null +++ b/src/screens/Authentication/components/SignInForm.tsx @@ -0,0 +1,340 @@ +import {useRef, useState} from 'react' +import { + ActivityIndicator, + Keyboard, + LayoutAnimation, + type TextInput, + View, +} from 'react-native' +import { + ComAtprotoServerCreateSession, + type ComAtprotoServerDescribeServer, +} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useRequestNotificationsPermission} from '#/lib/notifications/notifications' +import {isNetworkError} from '#/lib/strings/errors' +import {cleanError} from '#/lib/strings/errors' +import {createFullHandle} from '#/lib/strings/handles' +import {logger} from '#/logger' +import {useSetHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs' +import {useSessionApi} from '#/state/session' +import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {FormError} from '#/components/forms/FormError' +import * as TextField from '#/components/forms/TextField' +import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At' +import {Lock_Stroke2_Corner0_Rounded as Lock} from '#/components/icons/Lock' +import {Ticket_Stroke2_Corner0_Rounded as Ticket} from '#/components/icons/Ticket' +import {Loader} from '#/components/Loader' +import {Text} from '#/components/Typography' + +type ServiceDescription = ComAtprotoServerDescribeServer.OutputSchema + +export function SignInForm({ + error, + serviceUrl, + serviceDescription, + initialHandle = '', + setError, + // setServiceUrl, + onPressRetryConnect, + onPressForgotPassword, + onAttemptSuccess, + onAttemptFailed, +}: { + error: string + serviceUrl: string + serviceDescription: ServiceDescription | undefined + initialHandle?: string + setError: (v: string) => void + setServiceUrl: (v: string) => void + onPressRetryConnect: () => void + onPressForgotPassword: () => void + onAttemptSuccess: () => void + onAttemptFailed: () => void +}) { + const t = useTheme() + const [isProcessing, setIsProcessing] = useState(false) + const [isAuthFactorTokenNeeded, setIsAuthFactorTokenNeeded] = useState(false) + const [isAuthFactorTokenValueEmpty, setIsAuthFactorTokenValueEmpty] = + useState(true) + const identifierValueRef = useRef(initialHandle || '') + const passwordValueRef = useRef('') + const authFactorTokenValueRef = useRef('') + const passwordRef = useRef(null) + const {_} = useLingui() + const {login} = useSessionApi() + const requestNotificationsPermission = useRequestNotificationsPermission() + const {setShowLoggedOut} = useLoggedOutViewControls() + const setHasCheckedForStarterPack = useSetHasCheckedForStarterPack() + + // const onPressSelectService = () => { + // Keyboard.dismiss() + // } + + const onPressNext = async () => { + if (isProcessing) return + Keyboard.dismiss() + setError('') + + const identifier = identifierValueRef.current.toLowerCase().trim() + const password = passwordValueRef.current + const authFactorToken = authFactorTokenValueRef.current + + if (!identifier) { + setError(_(msg`Please enter your username`)) + return + } + + if (!password) { + setError(_(msg`Please enter your password`)) + return + } + + setIsProcessing(true) + + try { + // try to guess the handle if the user just gave their own username + let fullIdent = identifier + if ( + !identifier.includes('@') && // not an email + !identifier.includes('.') && // not a domain + serviceDescription && + serviceDescription.availableUserDomains.length > 0 + ) { + let matched = false + for (const domain of serviceDescription.availableUserDomains) { + if (fullIdent.endsWith(domain)) { + matched = true + } + } + if (!matched) { + fullIdent = createFullHandle( + identifier, + serviceDescription.availableUserDomains[0], + ) + } + } + + // TODO remove double login + await login( + { + service: serviceUrl, + identifier: fullIdent, + password, + authFactorToken: authFactorToken.trim(), + }, + 'LoginForm', + ) + onAttemptSuccess() + setShowLoggedOut(false) + setHasCheckedForStarterPack(true) + requestNotificationsPermission('Login') + } catch (e: any) { + const errMsg = e.toString() + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) + setIsProcessing(false) + if ( + e instanceof ComAtprotoServerCreateSession.AuthFactorTokenRequiredError + ) { + setIsAuthFactorTokenNeeded(true) + } else { + onAttemptFailed() + if (errMsg.includes('Token is invalid')) { + logger.debug('Failed to login due to invalid 2fa token', { + error: errMsg, + }) + setError(_(msg`Invalid 2FA confirmation code.`)) + } else if ( + errMsg.includes('Authentication Required') || + errMsg.includes('Invalid identifier or password') + ) { + logger.debug('Failed to login due to invalid credentials', { + error: errMsg, + }) + setError(_(msg`Incorrect username or password`)) + } else if (isNetworkError(e)) { + logger.warn('Failed to login due to network error', {error: errMsg}) + setError( + _( + msg`Unable to contact your service. Please check your Internet connection.`, + ), + ) + } else { + logger.warn('Failed to login', {error: errMsg}) + setError(cleanError(errMsg)) + } + } + } + } + + return ( + + + + Account + + + + + { + identifierValueRef.current = v + }} + onSubmitEditing={() => { + passwordRef.current?.focus() + }} + blurOnSubmit={false} // prevents flickering due to onSubmitEditing going to next field + editable={!isProcessing} + accessibilityHint={_( + msg`Enter the username or email address you used when you created your account`, + )} + /> + + + + + { + passwordValueRef.current = v + }} + onSubmitEditing={onPressNext} + blurOnSubmit={false} // HACK: https://github.com/facebook/react-native/issues/21911#issuecomment-558343069 Keyboard blur behavior is now handled in onSubmitEditing + editable={!isProcessing} + accessibilityHint={_(msg`Enter your password`)} + /> + + + + + {isAuthFactorTokenNeeded && ( + + + 2FA Confirmation + + + + { + setIsAuthFactorTokenValueEmpty(v === '') + authFactorTokenValueRef.current = v + }} + onSubmitEditing={onPressNext} + editable={!isProcessing} + accessibilityHint={_( + msg`Input the code which has been emailed to you`, + )} + style={[ + { + textTransform: isAuthFactorTokenValueEmpty + ? 'none' + : 'uppercase', + }, + ]} + /> + + + + Check your email for a sign in code and enter it here. + + + + )} + + + {!serviceDescription && error ? ( + + ) : !serviceDescription ? ( + <> + + + Connecting... + + + ) : ( + + )} + + + {/* */} + + ) +} diff --git a/src/view/com/util/fab/FABInner.tsx b/src/view/com/util/fab/FABInner.tsx index 77e283625c..889afd317b 100644 --- a/src/view/com/util/fab/FABInner.tsx +++ b/src/view/com/util/fab/FABInner.tsx @@ -1,5 +1,5 @@ -import {ComponentProps} from 'react' -import {StyleSheet, TouchableWithoutFeedback} from 'react-native' +import {type ComponentProps} from 'react' +import {StyleSheet} from 'react-native' import Animated from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {LinearGradient} from 'expo-linear-gradient' @@ -13,8 +13,7 @@ import {gradients} from '#/lib/styles' import {isWeb} from '#/platform/detection' import {ios} from '#/alf' -export interface FABProps - extends ComponentProps { +export interface FABProps extends ComponentProps { testID?: string icon: JSX.Element } diff --git a/src/view/shell/createNativeStackNavigatorWithAuth.tsx b/src/view/shell/createNativeStackNavigatorWithAuth.tsx index 1c32971d42..284dfea90e 100644 --- a/src/view/shell/createNativeStackNavigatorWithAuth.tsx +++ b/src/view/shell/createNativeStackNavigatorWithAuth.tsx @@ -27,7 +27,7 @@ import { import {PWI_ENABLED} from '#/lib/build-flags' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {isNative, isWeb} from '#/platform/detection' +import {isWeb} from '#/platform/detection' import {useSession} from '#/state/session' import {useOnboardingState} from '#/state/shell' import { @@ -112,7 +112,8 @@ function NativeStackNavigator({ const {setShowLoggedOut} = useLoggedOutViewControls() const {isMobile} = useWebMediaQueries() const {leftNavMinimal} = useLayoutBreakpoints() - if (!hasSession && (!PWI_ENABLED || activeRouteRequiresAuth || isNative)) { + // Temp: use old system for web. TODO: unify + if (isWeb && !hasSession && (!PWI_ENABLED || activeRouteRequiresAuth)) { return } if (hasSession && currentAccount?.signupQueued) { @@ -121,7 +122,7 @@ function NativeStackNavigator({ if (hasSession && currentAccount?.status === 'takendown') { return } - if (showLoggedOut) { + if (isWeb && showLoggedOut) { return setShowLoggedOut(false)} /> } if (currentAccount?.status === 'deactivated') { diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 8c08ec0c03..08cc5b6fef 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -31,7 +31,7 @@ import {LinkWarningDialog} from '#/components/dialogs/LinkWarning' import {MutedWordsDialog} from '#/components/dialogs/MutedWords' import {SigninDialog} from '#/components/dialogs/Signin' import {Outlet as PortalOutlet} from '#/components/Portal' -import {RoutesContainer, TabsNavigator} from '#/Navigation' +import {NativeNavigator, RoutesContainer} from '#/Navigation' import {BottomSheetOutlet} from '../../../modules/bottom-sheet' import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView' import {Composer} from './Composer' @@ -146,7 +146,7 @@ function ShellInner() { dim: 'rgba(10, 13, 16, 0.8)', }), }}> - +