Rework logged out state to preserve routing and work for web

zio/stable
Paul Frazee 2023-03-13 23:30:12 -05:00
parent b5c64a03b6
commit 774fb83719
26 changed files with 1063 additions and 1078 deletions

View File

@ -122,7 +122,9 @@ export class SessionModel {
try { try {
return await this.resumeSession(sess) return await this.resumeSession(sess)
} finally { } finally {
runInAction(() => {
this.isResumingSession = false this.isResumingSession = false
})
} }
} else { } else {
this.rootStore.log.debug( this.rootStore.log.debug(

View File

@ -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>(
ScreenState.S_SigninOrCreateAccount,
)
React.useEffect(() => {
screen('Login')
store.shell.setMinimalShellMode(true)
}, [store, screen])
if (
store.session.isResumingSession ||
screenState === ScreenState.S_SigninOrCreateAccount
) {
return (
<SplashScreen
onPressSignin={() => setScreenState(ScreenState.S_Signin)}
onPressCreateAccount={() => setScreenState(ScreenState.S_CreateAccount)}
/>
)
}
return (
<CenteredView style={[s.hContentRegion, pal.view]}>
<SafeAreaView testID="noSessionView" style={s.hContentRegion}>
<ErrorBoundary>
{screenState === ScreenState.S_Signin ? (
<Signin
onPressBack={() =>
setScreenState(ScreenState.S_SigninOrCreateAccount)
}
/>
) : undefined}
{screenState === ScreenState.S_CreateAccount ? (
<CreateAccount
onPressBack={() =>
setScreenState(ScreenState.S_SigninOrCreateAccount)
}
/>
) : undefined}
</ErrorBoundary>
</SafeAreaView>
</CenteredView>
)
})

View File

@ -1,28 +1,19 @@
import React from 'react' import React from 'react'
import {StyleSheet} from 'react-native' import {StyleSheet, View} from 'react-native'
import LinearGradient from 'react-native-linear-gradient' import {s, colors} from 'lib/styles'
import {s, gradients} from 'lib/styles'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
export const LogoTextHero = () => { export const LogoTextHero = () => {
return ( return (
<LinearGradient <View style={[styles.textHero]}>
colors={[gradients.blue.start, gradients.blue.end]}
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}
style={[styles.textHero]}>
<Text type="title-lg" style={[s.white, s.bold]}> <Text type="title-lg" style={[s.white, s.bold]}>
Bluesky Bluesky
</Text> </Text>
</LinearGradient> </View>
) )
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
logo: {
flexDirection: 'row',
justifyContent: 'center',
},
textHero: { textHero: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
@ -30,5 +21,6 @@ const styles = StyleSheet.create({
paddingRight: 20, paddingRight: 20,
paddingVertical: 15, paddingVertical: 15,
marginBottom: 20, marginBottom: 20,
backgroundColor: colors.blue3,
}, },
}) })

View File

@ -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 (
<CenteredView style={styles.container}>
<Image source={CLOUD_SPLASH as ImageSource} style={styles.bgImg} />
<SafeAreaView testID="noSessionView" style={styles.container}>
<ErrorBoundary>
<View style={styles.hero}>
<View style={styles.heroText}>
<Text style={styles.title}>Bluesky</Text>
</View>
</View>
<View testID="signinOrCreateAccount" style={styles.btns}>
<TouchableOpacity
testID="createAccountButton"
style={[pal.view, styles.btn]}
onPress={onPressCreateAccount}>
<Text style={[pal.link, styles.btnLabel]}>
Create a new account
</Text>
</TouchableOpacity>
<TouchableOpacity
testID="signInButton"
style={[pal.view, styles.btn]}
onPress={onPressSignin}>
<Text style={[pal.link, styles.btnLabel]}>Sign in</Text>
</TouchableOpacity>
</View>
</ErrorBoundary>
</SafeAreaView>
</CenteredView>
)
}
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,
},
})

View File

@ -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 (
<CenteredView style={styles.container}>
<View testID="noSessionView" style={styles.containerInner}>
<ErrorBoundary>
<Text style={styles.title}>Bluesky</Text>
<Text style={styles.subtitle}>See what's next</Text>
<View testID="signinOrCreateAccount" style={styles.btns}>
<TouchableOpacity
testID="createAccountButton"
style={[styles.btn, {backgroundColor: colors.blue3}]}
onPress={onPressCreateAccount}>
<Text style={[s.white, styles.btnLabel]}>
Create a new account
</Text>
</TouchableOpacity>
<TouchableOpacity
testID="signInButton"
style={[styles.btn, pal.btn]}
onPress={onPressSignin}>
<Text style={[pal.link, styles.btnLabel]}>Sign in</Text>
</TouchableOpacity>
</View>
<Text
type="xl"
style={[styles.notice, pal.textLight]}
lineHeight={1.3}>
Bluesky will launch soon.{' '}
<TextLink
type="xl"
text="Join the waitlist"
href="#"
style={pal.link}
/>{' '}
to try the beta before it's publicly available.
</Text>
</ErrorBoundary>
</View>
</CenteredView>
)
}
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',
},
})

View File

@ -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 = <P extends object>(
Component: React.ComponentType<P>,
): React.FC<P> =>
observer((props: P) => {
const store = useStores()
if (store.session.isResumingSession) {
return <Loading />
}
if (!store.session.hasSession) {
return <LoggedOut />
}
return <Component {...props} />
})
function Loading() {
const pal = usePalette('default')
return (
<View style={[styles.loading, pal.view]}>
<ActivityIndicator size="large" />
<Text type="2xl" style={[styles.loadingText, pal.textLight]}>
Firing up the grill...
</Text>
</View>
)
}
const styles = StyleSheet.create({
loading: {
height: '100%',
alignContent: 'center',
justifyContent: 'center',
paddingBottom: 100,
},
loadingText: {
paddingVertical: 20,
paddingHorizontal: 20,
textAlign: 'center',
},
})

View File

@ -4,6 +4,7 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import useAppState from 'react-native-appstate-hook' import useAppState from 'react-native-appstate-hook'
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {Feed} from '../com/posts/Feed' import {Feed} from '../com/posts/Feed'
import {LoadLatestBtn} from '../com/util/LoadLatestBtn' import {LoadLatestBtn} from '../com/util/LoadLatestBtn'
@ -18,7 +19,8 @@ import {ComposeIcon2} from 'lib/icons'
const HEADER_HEIGHT = 42 const HEADER_HEIGHT = 42
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'> type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export const HomeScreen = observer(function Home(_opts: Props) { export const HomeScreen = withAuthRequired(
observer(function Home(_opts: Props) {
const store = useStores() const store = useStores()
const onMainScroll = useOnMainScroll(store) const onMainScroll = useOnMainScroll(store)
const {screen, track} = useAnalytics() const {screen, track} = useAnalytics()
@ -109,4 +111,5 @@ export const HomeScreen = observer(function Home(_opts: Props) {
/> />
</View> </View>
) )
}) }),
)

View File

@ -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 (
<>
<View style={styles.hero}>
<View style={styles.heroText}>
<Text style={styles.title}>Bluesky</Text>
</View>
</View>
<View testID="signinOrCreateAccount" style={styles.btns}>
<TouchableOpacity
testID="createAccountButton"
style={[pal.view, styles.btn]}
onPress={onPressCreateAccount}>
<Text style={[pal.link, styles.btnLabel]}>Create a new account</Text>
</TouchableOpacity>
<TouchableOpacity
testID="signInButton"
style={[pal.view, styles.btn]}
onPress={onPressSignin}>
<Text style={[pal.link, styles.btnLabel]}>Sign in</Text>
</TouchableOpacity>
</View>
</>
)
}
export const Login = observer(() => {
const pal = usePalette('default')
const store = useStores()
const [screenState, setScreenState] = useState<ScreenState>(
ScreenState.S_SigninOrCreateAccount,
)
if (
store.session.isResumingSession ||
screenState === ScreenState.S_SigninOrCreateAccount
) {
return (
<View style={styles.container}>
<Image source={CLOUD_SPLASH as ImageSource} style={styles.bgImg} />
<SafeAreaView testID="noSessionView" style={styles.container}>
<ErrorBoundary>
{!store.session.isResumingSession && (
<SigninOrCreateAccount
onPressSignin={() => setScreenState(ScreenState.S_Signin)}
onPressCreateAccount={() =>
setScreenState(ScreenState.S_CreateAccount)
}
/>
)}
</ErrorBoundary>
</SafeAreaView>
</View>
)
}
return (
<View style={[styles.container, pal.view]}>
<SafeAreaView testID="noSessionView" style={styles.container}>
<ErrorBoundary>
{screenState === ScreenState.S_Signin ? (
<Signin
onPressBack={() =>
setScreenState(ScreenState.S_SigninOrCreateAccount)
}
/>
) : undefined}
{screenState === ScreenState.S_CreateAccount ? (
<CreateAccount
onPressBack={() =>
setScreenState(ScreenState.S_SigninOrCreateAccount)
}
/>
) : undefined}
</ErrorBoundary>
</SafeAreaView>
</View>
)
})
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,
},
})

View File

@ -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 (
<>
<View style={styles.hero}>
<View style={styles.heroText}>
<Text style={styles.title}>Bluesky</Text>
</View>
</View>
<View testID="signinOrCreateAccount" style={styles.btns}>
<TouchableOpacity
testID="createAccountButton"
style={[pal.view, styles.btn]}
onPress={onPressCreateAccount}>
<Text style={[pal.link, styles.btnLabel]}>New account</Text>
</TouchableOpacity>
<TouchableOpacity
testID="signInButton"
style={[pal.view, styles.btn]}
onPress={onPressSignin}>
<Text style={[pal.link, styles.btnLabel]}>Sign in</Text>
</TouchableOpacity>
</View>
</>
)
}
export const Login = observer(() => {
const pal = usePalette('default')
const [screenState, setScreenState] = useState<ScreenState>(
ScreenState.S_SigninOrCreateAccount,
)
if (screenState === ScreenState.S_SigninOrCreateAccount) {
return (
<CenteredView style={[styles.container, styles.vertCenter]}>
<ErrorBoundary>
<SigninOrCreateAccount
onPressSignin={() => setScreenState(ScreenState.S_Signin)}
onPressCreateAccount={() =>
setScreenState(ScreenState.S_CreateAccount)
}
/>
</ErrorBoundary>
</CenteredView>
)
}
return (
<CenteredView
style={[
styles.container,
styles.containerBorder,
pal.view,
pal.borderDark,
]}>
<SafeAreaView testID="noSessionView" style={styles.container}>
<ErrorBoundary>
{screenState === ScreenState.S_Signin ? (
<Signin
onPressBack={() =>
setScreenState(ScreenState.S_SigninOrCreateAccount)
}
/>
) : undefined}
{screenState === ScreenState.S_CreateAccount ? (
<CreateAccount
onPressBack={() =>
setScreenState(ScreenState.S_SigninOrCreateAccount)
}
/>
) : undefined}
</ErrorBoundary>
</SafeAreaView>
</CenteredView>
)
})
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,
},
})

View File

@ -1,11 +1,13 @@
import React, {useEffect} from 'react' import React, {useEffect} from 'react'
import {FlatList, View} from 'react-native' import {FlatList, View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {observer} from 'mobx-react-lite'
import useAppState from 'react-native-appstate-hook' import useAppState from 'react-native-appstate-hook'
import { import {
NativeStackScreenProps, NativeStackScreenProps,
NotificationsTabNavigatorParams, NotificationsTabNavigatorParams,
} from 'lib/routes/types' } from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {Feed} from '../com/notifications/Feed' import {Feed} from '../com/notifications/Feed'
import {useStores} from 'state/index' import {useStores} from 'state/index'
@ -19,7 +21,8 @@ type Props = NativeStackScreenProps<
NotificationsTabNavigatorParams, NotificationsTabNavigatorParams,
'Notifications' 'Notifications'
> >
export const NotificationsScreen = ({}: Props) => { export const NotificationsScreen = withAuthRequired(
observer(({}: Props) => {
const store = useStores() const store = useStores()
const onMainScroll = useOnMainScroll(store) const onMainScroll = useOnMainScroll(store)
const scrollElRef = React.useRef<FlatList>(null) const scrollElRef = React.useRef<FlatList>(null)
@ -92,4 +95,5 @@ export const NotificationsScreen = ({}: Props) => {
/> />
</View> </View>
) )
} }),
)

View File

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy' 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' import {makeRecordUri} from 'lib/strings/url-helpers'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
export const PostRepostedByScreen = ({route}: Props) => { export const PostRepostedByScreen = withAuthRequired(({route}: Props) => {
const store = useStores() const store = useStores()
const {name, rkey} = route.params const {name, rkey} = route.params
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
@ -25,4 +26,4 @@ export const PostRepostedByScreen = ({route}: Props) => {
<PostRepostedByComponent uri={uri} /> <PostRepostedByComponent uri={uri} />
</View> </View>
) )
} })

View File

@ -3,6 +3,7 @@ import {StyleSheet, View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {makeRecordUri} from 'lib/strings/url-helpers' import {makeRecordUri} from 'lib/strings/url-helpers'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread' import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
import {ComposePrompt} from 'view/com/composer/Prompt' import {ComposePrompt} from 'view/com/composer/Prompt'
@ -16,7 +17,7 @@ import {isDesktopWeb} from 'platform/detection'
const SHELL_FOOTER_HEIGHT = 44 const SHELL_FOOTER_HEIGHT = 44
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
export const PostThreadScreen = ({route}: Props) => { export const PostThreadScreen = withAuthRequired(({route}: Props) => {
const store = useStores() const store = useStores()
const safeAreaInsets = useSafeAreaInsets() const safeAreaInsets = useSafeAreaInsets()
const {name, rkey} = route.params const {name, rkey} = route.params
@ -84,7 +85,7 @@ export const PostThreadScreen = ({route}: Props) => {
)} )}
</View> </View>
) )
} })
const styles = StyleSheet.create({ const styles = StyleSheet.create({
prompt: { prompt: {

View File

@ -2,13 +2,14 @@ import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {PostVotedBy as PostLikedByComponent} from '../com/post-thread/PostVotedBy' import {PostVotedBy as PostLikedByComponent} from '../com/post-thread/PostVotedBy'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {makeRecordUri} from 'lib/strings/url-helpers' import {makeRecordUri} from 'lib/strings/url-helpers'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostUpvotedBy'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostUpvotedBy'>
export const PostUpvotedByScreen = ({route}: Props) => { export const PostUpvotedByScreen = withAuthRequired(({route}: Props) => {
const store = useStores() const store = useStores()
const {name, rkey} = route.params const {name, rkey} = route.params
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
@ -25,4 +26,4 @@ export const PostUpvotedByScreen = ({route}: Props) => {
<PostLikedByComponent uri={uri} direction="up" /> <PostLikedByComponent uri={uri} direction="up" />
</View> </View>
) )
} })

View File

@ -26,7 +26,7 @@ export const PrivacyPolicyScreen = (_props: Props) => {
<ViewHeader title="Privacy Policy" /> <ViewHeader title="Privacy Policy" />
<ScrollView style={[s.hContentRegion, pal.view]}> <ScrollView style={[s.hContentRegion, pal.view]}>
<View style={[s.p20]}> <View style={[s.p20]}>
<Text type="title-xl" style={[pal.text, s.pb20]}> <Text type="title-xl" style={[pal.text, s.bold, s.pb20]}>
Privacy Policy Privacy Policy
</Text> </Text>
<PrivacyPolicyHtml /> <PrivacyPolicyHtml />

View File

@ -3,6 +3,7 @@ import {ActivityIndicator, StyleSheet, View} from 'react-native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewSelector} from '../com/util/ViewSelector' import {ViewSelector} from '../com/util/ViewSelector'
import {CenteredView} from '../com/util/Views' import {CenteredView} from '../com/util/Views'
import {ProfileUiModel, Sections} from 'state/models/profile-ui' import {ProfileUiModel, Sections} from 'state/models/profile-ui'
@ -25,7 +26,8 @@ const END_ITEM = {_reactKey: '__end__'}
const EMPTY_ITEM = {_reactKey: '__empty__'} const EMPTY_ITEM = {_reactKey: '__empty__'}
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Profile'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'Profile'>
export const ProfileScreen = observer(({route}: Props) => { export const ProfileScreen = withAuthRequired(
observer(({route}: Props) => {
const store = useStores() const store = useStores()
const {screen, track} = useAnalytics() const {screen, track} = useAnalytics()
@ -140,7 +142,9 @@ export const ProfileScreen = observer(({route}: Props) => {
if (item === END_ITEM) { if (item === END_ITEM) {
return <Text style={styles.endItem}>- end of feed -</Text> return <Text style={styles.endItem}>- end of feed -</Text>
} }
return <FeedItem item={item} ignoreMuteFor={uiState.profile.did} /> return (
<FeedItem item={item} ignoreMuteFor={uiState.profile.did} />
)
} }
} else if (uiState.feed.isEmpty) { } else if (uiState.feed.isEmpty) {
items = items.concat([EMPTY_ITEM]) items = items.concat([EMPTY_ITEM])
@ -196,7 +200,8 @@ export const ProfileScreen = observer(({route}: Props) => {
/> />
</View> </View>
) )
}) }),
)
function LoadingMoreFooter() { function LoadingMoreFooter() {
return ( return (

View File

@ -2,12 +2,13 @@ import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/ProfileFollowers' import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/ProfileFollowers'
import {useStores} from 'state/index' import {useStores} from 'state/index'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollowers'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollowers'>
export const ProfileFollowersScreen = ({route}: Props) => { export const ProfileFollowersScreen = withAuthRequired(({route}: Props) => {
const store = useStores() const store = useStores()
const {name} = route.params const {name} = route.params
@ -23,4 +24,4 @@ export const ProfileFollowersScreen = ({route}: Props) => {
<ProfileFollowersComponent name={name} /> <ProfileFollowersComponent name={name} />
</View> </View>
) )
} })

View File

@ -2,12 +2,13 @@ import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader' import {ViewHeader} from '../com/util/ViewHeader'
import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows' import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows'
import {useStores} from 'state/index' import {useStores} from 'state/index'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollows'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollows'>
export const ProfileFollowsScreen = ({route}: Props) => { export const ProfileFollowsScreen = withAuthRequired(({route}: Props) => {
const store = useStores() const store = useStores()
const {name} = route.params const {name} = route.params
@ -23,4 +24,4 @@ export const ProfileFollowsScreen = ({route}: Props) => {
<ProfileFollowsComponent name={name} /> <ProfileFollowsComponent name={name} />
</View> </View>
) )
} })

View File

@ -12,6 +12,7 @@ import {
FontAwesomeIcon, FontAwesomeIcon,
FontAwesomeIconStyle, FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome' } from '@fortawesome/react-native-fontawesome'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ScrollView} from '../com/util/Views' import {ScrollView} from '../com/util/Views'
import { import {
NativeStackScreenProps, NativeStackScreenProps,
@ -36,7 +37,8 @@ const MENU_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10}
const FIVE_MIN = 5 * 60 * 1e3 const FIVE_MIN = 5 * 60 * 1e3
type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'> type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>
export const SearchScreen = observer<Props>(({}: Props) => { export const SearchScreen = withAuthRequired(
observer<Props>(({}: Props) => {
const pal = usePalette('default') const pal = usePalette('default')
const theme = useTheme() const theme = useTheme()
const store = useStores() const store = useStores()
@ -188,7 +190,8 @@ export const SearchScreen = observer<Props>(({}: Props) => {
</ScrollView> </ScrollView>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
) )
}) }),
)
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {

View File

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ScrollView} from '../com/util/Views' import {ScrollView} from '../com/util/Views'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import { import {
@ -17,7 +18,8 @@ import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
const FIVE_MIN = 5 * 60 * 1e3 const FIVE_MIN = 5 * 60 * 1e3
type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'> type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>
export const SearchScreen = observer(({}: Props) => { export const SearchScreen = withAuthRequired(
observer(({}: Props) => {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const scrollElRef = React.useRef<ScrollView>(null) const scrollElRef = React.useRef<ScrollView>(null)
@ -56,7 +58,8 @@ export const SearchScreen = observer(({}: Props) => {
<View style={s.footerSpacer} /> <View style={s.footerSpacer} />
</ScrollView> </ScrollView>
) )
}) }),
)
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {

View File

@ -16,6 +16,7 @@ import {
} from '@fortawesome/react-native-fontawesome' } from '@fortawesome/react-native-fontawesome'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import * as AppInfo from 'lib/app-info' import * as AppInfo from 'lib/app-info'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {s, colors} from 'lib/styles' import {s, colors} from 'lib/styles'
@ -33,7 +34,8 @@ import {useAnalytics} from 'lib/analytics'
import {NavigationProp} from 'lib/routes/types' import {NavigationProp} from 'lib/routes/types'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
export const SettingsScreen = observer(function Settings({}: Props) { export const SettingsScreen = withAuthRequired(
observer(function Settings({}: Props) {
const theme = useTheme() const theme = useTheme()
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
@ -261,7 +263,8 @@ export const SettingsScreen = observer(function Settings({}: Props) {
</ScrollView> </ScrollView>
</View> </View>
) )
}) }),
)
function AccountDropdownBtn({handle}: {handle: string}) { function AccountDropdownBtn({handle}: {handle: string}) {
const store = useStores() const store = useStores()

View File

@ -131,7 +131,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
return ( return (
<View style={styles.leftNav}> <View style={styles.leftNav}>
<ProfileCard /> {store.session.hasSession && <ProfileCard />}
<BackBtn /> <BackBtn />
<NavItem <NavItem
href="/" href="/"
@ -164,6 +164,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
} }
label="Notifications" label="Notifications"
/> />
{store.session.hasSession && (
<NavItem <NavItem
href={`/profile/${store.me.handle}`} href={`/profile/${store.me.handle}`}
icon={<UserIcon strokeWidth={1.75} size={28} style={pal.text} />} icon={<UserIcon strokeWidth={1.75} size={28} style={pal.text} />}
@ -172,6 +173,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
} }
label="Profile" label="Profile"
/> />
)}
<NavItem <NavItem
href="/settings" href="/settings"
icon={<CogIcon strokeWidth={1.75} size={28} style={pal.text} />} icon={<CogIcon strokeWidth={1.75} size={28} style={pal.text} />}
@ -180,7 +182,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
} }
label="Settings" label="Settings"
/> />
<ComposeBtn /> {store.session.hasSession && <ComposeBtn />}
</View> </View>
) )
}) })

View File

@ -7,12 +7,14 @@ import {Text} from 'view/com/util/text/Text'
import {TextLink} from 'view/com/util/Link' import {TextLink} from 'view/com/util/Link'
import {FEEDBACK_FORM_URL} from 'lib/constants' import {FEEDBACK_FORM_URL} from 'lib/constants'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {useStores} from 'state/index'
export const DesktopRightNav = observer(function DesktopRightNav() { export const DesktopRightNav = observer(function DesktopRightNav() {
const store = useStores()
const pal = usePalette('default') const pal = usePalette('default')
return ( return (
<View style={[styles.rightNav, pal.view]}> <View style={[styles.rightNav, pal.view]}>
<DesktopSearch /> {store.session.hasSession && <DesktopSearch />}
<View style={styles.message}> <View style={styles.message}>
<Text type="md" style={[pal.textLight, styles.messageLine]}> <Text type="md" style={[pal.textLight, styles.messageLine]}>
Welcome to Bluesky! This is a beta application that's still in Welcome to Bluesky! This is a beta application that's still in

View File

@ -5,7 +5,6 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {Drawer} from 'react-native-drawer-layout' import {Drawer} from 'react-native-drawer-layout'
import {useNavigationState} from '@react-navigation/native' import {useNavigationState} from '@react-navigation/native'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {Login} from 'view/screens/Login'
import {ModalsContainer} from 'view/com/modals/Modal' import {ModalsContainer} from 'view/com/modals/Modal'
import {Lightbox} from 'view/com/lightbox/Lightbox' import {Lightbox} from 'view/com/lightbox/Lightbox'
import {Text} from 'view/com/util/text/Text' import {Text} from 'view/com/util/text/Text'
@ -104,20 +103,6 @@ export const Shell: React.FC = observer(() => {
) )
} }
if (!store.session.hasSession) {
return (
<View style={styles.outerContainer}>
<StatusBar
barStyle={
theme.colorScheme === 'dark' ? 'light-content' : 'dark-content'
}
/>
<Login />
<ModalsContainer />
</View>
)
}
return ( return (
<View testID="mobileShellView" style={[styles.outerContainer, pal.view]}> <View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
<StatusBar <StatusBar

View File

@ -4,7 +4,6 @@ import {View, StyleSheet} from 'react-native'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {DesktopLeftNav} from './desktop/LeftNav' import {DesktopLeftNav} from './desktop/LeftNav'
import {DesktopRightNav} from './desktop/RightNav' import {DesktopRightNav} from './desktop/RightNav'
import {Login} from '../screens/Login'
import {ErrorBoundary} from '../com/util/ErrorBoundary' import {ErrorBoundary} from '../com/util/ErrorBoundary'
import {Lightbox} from '../com/lightbox/Lightbox' import {Lightbox} from '../com/lightbox/Lightbox'
import {ModalsContainer} from '../com/modals/Modal' import {ModalsContainer} from '../com/modals/Modal'
@ -45,21 +44,10 @@ const ShellInner = observer(() => {
export const Shell: React.FC = observer(() => { export const Shell: React.FC = observer(() => {
const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark) const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
const store = useStores()
if (isMobileWeb) { if (isMobileWeb) {
return <NoMobileWeb /> return <NoMobileWeb />
} }
if (!store.session.hasSession) {
return (
<View style={[s.hContentRegion, pageBg]}>
<Login />
<ModalsContainer />
</View>
)
}
return ( return (
<View style={[s.hContentRegion, pageBg]}> <View style={[s.hContentRegion, pageBg]}>
<RoutesContainer> <RoutesContainer>