* Update the account-create and signin views to use the design system. Also: - Add borderDark to the theme - Start to an account selector in the signin flow * Dark mode fixes in signin ui * Track multiple active accounts and provide account-switching UI * Add test tooling for an in-memory pds * Add complete integration tests for login and the account switcher
190 lines
5.4 KiB
TypeScript
190 lines
5.4 KiB
TypeScript
import React, {useEffect} from 'react'
|
|
import {
|
|
ActivityIndicator,
|
|
ScrollView,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
View,
|
|
} from 'react-native'
|
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
|
import {observer} from 'mobx-react-lite'
|
|
import {useStores} from '../../state'
|
|
import {ScreenParams} from '../routes'
|
|
import {s} from '../lib/styles'
|
|
import {ViewHeader} from '../com/util/ViewHeader'
|
|
import {Link} from '../com/util/Link'
|
|
import {Text} from '../com/util/text/Text'
|
|
import * as Toast from '../com/util/Toast'
|
|
import {UserAvatar} from '../com/util/UserAvatar'
|
|
import {usePalette} from '../lib/hooks/usePalette'
|
|
import {AccountData} from '../../state/models/session'
|
|
|
|
export const Settings = observer(function Settings({
|
|
navIdx,
|
|
visible,
|
|
}: ScreenParams) {
|
|
const pal = usePalette('default')
|
|
const store = useStores()
|
|
const [isSwitching, setIsSwitching] = React.useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!visible) {
|
|
return
|
|
}
|
|
store.shell.setMinimalShellMode(false)
|
|
store.nav.setTitle(navIdx, 'Settings')
|
|
}, [visible, store])
|
|
|
|
const onPressSwitchAccount = async (acct: AccountData) => {
|
|
setIsSwitching(true)
|
|
if (await store.session.resumeSession(acct)) {
|
|
setIsSwitching(false)
|
|
Toast.show(`Signed in as ${acct.displayName || acct.handle}`)
|
|
return
|
|
}
|
|
setIsSwitching(false)
|
|
Toast.show('Sorry! We need you to enter your password.')
|
|
store.session.clear()
|
|
}
|
|
const onPressAddAccount = () => {
|
|
store.session.clear()
|
|
}
|
|
const onPressSignout = () => {
|
|
store.session.logout()
|
|
}
|
|
|
|
return (
|
|
<View style={[s.h100pct]} testID="settingsScreen">
|
|
<ViewHeader title="Settings" />
|
|
<ScrollView style={[s.mt10, s.pl10, s.pr10, s.h100pct]}>
|
|
<View style={[s.flexRow]}>
|
|
<Text type="xl-bold" style={pal.text}>
|
|
Signed in as
|
|
</Text>
|
|
<View style={s.flex1} />
|
|
<TouchableOpacity
|
|
testID="signOutBtn"
|
|
onPress={isSwitching ? undefined : onPressSignout}>
|
|
<Text type="xl-medium" style={pal.link}>
|
|
Sign out
|
|
</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
{isSwitching ? (
|
|
<View style={[pal.view, styles.profile]}>
|
|
<ActivityIndicator />
|
|
</View>
|
|
) : (
|
|
<Link
|
|
href={`/profile/${store.me.handle}`}
|
|
title="Your profile"
|
|
noFeedback>
|
|
<View style={[pal.view, styles.profile]}>
|
|
<UserAvatar
|
|
size={40}
|
|
displayName={store.me.displayName}
|
|
handle={store.me.handle || ''}
|
|
avatar={store.me.avatar}
|
|
/>
|
|
<View style={[s.ml10]}>
|
|
<Text type="xl-bold" style={pal.text}>
|
|
{store.me.displayName || store.me.handle}
|
|
</Text>
|
|
<Text style={pal.textLight}>@{store.me.handle}</Text>
|
|
</View>
|
|
</View>
|
|
</Link>
|
|
)}
|
|
<Text type="sm-medium" style={pal.text}>
|
|
Switch to:
|
|
</Text>
|
|
{store.session.switchableAccounts.map(account => (
|
|
<TouchableOpacity
|
|
testID={`switchToAccountBtn-${account.handle}`}
|
|
key={account.did}
|
|
style={[
|
|
pal.view,
|
|
styles.profile,
|
|
s.mb2,
|
|
isSwitching && styles.dimmed,
|
|
]}
|
|
onPress={
|
|
isSwitching ? undefined : () => onPressSwitchAccount(account)
|
|
}>
|
|
<UserAvatar
|
|
size={40}
|
|
displayName={account.displayName}
|
|
handle={account.handle || ''}
|
|
avatar={account.aviUrl}
|
|
/>
|
|
<View style={[s.ml10]}>
|
|
<Text type="xl-bold" style={pal.text}>
|
|
{account.displayName || account.handle}
|
|
</Text>
|
|
<Text style={pal.textLight}>@{account.handle}</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
))}
|
|
<TouchableOpacity
|
|
testID="switchToNewAccountBtn"
|
|
style={[
|
|
pal.view,
|
|
styles.profile,
|
|
s.mb2,
|
|
{alignItems: 'center'},
|
|
isSwitching && styles.dimmed,
|
|
]}
|
|
onPress={isSwitching ? undefined : onPressAddAccount}>
|
|
<FontAwesomeIcon icon="plus" />
|
|
<View style={[s.ml5]}>
|
|
<Text type="md-medium" style={pal.text}>
|
|
Add account
|
|
</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
<View style={{height: 50}} />
|
|
<Text type="sm-medium" style={[s.mb5]}>
|
|
Developer tools
|
|
</Text>
|
|
<Link
|
|
style={[pal.view, s.p10, s.mb2]}
|
|
href="/sys/log"
|
|
title="System log">
|
|
<Text style={pal.link}>System log</Text>
|
|
</Link>
|
|
<Link
|
|
style={[pal.view, s.p10, s.mb2]}
|
|
href="/sys/debug"
|
|
title="Debug tools">
|
|
<Text style={pal.link}>Storybook</Text>
|
|
</Link>
|
|
<View style={s.footerSpacer} />
|
|
</ScrollView>
|
|
</View>
|
|
)
|
|
})
|
|
|
|
const styles = StyleSheet.create({
|
|
dimmed: {
|
|
opacity: 0.5,
|
|
},
|
|
title: {
|
|
fontSize: 32,
|
|
fontWeight: 'bold',
|
|
marginTop: 20,
|
|
marginBottom: 14,
|
|
},
|
|
profile: {
|
|
flexDirection: 'row',
|
|
marginVertical: 6,
|
|
borderRadius: 4,
|
|
paddingVertical: 10,
|
|
paddingHorizontal: 10,
|
|
},
|
|
avi: {
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 30,
|
|
marginRight: 8,
|
|
},
|
|
})
|