diff --git a/src/state/models/session.ts b/src/state/models/session.ts index b79283be..7c4d0931 100644 --- a/src/state/models/session.ts +++ b/src/state/models/session.ts @@ -122,7 +122,9 @@ export class SessionModel { try { return await this.resumeSession(sess) } finally { - this.isResumingSession = false + runInAction(() => { + this.isResumingSession = false + }) } } else { this.rootStore.log.debug( diff --git a/src/view/com/login/CreateAccount.tsx b/src/view/com/auth/CreateAccount.tsx similarity index 100% rename from src/view/com/login/CreateAccount.tsx rename to src/view/com/auth/CreateAccount.tsx diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx new file mode 100644 index 00000000..47dd51d9 --- /dev/null +++ b/src/view/com/auth/LoggedOut.tsx @@ -0,0 +1,67 @@ +import React from 'react' +import {SafeAreaView} from 'react-native' +import {observer} from 'mobx-react-lite' +import {Signin} from 'view/com/auth/Signin' +import {CreateAccount} from 'view/com/auth/CreateAccount' +import {ErrorBoundary} from 'view/com/util/ErrorBoundary' +import {s} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {useStores} from 'state/index' +import {useAnalytics} from 'lib/analytics' +import {SplashScreen} from './SplashScreen' +import {CenteredView} from '../util/Views' + +enum ScreenState { + S_SigninOrCreateAccount, + S_Signin, + S_CreateAccount, +} + +export const LoggedOut = observer(() => { + const pal = usePalette('default') + const store = useStores() + const {screen} = useAnalytics() + const [screenState, setScreenState] = React.useState( + ScreenState.S_SigninOrCreateAccount, + ) + + React.useEffect(() => { + screen('Login') + store.shell.setMinimalShellMode(true) + }, [store, screen]) + + if ( + store.session.isResumingSession || + screenState === ScreenState.S_SigninOrCreateAccount + ) { + return ( + setScreenState(ScreenState.S_Signin)} + onPressCreateAccount={() => setScreenState(ScreenState.S_CreateAccount)} + /> + ) + } + + return ( + + + + {screenState === ScreenState.S_Signin ? ( + + setScreenState(ScreenState.S_SigninOrCreateAccount) + } + /> + ) : undefined} + {screenState === ScreenState.S_CreateAccount ? ( + + setScreenState(ScreenState.S_SigninOrCreateAccount) + } + /> + ) : undefined} + + + + ) +}) diff --git a/src/view/com/login/Logo.tsx b/src/view/com/auth/Logo.tsx similarity index 51% rename from src/view/com/login/Logo.tsx rename to src/view/com/auth/Logo.tsx index 7601ea31..ac408cd2 100644 --- a/src/view/com/login/Logo.tsx +++ b/src/view/com/auth/Logo.tsx @@ -1,28 +1,19 @@ import React from 'react' -import {StyleSheet} from 'react-native' -import LinearGradient from 'react-native-linear-gradient' -import {s, gradients} from 'lib/styles' +import {StyleSheet, View} from 'react-native' +import {s, colors} from 'lib/styles' import {Text} from '../util/text/Text' export const LogoTextHero = () => { return ( - + Bluesky - + ) } const styles = StyleSheet.create({ - logo: { - flexDirection: 'row', - justifyContent: 'center', - }, textHero: { flexDirection: 'row', alignItems: 'center', @@ -30,5 +21,6 @@ const styles = StyleSheet.create({ paddingRight: 20, paddingVertical: 15, marginBottom: 20, + backgroundColor: colors.blue3, }, }) diff --git a/src/view/com/login/Signin.tsx b/src/view/com/auth/Signin.tsx similarity index 100% rename from src/view/com/login/Signin.tsx rename to src/view/com/auth/Signin.tsx diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx new file mode 100644 index 00000000..27943f64 --- /dev/null +++ b/src/view/com/auth/SplashScreen.tsx @@ -0,0 +1,92 @@ +import React from 'react' +import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native' +import Image, {Source as ImageSource} from 'view/com/util/images/Image' +import {Text} from 'view/com/util/text/Text' +import {ErrorBoundary} from 'view/com/util/ErrorBoundary' +import {colors} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {CLOUD_SPLASH} from 'lib/assets' +import {CenteredView} from '../util/Views' + +export const SplashScreen = ({ + onPressSignin, + onPressCreateAccount, +}: { + onPressSignin: () => void + onPressCreateAccount: () => void +}) => { + const pal = usePalette('default') + return ( + + + + + + + Bluesky + + + + + + Create a new account + + + + Sign in + + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + height: '100%', + }, + hero: { + flex: 2, + justifyContent: 'center', + }, + bgImg: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + }, + heroText: { + backgroundColor: colors.white, + paddingTop: 10, + paddingBottom: 20, + }, + btns: { + paddingBottom: 40, + }, + title: { + textAlign: 'center', + color: colors.blue3, + fontSize: 68, + fontWeight: 'bold', + }, + btn: { + borderRadius: 4, + paddingVertical: 16, + marginBottom: 20, + marginHorizontal: 20, + backgroundColor: colors.blue3, + }, + btnLabel: { + textAlign: 'center', + fontSize: 21, + color: colors.white, + }, +}) diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx new file mode 100644 index 00000000..05d0355d --- /dev/null +++ b/src/view/com/auth/SplashScreen.web.tsx @@ -0,0 +1,102 @@ +import React from 'react' +import {StyleSheet, TouchableOpacity, View} from 'react-native' +import {Text} from 'view/com/util/text/Text' +import {TextLink} from '../util/Link' +import {ErrorBoundary} from 'view/com/util/ErrorBoundary' +import {s, colors} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {CenteredView} from '../util/Views' + +export const SplashScreen = ({ + onPressSignin, + onPressCreateAccount, +}: { + onPressSignin: () => void + onPressCreateAccount: () => void +}) => { + const pal = usePalette('default') + return ( + + + + Bluesky + See what's next + + + + Create a new account + + + + Sign in + + + + Bluesky will launch soon.{' '} + {' '} + to try the beta before it's publicly available. + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + height: '100%', + backgroundColor: colors.gray1, + }, + containerInner: { + backgroundColor: colors.white, + paddingVertical: 40, + paddingBottom: 50, + paddingHorizontal: 20, + }, + title: { + textAlign: 'center', + color: colors.blue3, + fontSize: 68, + fontWeight: 'bold', + paddingBottom: 10, + }, + subtitle: { + textAlign: 'center', + color: colors.gray5, + fontSize: 52, + fontWeight: 'bold', + paddingBottom: 30, + }, + btns: { + flexDirection: 'row', + paddingBottom: 40, + }, + btn: { + flex: 1, + borderRadius: 30, + paddingVertical: 12, + marginHorizontal: 10, + }, + btnLabel: { + textAlign: 'center', + fontSize: 18, + }, + notice: { + paddingHorizontal: 40, + textAlign: 'center', + }, +}) diff --git a/src/view/com/auth/withAuthRequired.tsx b/src/view/com/auth/withAuthRequired.tsx new file mode 100644 index 00000000..11b67f38 --- /dev/null +++ b/src/view/com/auth/withAuthRequired.tsx @@ -0,0 +1,47 @@ +import React from 'react' +import {ActivityIndicator, StyleSheet, View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {useStores} from 'state/index' +import {LoggedOut} from './LoggedOut' +import {Text} from '../util/text/Text' +import {usePalette} from 'lib/hooks/usePalette' + +export const withAuthRequired =

( + Component: React.ComponentType

, +): React.FC

=> + observer((props: P) => { + const store = useStores() + if (store.session.isResumingSession) { + return + } + if (!store.session.hasSession) { + return + } + return + }) + +function Loading() { + const pal = usePalette('default') + return ( + + + + Firing up the grill... + + + ) +} + +const styles = StyleSheet.create({ + loading: { + height: '100%', + alignContent: 'center', + justifyContent: 'center', + paddingBottom: 100, + }, + loadingText: { + paddingVertical: 20, + paddingHorizontal: 20, + textAlign: 'center', + }, +}) diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 505b1fcf..adc73315 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -4,6 +4,7 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {observer} from 'mobx-react-lite' import useAppState from 'react-native-appstate-hook' import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {Feed} from '../com/posts/Feed' import {LoadLatestBtn} from '../com/util/LoadLatestBtn' @@ -18,95 +19,97 @@ import {ComposeIcon2} from 'lib/icons' const HEADER_HEIGHT = 42 type Props = NativeStackScreenProps -export const HomeScreen = observer(function Home(_opts: Props) { - const store = useStores() - const onMainScroll = useOnMainScroll(store) - const {screen, track} = useAnalytics() - const scrollElRef = React.useRef(null) - const {appState} = useAppState({ - onForeground: () => doPoll(true), - }) - const isFocused = useIsFocused() +export const HomeScreen = withAuthRequired( + observer(function Home(_opts: Props) { + const store = useStores() + const onMainScroll = useOnMainScroll(store) + const {screen, track} = useAnalytics() + const scrollElRef = React.useRef(null) + const {appState} = useAppState({ + onForeground: () => doPoll(true), + }) + const isFocused = useIsFocused() - const doPoll = React.useCallback( - (knownActive = false) => { - if ((!knownActive && appState !== 'active') || !isFocused) { - return - } - if (store.me.mainFeed.isLoading) { - return - } - store.log.debug('HomeScreen: Polling for new posts') - store.me.mainFeed.checkForLatest() - }, - [appState, isFocused, store], - ) + const doPoll = React.useCallback( + (knownActive = false) => { + if ((!knownActive && appState !== 'active') || !isFocused) { + return + } + if (store.me.mainFeed.isLoading) { + return + } + store.log.debug('HomeScreen: Polling for new posts') + store.me.mainFeed.checkForLatest() + }, + [appState, isFocused, store], + ) - const scrollToTop = React.useCallback(() => { - // NOTE: the feed is offset by the height of the collapsing header, - // so we scroll to the negative of that height -prf - scrollElRef.current?.scrollToOffset({offset: -HEADER_HEIGHT}) - }, [scrollElRef]) + const scrollToTop = React.useCallback(() => { + // NOTE: the feed is offset by the height of the collapsing header, + // so we scroll to the negative of that height -prf + scrollElRef.current?.scrollToOffset({offset: -HEADER_HEIGHT}) + }, [scrollElRef]) - useFocusEffect( - React.useCallback(() => { - const softResetSub = store.onScreenSoftReset(scrollToTop) - const feedCleanup = store.me.mainFeed.registerListeners() - const pollInterval = setInterval(doPoll, 15e3) + useFocusEffect( + React.useCallback(() => { + const softResetSub = store.onScreenSoftReset(scrollToTop) + const feedCleanup = store.me.mainFeed.registerListeners() + const pollInterval = setInterval(doPoll, 15e3) - screen('Feed') - store.log.debug('HomeScreen: Updating feed') - if (store.me.mainFeed.hasContent) { - store.me.mainFeed.update() - } + screen('Feed') + store.log.debug('HomeScreen: Updating feed') + if (store.me.mainFeed.hasContent) { + store.me.mainFeed.update() + } - return () => { - clearInterval(pollInterval) - softResetSub.remove() - feedCleanup() - } - }, [store, doPoll, scrollToTop, screen]), - ) + return () => { + clearInterval(pollInterval) + softResetSub.remove() + feedCleanup() + } + }, [store, doPoll, scrollToTop, screen]), + ) - const onPressCompose = React.useCallback(() => { - track('HomeScreen:PressCompose') - store.shell.openComposer({}) - }, [store, track]) + const onPressCompose = React.useCallback(() => { + track('HomeScreen:PressCompose') + store.shell.openComposer({}) + }, [store, track]) - const onPressTryAgain = React.useCallback(() => { - store.me.mainFeed.refresh() - }, [store]) + const onPressTryAgain = React.useCallback(() => { + store.me.mainFeed.refresh() + }, [store]) - const onPressLoadLatest = React.useCallback(() => { - store.me.mainFeed.refresh() - scrollToTop() - }, [store, scrollToTop]) + const onPressLoadLatest = React.useCallback(() => { + store.me.mainFeed.refresh() + scrollToTop() + }, [store, scrollToTop]) - return ( - - {store.shell.isOnboarding && } - - {!store.shell.isOnboarding && ( - - )} - {store.me.mainFeed.hasNewLatest && !store.me.mainFeed.isRefreshing && ( - - )} - } - /> - - ) -}) + return ( + + {store.shell.isOnboarding && } + + {!store.shell.isOnboarding && ( + + )} + {store.me.mainFeed.hasNewLatest && !store.me.mainFeed.isRefreshing && ( + + )} + } + /> + + ) + }), +) diff --git a/src/view/screens/Login.tsx b/src/view/screens/Login.tsx deleted file mode 100644 index 8ff9fc9c..00000000 --- a/src/view/screens/Login.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, {useEffect, useState} from 'react' -import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native' -import Image, {Source as ImageSource} from 'view/com/util/images/Image' -import {observer} from 'mobx-react-lite' -import {Signin} from '../com/login/Signin' -import {CreateAccount} from '../com/login/CreateAccount' -import {Text} from '../com/util/text/Text' -import {ErrorBoundary} from '../com/util/ErrorBoundary' -import {colors} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' -import {useStores} from 'state/index' -import {CLOUD_SPLASH} from 'lib/assets' -import {useAnalytics} from 'lib/analytics' - -enum ScreenState { - S_SigninOrCreateAccount, - S_Signin, - S_CreateAccount, -} - -const SigninOrCreateAccount = ({ - onPressSignin, - onPressCreateAccount, -}: { - onPressSignin: () => void - onPressCreateAccount: () => void -}) => { - const {screen} = useAnalytics() - - useEffect(() => { - screen('Login') - }, [screen]) - - const pal = usePalette('default') - return ( - <> - - - Bluesky - - - - - Create a new account - - - Sign in - - - - ) -} - -export const Login = observer(() => { - const pal = usePalette('default') - const store = useStores() - const [screenState, setScreenState] = useState( - ScreenState.S_SigninOrCreateAccount, - ) - - if ( - store.session.isResumingSession || - screenState === ScreenState.S_SigninOrCreateAccount - ) { - return ( - - - - - {!store.session.isResumingSession && ( - setScreenState(ScreenState.S_Signin)} - onPressCreateAccount={() => - setScreenState(ScreenState.S_CreateAccount) - } - /> - )} - - - - ) - } - - return ( - - - - {screenState === ScreenState.S_Signin ? ( - - setScreenState(ScreenState.S_SigninOrCreateAccount) - } - /> - ) : undefined} - {screenState === ScreenState.S_CreateAccount ? ( - - setScreenState(ScreenState.S_SigninOrCreateAccount) - } - /> - ) : undefined} - - - - ) -}) - -const styles = StyleSheet.create({ - container: { - height: '100%', - }, - outer: { - flex: 1, - }, - hero: { - flex: 2, - justifyContent: 'center', - }, - bgImg: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - }, - heroText: { - backgroundColor: colors.white, - paddingTop: 10, - paddingBottom: 20, - }, - btns: { - paddingBottom: 40, - }, - title: { - textAlign: 'center', - color: colors.blue3, - fontSize: 68, - fontWeight: 'bold', - }, - subtitle: { - textAlign: 'center', - color: colors.blue3, - fontSize: 18, - }, - btn: { - borderRadius: 4, - paddingVertical: 16, - marginBottom: 20, - marginHorizontal: 20, - backgroundColor: colors.blue3, - }, - btnLabel: { - textAlign: 'center', - fontSize: 21, - // fontWeight: '500', - color: colors.white, - }, -}) diff --git a/src/view/screens/Login.web.tsx b/src/view/screens/Login.web.tsx deleted file mode 100644 index 1980d8dc..00000000 --- a/src/view/screens/Login.web.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React, {useState} from 'react' -import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native' -import {observer} from 'mobx-react-lite' -import {CenteredView} from '../com/util/Views' -import {Signin} from '../com/login/Signin' -import {CreateAccount} from '../com/login/CreateAccount' -import {Text} from '../com/util/text/Text' -import {ErrorBoundary} from '../com/util/ErrorBoundary' -import {colors} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' - -enum ScreenState { - S_SigninOrCreateAccount, - S_Signin, - S_CreateAccount, -} - -const SigninOrCreateAccount = ({ - onPressSignin, - onPressCreateAccount, -}: { - onPressSignin: () => void - onPressCreateAccount: () => void -}) => { - const pal = usePalette('default') - return ( - <> - - - Bluesky - - - - - New account - - - Sign in - - - - ) -} - -export const Login = observer(() => { - const pal = usePalette('default') - const [screenState, setScreenState] = useState( - ScreenState.S_SigninOrCreateAccount, - ) - - if (screenState === ScreenState.S_SigninOrCreateAccount) { - return ( - - - setScreenState(ScreenState.S_Signin)} - onPressCreateAccount={() => - setScreenState(ScreenState.S_CreateAccount) - } - /> - - - ) - } - - return ( - - - - {screenState === ScreenState.S_Signin ? ( - - setScreenState(ScreenState.S_SigninOrCreateAccount) - } - /> - ) : undefined} - {screenState === ScreenState.S_CreateAccount ? ( - - setScreenState(ScreenState.S_SigninOrCreateAccount) - } - /> - ) : undefined} - - - - ) -}) - -const styles = StyleSheet.create({ - container: { - height: '100%', - }, - containerBorder: { - borderLeftWidth: 1, - borderRightWidth: 1, - }, - vertCenter: { - justifyContent: 'center', - }, - bgImg: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - }, - hero: {}, - heroText: { - backgroundColor: colors.white, - paddingTop: 10, - paddingBottom: 20, - }, - btns: { - flexDirection: 'row', - paddingTop: 40, - }, - title: { - textAlign: 'center', - color: colors.blue3, - fontSize: 68, - fontWeight: 'bold', - }, - subtitle: { - textAlign: 'center', - color: colors.blue3, - fontSize: 18, - }, - btn: { - flex: 1, - borderRadius: 4, - paddingVertical: 16, - marginBottom: 20, - marginHorizontal: 20, - borderWidth: 1, - borderColor: colors.blue3, - }, - btnLabel: { - textAlign: 'center', - fontSize: 21, - fontWeight: '500', - color: colors.blue3, - }, -}) diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 492177d1..b704f9c4 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -1,11 +1,13 @@ import React, {useEffect} from 'react' import {FlatList, View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' +import {observer} from 'mobx-react-lite' import useAppState from 'react-native-appstate-hook' import { NativeStackScreenProps, NotificationsTabNavigatorParams, } from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {Feed} from '../com/notifications/Feed' import {useStores} from 'state/index' @@ -19,77 +21,79 @@ type Props = NativeStackScreenProps< NotificationsTabNavigatorParams, 'Notifications' > -export const NotificationsScreen = ({}: Props) => { - const store = useStores() - const onMainScroll = useOnMainScroll(store) - const scrollElRef = React.useRef(null) - const {screen} = useAnalytics() - const {appState} = useAppState({ - onForeground: () => doPoll(true), - }) +export const NotificationsScreen = withAuthRequired( + observer(({}: Props) => { + const store = useStores() + const onMainScroll = useOnMainScroll(store) + const scrollElRef = React.useRef(null) + const {screen} = useAnalytics() + const {appState} = useAppState({ + onForeground: () => doPoll(true), + }) - // event handlers - // = - const onPressTryAgain = () => { - store.me.notifications.refresh() - } - const scrollToTop = React.useCallback(() => { - scrollElRef.current?.scrollToOffset({offset: 0}) - }, [scrollElRef]) + // event handlers + // = + const onPressTryAgain = () => { + store.me.notifications.refresh() + } + const scrollToTop = React.useCallback(() => { + scrollElRef.current?.scrollToOffset({offset: 0}) + }, [scrollElRef]) - // periodic polling - // = - const doPoll = React.useCallback( - async (isForegrounding = false) => { - if (isForegrounding) { - // app is foregrounding, refresh optimistically - store.log.debug('NotificationsScreen: Refreshing on app foreground') - await Promise.all([ - store.me.notifications.loadUnreadCount(), - store.me.notifications.refresh(), - ]) - } else if (appState === 'active') { - // periodic poll, refresh if there are new notifs - store.log.debug('NotificationsScreen: Polling for new notifications') - const didChange = await store.me.notifications.loadUnreadCount() - if (didChange) { - store.log.debug('NotificationsScreen: Loading new notifications') - await store.me.notifications.loadLatest() + // periodic polling + // = + const doPoll = React.useCallback( + async (isForegrounding = false) => { + if (isForegrounding) { + // app is foregrounding, refresh optimistically + store.log.debug('NotificationsScreen: Refreshing on app foreground') + await Promise.all([ + store.me.notifications.loadUnreadCount(), + store.me.notifications.refresh(), + ]) + } else if (appState === 'active') { + // periodic poll, refresh if there are new notifs + store.log.debug('NotificationsScreen: Polling for new notifications') + const didChange = await store.me.notifications.loadUnreadCount() + if (didChange) { + store.log.debug('NotificationsScreen: Loading new notifications') + await store.me.notifications.loadLatest() + } } - } - }, - [appState, store], - ) - useEffect(() => { - const pollInterval = setInterval(doPoll, NOTIFICATIONS_POLL_INTERVAL) - return () => clearInterval(pollInterval) - }, [doPoll]) + }, + [appState, store], + ) + useEffect(() => { + const pollInterval = setInterval(doPoll, NOTIFICATIONS_POLL_INTERVAL) + return () => clearInterval(pollInterval) + }, [doPoll]) - // on-visible setup - // = - useFocusEffect( - React.useCallback(() => { - store.log.debug('NotificationsScreen: Updating feed') - const softResetSub = store.onScreenSoftReset(scrollToTop) - store.me.notifications.update() - screen('Notifications') + // on-visible setup + // = + useFocusEffect( + React.useCallback(() => { + store.log.debug('NotificationsScreen: Updating feed') + const softResetSub = store.onScreenSoftReset(scrollToTop) + store.me.notifications.update() + screen('Notifications') - return () => { - softResetSub.remove() - store.me.notifications.markAllRead() - } - }, [store, screen, scrollToTop]), - ) + return () => { + softResetSub.remove() + store.me.notifications.markAllRead() + } + }, [store, screen, scrollToTop]), + ) - return ( - - - - - ) -} + return ( + + + + + ) + }), +) diff --git a/src/view/screens/PostRepostedBy.tsx b/src/view/screens/PostRepostedBy.tsx index 1a63445e..19f0af18 100644 --- a/src/view/screens/PostRepostedBy.tsx +++ b/src/view/screens/PostRepostedBy.tsx @@ -1,6 +1,7 @@ import React from 'react' import {View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {ViewHeader} from '../com/util/ViewHeader' import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy' @@ -8,7 +9,7 @@ import {useStores} from 'state/index' import {makeRecordUri} from 'lib/strings/url-helpers' type Props = NativeStackScreenProps -export const PostRepostedByScreen = ({route}: Props) => { +export const PostRepostedByScreen = withAuthRequired(({route}: Props) => { const store = useStores() const {name, rkey} = route.params const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) @@ -25,4 +26,4 @@ export const PostRepostedByScreen = ({route}: Props) => { ) -} +}) diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx index 0e9feae0..ad54126b 100644 --- a/src/view/screens/PostThread.tsx +++ b/src/view/screens/PostThread.tsx @@ -3,6 +3,7 @@ import {StyleSheet, View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {makeRecordUri} from 'lib/strings/url-helpers' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread' import {ComposePrompt} from 'view/com/composer/Prompt' @@ -16,7 +17,7 @@ import {isDesktopWeb} from 'platform/detection' const SHELL_FOOTER_HEIGHT = 44 type Props = NativeStackScreenProps -export const PostThreadScreen = ({route}: Props) => { +export const PostThreadScreen = withAuthRequired(({route}: Props) => { const store = useStores() const safeAreaInsets = useSafeAreaInsets() const {name, rkey} = route.params @@ -84,7 +85,7 @@ export const PostThreadScreen = ({route}: Props) => { )} ) -} +}) const styles = StyleSheet.create({ prompt: { diff --git a/src/view/screens/PostUpvotedBy.tsx b/src/view/screens/PostUpvotedBy.tsx index b1690721..35b55f3c 100644 --- a/src/view/screens/PostUpvotedBy.tsx +++ b/src/view/screens/PostUpvotedBy.tsx @@ -2,13 +2,14 @@ import React from 'react' import {View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {PostVotedBy as PostLikedByComponent} from '../com/post-thread/PostVotedBy' import {useStores} from 'state/index' import {makeRecordUri} from 'lib/strings/url-helpers' type Props = NativeStackScreenProps -export const PostUpvotedByScreen = ({route}: Props) => { +export const PostUpvotedByScreen = withAuthRequired(({route}: Props) => { const store = useStores() const {name, rkey} = route.params const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) @@ -25,4 +26,4 @@ export const PostUpvotedByScreen = ({route}: Props) => { ) -} +}) diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx index d5476ab5..ec39ac2d 100644 --- a/src/view/screens/PrivacyPolicy.tsx +++ b/src/view/screens/PrivacyPolicy.tsx @@ -26,7 +26,7 @@ export const PrivacyPolicyScreen = (_props: Props) => { - + Privacy Policy diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index e0d0a588..b5073f28 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -3,6 +3,7 @@ import {ActivityIndicator, StyleSheet, View} from 'react-native' import {observer} from 'mobx-react-lite' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewSelector} from '../com/util/ViewSelector' import {CenteredView} from '../com/util/Views' import {ProfileUiModel, Sections} from 'state/models/profile-ui' @@ -25,178 +26,182 @@ const END_ITEM = {_reactKey: '__end__'} const EMPTY_ITEM = {_reactKey: '__empty__'} type Props = NativeStackScreenProps -export const ProfileScreen = observer(({route}: Props) => { - const store = useStores() - const {screen, track} = useAnalytics() +export const ProfileScreen = withAuthRequired( + observer(({route}: Props) => { + const store = useStores() + const {screen, track} = useAnalytics() - useEffect(() => { - screen('Profile') - }, [screen]) + useEffect(() => { + screen('Profile') + }, [screen]) - const onMainScroll = useOnMainScroll(store) - const [hasSetup, setHasSetup] = useState(false) - const uiState = React.useMemo( - () => new ProfileUiModel(store, {user: route.params.name}), - [route.params.name, store], - ) + const onMainScroll = useOnMainScroll(store) + const [hasSetup, setHasSetup] = useState(false) + const uiState = React.useMemo( + () => new ProfileUiModel(store, {user: route.params.name}), + [route.params.name, store], + ) - useFocusEffect( - React.useCallback(() => { - let aborted = false - const feedCleanup = uiState.feed.registerListeners() - if (hasSetup) { - uiState.update() - } else { - uiState.setup().then(() => { - if (aborted) { - return - } - setHasSetup(true) - }) - } - return () => { - aborted = true - feedCleanup() - } - }, [hasSetup, uiState]), - ) + useFocusEffect( + React.useCallback(() => { + let aborted = false + const feedCleanup = uiState.feed.registerListeners() + if (hasSetup) { + uiState.update() + } else { + uiState.setup().then(() => { + if (aborted) { + return + } + setHasSetup(true) + }) + } + return () => { + aborted = true + feedCleanup() + } + }, [hasSetup, uiState]), + ) - // events - // = + // events + // = - const onPressCompose = React.useCallback(() => { - track('ProfileScreen:PressCompose') - store.shell.openComposer({}) - }, [store, track]) - const onSelectView = (index: number) => { - uiState.setSelectedViewIndex(index) - } - const onRefresh = () => { - uiState - .refresh() - .catch((err: any) => - store.log.error('Failed to refresh user profile', err), - ) - } - const onEndReached = () => { - uiState - .loadMore() - .catch((err: any) => - store.log.error('Failed to load more entries in user profile', err), - ) - } - const onPressTryAgain = () => { - uiState.setup() - } - - // rendering - // = - - const renderHeader = () => { - if (!uiState) { - return + const onPressCompose = React.useCallback(() => { + track('ProfileScreen:PressCompose') + store.shell.openComposer({}) + }, [store, track]) + const onSelectView = (index: number) => { + uiState.setSelectedViewIndex(index) } - return - } - let renderItem - let Footer - let items: any[] = [] - if (uiState) { - if (uiState.isInitialLoading) { - items = items.concat([LOADING_ITEM]) - renderItem = () => - } else if (uiState.currentView.hasError) { - items = items.concat([ - { - _reactKey: '__error__', - error: uiState.currentView.error, - }, - ]) - renderItem = (item: any) => ( - - { + uiState + .refresh() + .catch((err: any) => + store.log.error('Failed to refresh user profile', err), + ) + } + const onEndReached = () => { + uiState + .loadMore() + .catch((err: any) => + store.log.error('Failed to load more entries in user profile', err), + ) + } + const onPressTryAgain = () => { + uiState.setup() + } + + // rendering + // = + + const renderHeader = () => { + if (!uiState) { + return + } + return + } + let renderItem + let Footer + let items: any[] = [] + if (uiState) { + if (uiState.isInitialLoading) { + items = items.concat([LOADING_ITEM]) + renderItem = () => + } else if (uiState.currentView.hasError) { + items = items.concat([ + { + _reactKey: '__error__', + error: uiState.currentView.error, + }, + ]) + renderItem = (item: any) => ( + + + + ) + } else { + if ( + uiState.selectedView === Sections.Posts || + uiState.selectedView === Sections.PostsWithReplies + ) { + if (uiState.feed.hasContent) { + if (uiState.selectedView === Sections.Posts) { + items = uiState.feed.nonReplyFeed + } else { + items = uiState.feed.feed.slice() + } + if (!uiState.feed.hasMore) { + items = items.concat([END_ITEM]) + } else if (uiState.feed.isLoading) { + Footer = LoadingMoreFooter + } + renderItem = (item: any) => { + if (item === END_ITEM) { + return - end of feed - + } + return ( + + ) + } + } else if (uiState.feed.isEmpty) { + items = items.concat([EMPTY_ITEM]) + renderItem = () => ( + + ) + } + } else { + items = items.concat([EMPTY_ITEM]) + renderItem = () => TODO + } + } + } + if (!renderItem) { + renderItem = () => + } + + return ( + + {uiState.profile.hasError ? ( + - - ) - } else { - if ( - uiState.selectedView === Sections.Posts || - uiState.selectedView === Sections.PostsWithReplies - ) { - if (uiState.feed.hasContent) { - if (uiState.selectedView === Sections.Posts) { - items = uiState.feed.nonReplyFeed - } else { - items = uiState.feed.feed.slice() - } - if (!uiState.feed.hasMore) { - items = items.concat([END_ITEM]) - } else if (uiState.feed.isLoading) { - Footer = LoadingMoreFooter - } - renderItem = (item: any) => { - if (item === END_ITEM) { - return - end of feed - - } - return - } - } else if (uiState.feed.isEmpty) { - items = items.concat([EMPTY_ITEM]) - renderItem = () => ( - - ) - } - } else { - items = items.concat([EMPTY_ITEM]) - renderItem = () => TODO - } - } - } - if (!renderItem) { - renderItem = () => - } - - return ( - - {uiState.profile.hasError ? ( - + ) : ( + {renderHeader()} + )} + } /> - ) : uiState.profile.hasLoaded ? ( - - ) : ( - {renderHeader()} - )} - } - /> - - ) -}) + + ) + }), +) function LoadingMoreFooter() { return ( diff --git a/src/view/screens/ProfileFollowers.tsx b/src/view/screens/ProfileFollowers.tsx index b248cdc3..e2f95fbe 100644 --- a/src/view/screens/ProfileFollowers.tsx +++ b/src/view/screens/ProfileFollowers.tsx @@ -2,12 +2,13 @@ import React from 'react' import {View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/ProfileFollowers' import {useStores} from 'state/index' type Props = NativeStackScreenProps -export const ProfileFollowersScreen = ({route}: Props) => { +export const ProfileFollowersScreen = withAuthRequired(({route}: Props) => { const store = useStores() const {name} = route.params @@ -23,4 +24,4 @@ export const ProfileFollowersScreen = ({route}: Props) => { ) -} +}) diff --git a/src/view/screens/ProfileFollows.tsx b/src/view/screens/ProfileFollows.tsx index 7edf8edb..f70944f5 100644 --- a/src/view/screens/ProfileFollows.tsx +++ b/src/view/screens/ProfileFollows.tsx @@ -2,12 +2,13 @@ import React from 'react' import {View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows' import {useStores} from 'state/index' type Props = NativeStackScreenProps -export const ProfileFollowsScreen = ({route}: Props) => { +export const ProfileFollowsScreen = withAuthRequired(({route}: Props) => { const store = useStores() const {name} = route.params @@ -23,4 +24,4 @@ export const ProfileFollowsScreen = ({route}: Props) => { ) -} +}) diff --git a/src/view/screens/Search.tsx b/src/view/screens/Search.tsx index a50d5c6a..19535a16 100644 --- a/src/view/screens/Search.tsx +++ b/src/view/screens/Search.tsx @@ -12,6 +12,7 @@ import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ScrollView} from '../com/util/Views' import { NativeStackScreenProps, @@ -36,159 +37,161 @@ const MENU_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10} const FIVE_MIN = 5 * 60 * 1e3 type Props = NativeStackScreenProps -export const SearchScreen = observer(({}: Props) => { - const pal = usePalette('default') - const theme = useTheme() - const store = useStores() - const {track} = useAnalytics() - const scrollElRef = React.useRef(null) - const onMainScroll = useOnMainScroll(store) - const textInput = React.useRef(null) - const [lastRenderTime, setRenderTime] = React.useState(Date.now()) // used to trigger reloads - const [isInputFocused, setIsInputFocused] = React.useState(false) - const [query, setQuery] = React.useState('') - const autocompleteView = React.useMemo( - () => new UserAutocompleteViewModel(store), - [store], - ) +export const SearchScreen = withAuthRequired( + observer(({}: Props) => { + const pal = usePalette('default') + const theme = useTheme() + const store = useStores() + const {track} = useAnalytics() + const scrollElRef = React.useRef(null) + const onMainScroll = useOnMainScroll(store) + const textInput = React.useRef(null) + const [lastRenderTime, setRenderTime] = React.useState(Date.now()) // used to trigger reloads + const [isInputFocused, setIsInputFocused] = React.useState(false) + const [query, setQuery] = React.useState('') + const autocompleteView = React.useMemo( + () => new UserAutocompleteViewModel(store), + [store], + ) - const onSoftReset = () => { - scrollElRef.current?.scrollTo({x: 0, y: 0}) - } - - useFocusEffect( - React.useCallback(() => { - const softResetSub = store.onScreenSoftReset(onSoftReset) - const cleanup = () => { - softResetSub.remove() - } - - const now = Date.now() - if (now - lastRenderTime > FIVE_MIN) { - setRenderTime(Date.now()) // trigger reload of suggestions - } - store.shell.setMinimalShellMode(false) - autocompleteView.setup() - - return cleanup - }, [store, autocompleteView, lastRenderTime, setRenderTime]), - ) - - const onPressMenu = () => { - track('ViewHeader:MenuButtonClicked') - store.shell.openDrawer() - } - - const onChangeQuery = (text: string) => { - setQuery(text) - if (text.length > 0) { - autocompleteView.setActive(true) - autocompleteView.setPrefix(text) - } else { - autocompleteView.setActive(false) + const onSoftReset = () => { + scrollElRef.current?.scrollTo({x: 0, y: 0}) } - } - const onPressClearQuery = () => { - setQuery('') - } - const onPressCancelSearch = () => { - setQuery('') - autocompleteView.setActive(false) - textInput.current?.blur() - } - return ( - - - - - - - - - setIsInputFocused(true)} - onBlur={() => setIsInputFocused(false)} - onChangeText={onChangeQuery} - /> - {query ? ( - - - + useFocusEffect( + React.useCallback(() => { + const softResetSub = store.onScreenSoftReset(onSoftReset) + const cleanup = () => { + softResetSub.remove() + } + + const now = Date.now() + if (now - lastRenderTime > FIVE_MIN) { + setRenderTime(Date.now()) // trigger reload of suggestions + } + store.shell.setMinimalShellMode(false) + autocompleteView.setup() + + return cleanup + }, [store, autocompleteView, lastRenderTime, setRenderTime]), + ) + + const onPressMenu = () => { + track('ViewHeader:MenuButtonClicked') + store.shell.openDrawer() + } + + const onChangeQuery = (text: string) => { + setQuery(text) + if (text.length > 0) { + autocompleteView.setActive(true) + autocompleteView.setPrefix(text) + } else { + autocompleteView.setActive(false) + } + } + const onPressClearQuery = () => { + setQuery('') + } + const onPressCancelSearch = () => { + setQuery('') + autocompleteView.setActive(false) + textInput.current?.blur() + } + + return ( + + + + + + + + + setIsInputFocused(true)} + onBlur={() => setIsInputFocused(false)} + onChangeText={onChangeQuery} + /> + {query ? ( + + + + ) : undefined} + + {query || isInputFocused ? ( + + + Cancel + + ) : undefined} - {query || isInputFocused ? ( - - - Cancel - + {query && autocompleteView.searchRes.length ? ( + <> + {autocompleteView.searchRes.map(item => ( + + ))} + + ) : query && !autocompleteView.searchRes.length ? ( + + + No results found for {autocompleteView.prefix} + - ) : undefined} - - {query && autocompleteView.searchRes.length ? ( - <> - {autocompleteView.searchRes.map(item => ( - - ))} - - ) : query && !autocompleteView.searchRes.length ? ( - - - No results found for {autocompleteView.prefix} - - - ) : isInputFocused ? ( - - - Search for users on the network - - - ) : ( - - - - - - )} - - - - ) -}) + ) : isInputFocused ? ( + + + Search for users on the network + + + ) : ( + + + + + + )} + + + + ) + }), +) const styles = StyleSheet.create({ container: { diff --git a/src/view/screens/Search.web.tsx b/src/view/screens/Search.web.tsx index 75b5f01c..29b88449 100644 --- a/src/view/screens/Search.web.tsx +++ b/src/view/screens/Search.web.tsx @@ -1,6 +1,7 @@ import React from 'react' import {StyleSheet, View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ScrollView} from '../com/util/Views' import {observer} from 'mobx-react-lite' import { @@ -17,46 +18,48 @@ import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' const FIVE_MIN = 5 * 60 * 1e3 type Props = NativeStackScreenProps -export const SearchScreen = observer(({}: Props) => { - const pal = usePalette('default') - const store = useStores() - const scrollElRef = React.useRef(null) - const onMainScroll = useOnMainScroll(store) - const [lastRenderTime, setRenderTime] = React.useState(Date.now()) // used to trigger reloads +export const SearchScreen = withAuthRequired( + observer(({}: Props) => { + const pal = usePalette('default') + const store = useStores() + const scrollElRef = React.useRef(null) + const onMainScroll = useOnMainScroll(store) + const [lastRenderTime, setRenderTime] = React.useState(Date.now()) // used to trigger reloads - const onSoftReset = () => { - scrollElRef.current?.scrollTo({x: 0, y: 0}) - } + const onSoftReset = () => { + scrollElRef.current?.scrollTo({x: 0, y: 0}) + } - useFocusEffect( - React.useCallback(() => { - const softResetSub = store.onScreenSoftReset(onSoftReset) + useFocusEffect( + React.useCallback(() => { + const softResetSub = store.onScreenSoftReset(onSoftReset) - const now = Date.now() - if (now - lastRenderTime > FIVE_MIN) { - setRenderTime(Date.now()) // trigger reload of suggestions - } - store.shell.setMinimalShellMode(false) + const now = Date.now() + if (now - lastRenderTime > FIVE_MIN) { + setRenderTime(Date.now()) // trigger reload of suggestions + } + store.shell.setMinimalShellMode(false) - return () => { - softResetSub.remove() - } - }, [store, lastRenderTime, setRenderTime]), - ) + return () => { + softResetSub.remove() + } + }, [store, lastRenderTime, setRenderTime]), + ) - return ( - - - - - - ) -}) + return ( + + + + + + ) + }), +) const styles = StyleSheet.create({ container: { diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index 2e5d2c00..a79a357b 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -16,6 +16,7 @@ import { } from '@fortawesome/react-native-fontawesome' import {observer} from 'mobx-react-lite' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' import * as AppInfo from 'lib/app-info' import {useStores} from 'state/index' import {s, colors} from 'lib/styles' @@ -33,235 +34,237 @@ import {useAnalytics} from 'lib/analytics' import {NavigationProp} from 'lib/routes/types' type Props = NativeStackScreenProps -export const SettingsScreen = observer(function Settings({}: Props) { - const theme = useTheme() - const pal = usePalette('default') - const store = useStores() - const navigation = useNavigation() - const {screen, track} = useAnalytics() - const [isSwitching, setIsSwitching] = React.useState(false) +export const SettingsScreen = withAuthRequired( + observer(function Settings({}: Props) { + const theme = useTheme() + const pal = usePalette('default') + const store = useStores() + const navigation = useNavigation() + const {screen, track} = useAnalytics() + const [isSwitching, setIsSwitching] = React.useState(false) - useFocusEffect( - React.useCallback(() => { - screen('Settings') - store.shell.setMinimalShellMode(false) - }, [screen, store]), - ) + useFocusEffect( + React.useCallback(() => { + screen('Settings') + store.shell.setMinimalShellMode(false) + }, [screen, store]), + ) - const onPressSwitchAccount = async (acct: AccountData) => { - track('Settings:SwitchAccountButtonClicked') - setIsSwitching(true) - if (await store.session.resumeSession(acct)) { + const onPressSwitchAccount = async (acct: AccountData) => { + track('Settings:SwitchAccountButtonClicked') + setIsSwitching(true) + if (await store.session.resumeSession(acct)) { + setIsSwitching(false) + navigation.navigate('HomeTab') + navigation.dispatch(StackActions.popToTop()) + Toast.show(`Signed in as ${acct.displayName || acct.handle}`) + return + } setIsSwitching(false) + Toast.show('Sorry! We need you to enter your password.') navigation.navigate('HomeTab') navigation.dispatch(StackActions.popToTop()) - Toast.show(`Signed in as ${acct.displayName || acct.handle}`) - return + store.session.clear() + } + const onPressAddAccount = () => { + track('Settings:AddAccountButtonClicked') + store.session.clear() + } + const onPressChangeHandle = () => { + track('Settings:ChangeHandleButtonClicked') + store.shell.openModal({ + name: 'change-handle', + onChanged() { + setIsSwitching(true) + store.session.reloadFromServer().then( + () => { + setIsSwitching(false) + Toast.show('Your handle has been updated') + }, + err => { + store.log.error( + 'Failed to reload from server after handle update', + {err}, + ) + setIsSwitching(false) + }, + ) + }, + }) + } + const onPressSignout = () => { + track('Settings:SignOutButtonClicked') + store.session.logout() + } + const onPressDeleteAccount = () => { + store.shell.openModal({name: 'delete-account'}) } - setIsSwitching(false) - Toast.show('Sorry! We need you to enter your password.') - navigation.navigate('HomeTab') - navigation.dispatch(StackActions.popToTop()) - store.session.clear() - } - const onPressAddAccount = () => { - track('Settings:AddAccountButtonClicked') - store.session.clear() - } - const onPressChangeHandle = () => { - track('Settings:ChangeHandleButtonClicked') - store.shell.openModal({ - name: 'change-handle', - onChanged() { - setIsSwitching(true) - store.session.reloadFromServer().then( - () => { - setIsSwitching(false) - Toast.show('Your handle has been updated') - }, - err => { - store.log.error( - 'Failed to reload from server after handle update', - {err}, - ) - setIsSwitching(false) - }, - ) - }, - }) - } - const onPressSignout = () => { - track('Settings:SignOutButtonClicked') - store.session.logout() - } - const onPressDeleteAccount = () => { - store.shell.openModal({name: 'delete-account'}) - } - return ( - - - - - - - Signed in as - - - - {isSwitching ? ( - - + return ( + + + + + + + Signed in as + + - ) : ( - + {isSwitching ? ( + + + ) : ( + + + + + + + + {store.me.displayName || store.me.handle} + + + {store.me.handle} + + + + + Sign out + + + + + )} + {store.session.switchableAccounts.map(account => ( + onPressSwitchAccount(account) + }> - + - - {store.me.displayName || store.me.handle} + + {account.displayName || account.handle} - - {store.me.handle} + + {account.handle} - - - Sign out - - - - - )} - {store.session.switchableAccounts.map(account => ( + + + ))} onPressSwitchAccount(account) - }> - - + testID="switchToNewAccountBtn" + style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} + onPress={isSwitching ? undefined : onPressAddAccount}> + + - - - {account.displayName || account.handle} - - - {account.handle} - - - + + Add account + - ))} - - - - - - Add account + + + + + Advanced - + + + + + + Change my handle + + - + - - Advanced - - - - - - - Change my handle + + Danger zone - - - - - - Danger zone - - - - + + + + - - - Delete my account - - + }> + Delete my account + + - + - - Developer tools - - - - System log + + Developer tools - - - - Storybook + + + System log + + + + + Storybook + + + + Build version {AppInfo.appVersion} ({AppInfo.buildVersion}) - - - Build version {AppInfo.appVersion} ({AppInfo.buildVersion}) - - - - - ) -}) + + + + ) + }), +) function AccountDropdownBtn({handle}: {handle: string}) { const store = useStores() diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 46c77178..65757b07 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -131,7 +131,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { return ( - + {store.session.hasSession && } - } - iconFilled={ - - } - label="Profile" - /> + {store.session.hasSession && ( + } + iconFilled={ + + } + label="Profile" + /> + )} } @@ -180,7 +182,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { } label="Settings" /> - + {store.session.hasSession && } ) }) diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx index 58fb3139..3f196cb7 100644 --- a/src/view/shell/desktop/RightNav.tsx +++ b/src/view/shell/desktop/RightNav.tsx @@ -7,12 +7,14 @@ import {Text} from 'view/com/util/text/Text' import {TextLink} from 'view/com/util/Link' import {FEEDBACK_FORM_URL} from 'lib/constants' import {s} from 'lib/styles' +import {useStores} from 'state/index' export const DesktopRightNav = observer(function DesktopRightNav() { + const store = useStores() const pal = usePalette('default') return ( - + {store.session.hasSession && } Welcome to Bluesky! This is a beta application that's still in diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 116915ff..15f9ef58 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -5,7 +5,6 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context' import {Drawer} from 'react-native-drawer-layout' import {useNavigationState} from '@react-navigation/native' import {useStores} from 'state/index' -import {Login} from 'view/screens/Login' import {ModalsContainer} from 'view/com/modals/Modal' import {Lightbox} from 'view/com/lightbox/Lightbox' import {Text} from 'view/com/util/text/Text' @@ -104,20 +103,6 @@ export const Shell: React.FC = observer(() => { ) } - if (!store.session.hasSession) { - return ( - - - - - - ) - } - return ( { export const Shell: React.FC = observer(() => { const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark) - const store = useStores() if (isMobileWeb) { return } - - if (!store.session.hasSession) { - return ( - - - - - ) - } - return (