Improvements to UI in web logged-out views (#1341)

* Add LoggedOutLayout for desktop/tablet web

* Avoid screen flash in the transition to onboarding

* Fix comment
zio/stable
Paul Frazee 2023-08-30 17:55:01 -07:00 committed by GitHub
parent a498acab6e
commit 04992f14f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 239 additions and 120 deletions

View File

@ -109,13 +109,8 @@ export class CreateAccountModel {
this.setError('') this.setError('')
this.setIsProcessing(true) this.setIsProcessing(true)
// open the onboarding screens after the session is created
const sessionReadySub = this.rootStore.onSessionReady(() => {
sessionReadySub.remove()
this.rootStore.onboarding.start()
})
try { try {
this.rootStore.onboarding.start() // start now to avoid flashing the wrong view
await this.rootStore.session.createAccount({ await this.rootStore.session.createAccount({
service: this.serviceUrl, service: this.serviceUrl,
email: this.email, email: this.email,
@ -125,7 +120,7 @@ export class CreateAccountModel {
}) })
track('Create Account') track('Create Account')
} catch (e: any) { } catch (e: any) {
sessionReadySub.remove() this.rootStore.onboarding.skip() // undo starting the onboard
let errMsg = e.toString() let errMsg = e.toString()
if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) { if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) {
errMsg = errMsg =

View File

@ -9,7 +9,6 @@ import {usePalette} from 'lib/hooks/usePalette'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {useAnalytics} from 'lib/analytics/analytics' import {useAnalytics} from 'lib/analytics/analytics'
import {SplashScreen} from './SplashScreen' import {SplashScreen} from './SplashScreen'
import {CenteredView} from '../util/Views'
enum ScreenState { enum ScreenState {
S_LoginOrCreateAccount, S_LoginOrCreateAccount,
@ -43,8 +42,7 @@ export const LoggedOut = observer(() => {
} }
return ( return (
<CenteredView style={[s.hContentRegion, pal.view]}> <SafeAreaView testID="noSessionView" style={[s.hContentRegion, pal.view]}>
<SafeAreaView testID="noSessionView" style={s.hContentRegion}>
<ErrorBoundary> <ErrorBoundary>
{screenState === ScreenState.S_Login ? ( {screenState === ScreenState.S_Login ? (
<Login <Login
@ -62,6 +60,5 @@ export const LoggedOut = observer(() => {
) : undefined} ) : undefined}
</ErrorBoundary> </ErrorBoundary>
</SafeAreaView> </SafeAreaView>
</CenteredView>
) )
}) })

View File

@ -10,6 +10,7 @@ import {
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {useAnalytics} from 'lib/analytics/analytics' import {useAnalytics} from 'lib/analytics/analytics'
import {Text} from '../../util/text/Text' import {Text} from '../../util/text/Text'
import {LoggedOutLayout} from 'view/com/util/layouts/LoggedOutLayout'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {CreateAccountModel} from 'state/models/ui/create-account' import {CreateAccountModel} from 'state/models/ui/create-account'
@ -65,6 +66,10 @@ export const CreateAccount = observer(
}, [model, track]) }, [model, track])
return ( return (
<LoggedOutLayout
leadin={`Step ${model.step}`}
title="Create Account"
description="We're so excited to have you join us!">
<ScrollView testID="createAccount" style={pal.view}> <ScrollView testID="createAccount" style={pal.view}>
<KeyboardAvoidingView behavior="padding"> <KeyboardAvoidingView behavior="padding">
<View style={styles.stepContainer}> <View style={styles.stepContainer}>
@ -119,6 +124,7 @@ export const CreateAccount = observer(
<View style={s.footerSpacer} /> <View style={s.footerSpacer} />
</KeyboardAvoidingView> </KeyboardAvoidingView>
</ScrollView> </ScrollView>
</LoggedOutLayout>
) )
}, },
) )

View File

@ -93,6 +93,7 @@ function validWebLink(url?: string): string | undefined {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
policies: { policies: {
flexDirection: 'row',
gap: 8, gap: 8,
}, },
errorIcon: { errorIcon: {

View File

@ -17,6 +17,7 @@ import {BskyAgent} from '@atproto/api'
import {useAnalytics} from 'lib/analytics/analytics' import {useAnalytics} from 'lib/analytics/analytics'
import {Text} from '../../util/text/Text' import {Text} from '../../util/text/Text'
import {UserAvatar} from '../../util/UserAvatar' import {UserAvatar} from '../../util/UserAvatar'
import {LoggedOutLayout} from 'view/com/util/layouts/LoggedOutLayout'
import {s, colors} from 'lib/styles' import {s, colors} from 'lib/styles'
import {createFullHandle} from 'lib/strings/handles' import {createFullHandle} from 'lib/strings/handles'
import {toNiceDomain} from 'lib/strings/url-helpers' import {toNiceDomain} from 'lib/strings/url-helpers'
@ -99,11 +100,12 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
} }
return ( return (
<KeyboardAvoidingView <KeyboardAvoidingView testID="signIn" behavior="padding" style={pal.view}>
testID="signIn"
behavior="padding"
style={[pal.view, s.pt10]}>
{currentForm === Forms.Login ? ( {currentForm === Forms.Login ? (
<LoggedOutLayout
leadin=""
title="Sign in"
description="Enter your username and password">
<LoginForm <LoginForm
store={store} store={store}
error={error} error={error}
@ -116,15 +118,25 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
onPressForgotPassword={onPressForgotPassword} onPressForgotPassword={onPressForgotPassword}
onPressRetryConnect={onPressRetryConnect} onPressRetryConnect={onPressRetryConnect}
/> />
</LoggedOutLayout>
) : undefined} ) : undefined}
{currentForm === Forms.ChooseAccount ? ( {currentForm === Forms.ChooseAccount ? (
<LoggedOutLayout
leadin=""
title="Sign in as..."
description="Select from an existing account">
<ChooseAccountForm <ChooseAccountForm
store={store} store={store}
onSelectAccount={onSelectAccount} onSelectAccount={onSelectAccount}
onPressBack={onPressBack} onPressBack={onPressBack}
/> />
</LoggedOutLayout>
) : undefined} ) : undefined}
{currentForm === Forms.ForgotPassword ? ( {currentForm === Forms.ForgotPassword ? (
<LoggedOutLayout
leadin=""
title="Forgot Password"
description="Let's get your password reset!">
<ForgotPasswordForm <ForgotPasswordForm
store={store} store={store}
error={error} error={error}
@ -135,8 +147,13 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
onPressBack={gotoForm(Forms.Login)} onPressBack={gotoForm(Forms.Login)}
onEmailSent={gotoForm(Forms.SetNewPassword)} onEmailSent={gotoForm(Forms.SetNewPassword)}
/> />
</LoggedOutLayout>
) : undefined} ) : undefined}
{currentForm === Forms.SetNewPassword ? ( {currentForm === Forms.SetNewPassword ? (
<LoggedOutLayout
leadin=""
title="Forgot Password"
description="Let's get your password reset!">
<SetNewPasswordForm <SetNewPasswordForm
store={store} store={store}
error={error} error={error}
@ -145,6 +162,7 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
onPressBack={gotoForm(Forms.ForgotPassword)} onPressBack={gotoForm(Forms.ForgotPassword)}
onPasswordSet={gotoForm(Forms.PasswordUpdated)} onPasswordSet={gotoForm(Forms.PasswordUpdated)}
/> />
</LoggedOutLayout>
) : undefined} ) : undefined}
{currentForm === Forms.PasswordUpdated ? ( {currentForm === Forms.PasswordUpdated ? (
<PasswordUpdatedForm onPressNext={gotoForm(Forms.Login)} /> <PasswordUpdatedForm onPressNext={gotoForm(Forms.Login)} />
@ -834,9 +852,9 @@ const SetNewPasswordForm = ({
const PasswordUpdatedForm = ({onPressNext}: {onPressNext: () => void}) => { const PasswordUpdatedForm = ({onPressNext}: {onPressNext: () => void}) => {
const {screen} = useAnalytics() const {screen} = useAnalytics()
// useEffect(() => { useEffect(() => {
screen('Signin:PasswordUpdatedForm') screen('Signin:PasswordUpdatedForm')
// }, [screen]) }, [screen])
const pal = usePalette('default') const pal = usePalette('default')
return ( return (

View File

@ -0,0 +1,102 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
import {Text} from '../text/Text'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
export const LoggedOutLayout = ({
leadin,
title,
description,
children,
}: React.PropsWithChildren<{
leadin: string
title: string
description: string
}>) => {
const {isMobile, isTabletOrMobile} = useWebMediaQueries()
const pal = usePalette('default')
const sideBg = useColorSchemeStyle(pal.viewLight, pal.view)
const contentBg = useColorSchemeStyle(pal.view, {
backgroundColor: pal.colors.background,
borderColor: pal.colors.border,
borderLeftWidth: 1,
})
if (isMobile) {
return <View style={{paddingTop: 10}}>{children}</View>
}
return (
<View style={styles.container}>
<View style={[styles.side, sideBg]}>
<Text
style={[
pal.textLight,
styles.leadinText,
isTabletOrMobile && styles.leadinTextSmall,
]}>
{leadin}
</Text>
<Text
style={[
pal.link,
styles.titleText,
isTabletOrMobile && styles.titleTextSmall,
]}>
{title}
</Text>
<Text type="2xl-medium" style={[pal.textLight, styles.descriptionText]}>
{description}
</Text>
</View>
<View style={[styles.content, contentBg]}>
<View style={styles.contentWrapper}>{children}</View>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
height: '100vh',
},
side: {
flex: 1,
paddingHorizontal: 40,
paddingBottom: 80,
justifyContent: 'center',
},
content: {
flex: 2,
paddingHorizontal: 40,
justifyContent: 'center',
},
leadinText: {
fontSize: 36,
fontWeight: '800',
textAlign: 'right',
},
leadinTextSmall: {
fontSize: 24,
},
titleText: {
fontSize: 58,
fontWeight: '800',
textAlign: 'right',
},
titleTextSmall: {
fontSize: 36,
},
descriptionText: {
maxWidth: 400,
marginTop: 10,
marginLeft: 'auto',
textAlign: 'right',
},
contentWrapper: {
maxWidth: 600,
},
})