Improve account switcher pending state (#3827)

* Protect against races

* Reduce UI jank when switching accounts

* Add pending state to selected account

* Disable presses while pending
This commit is contained in:
dan 2024-05-02 21:55:50 +01:00 committed by GitHub
parent 8ba1b10ce0
commit b86c3b486f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 105 additions and 79 deletions

View file

@ -1,6 +1,5 @@
import React from 'react'
import {
ActivityIndicator,
Linking,
Platform,
Pressable,
@ -63,6 +62,7 @@ import * as Toast from 'view/com/util/Toast'
import {UserAvatar} from 'view/com/util/UserAvatar'
import {ScrollView} from 'view/com/util/Views'
import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage'
import {useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
import * as TextField from '#/components/forms/TextField'
@ -72,11 +72,11 @@ import {ExportCarDialog} from './ExportCarDialog'
function SettingsAccountCard({
account,
isSwitchingAccounts,
pendingDid,
onPressSwitchAccount,
}: {
account: SessionAccount
isSwitchingAccounts: boolean
pendingDid: string | null
onPressSwitchAccount: (
account: SessionAccount,
logContext: 'Settings',
@ -84,13 +84,19 @@ function SettingsAccountCard({
}) {
const pal = usePalette('default')
const {_} = useLingui()
const t = useTheme()
const {currentAccount} = useSession()
const {logout} = useSessionApi()
const {data: profile} = useProfileQuery({did: account.did})
const isCurrentAccount = account.did === currentAccount?.did
const contents = (
<View style={[pal.view, styles.linkCard]}>
<View
style={[
pal.view,
styles.linkCard,
account.did === pendingDid && t.atoms.bg_contrast_25,
]}>
<View style={styles.avi}>
<UserAvatar
size={40}
@ -122,7 +128,8 @@ function SettingsAccountCard({
}}
accessibilityRole="button"
accessibilityLabel={_(msg`Sign out`)}
accessibilityHint={`Signs ${profile?.displayName} out of Bluesky`}>
accessibilityHint={`Signs ${profile?.displayName} out of Bluesky`}
activeOpacity={0.8}>
<Text type="lg" style={pal.link}>
<Trans>Sign out</Trans>
</Text>
@ -148,13 +155,12 @@ function SettingsAccountCard({
testID={`switchToAccountBtn-${account.handle}`}
key={account.did}
onPress={
isSwitchingAccounts
? undefined
: () => onPressSwitchAccount(account, 'Settings')
pendingDid ? undefined : () => onPressSwitchAccount(account, 'Settings')
}
accessibilityRole="button"
accessibilityLabel={_(msg`Switch to ${account.handle}`)}
accessibilityHint={_(msg`Switches the account you are logged in to`)}>
accessibilityHint={_(msg`Switches the account you are logged in to`)}
activeOpacity={0.8}>
{contents}
</TouchableOpacity>
)
@ -181,7 +187,8 @@ export function SettingsScreen({}: Props) {
const closeAllActiveElements = useCloseAllActiveElements()
const exportCarControl = useDialogControl()
const birthdayControl = useDialogControl()
const {isSwitchingAccounts, onPressSwitchAccount} = useAccountSwitcher()
const {pendingDid, onPressSwitchAccount} = useAccountSwitcher()
const isSwitchingAccounts = !!pendingDid
// TODO: TEMP REMOVE WHEN CLOPS ARE RELEASED
const gate = useGate()
@ -382,60 +389,54 @@ export function SettingsScreen({}: Props) {
<View style={styles.spacer20} />
{!currentAccount.emailConfirmed && <EmailConfirmationNotice />}
<View style={[s.flexRow, styles.heading]}>
<Text type="xl-bold" style={pal.text}>
<Trans>Signed in as</Trans>
</Text>
<View style={s.flex1} />
</View>
<View pointerEvents={pendingDid ? 'none' : 'auto'}>
<SettingsAccountCard
account={currentAccount}
onPressSwitchAccount={onPressSwitchAccount}
pendingDid={pendingDid}
/>
</View>
</>
) : null}
<View style={[s.flexRow, styles.heading]}>
<Text type="xl-bold" style={pal.text}>
<Trans>Signed in as</Trans>
</Text>
<View style={s.flex1} />
<View pointerEvents={pendingDid ? 'none' : 'auto'}>
{accounts
.filter(a => a.did !== currentAccount?.did)
.map(account => (
<SettingsAccountCard
key={account.did}
account={account}
onPressSwitchAccount={onPressSwitchAccount}
pendingDid={pendingDid}
/>
))}
<TouchableOpacity
testID="switchToNewAccountBtn"
style={[styles.linkCard, pal.view]}
onPress={isSwitchingAccounts ? undefined : onPressAddAccount}
accessibilityRole="button"
accessibilityLabel={_(msg`Add account`)}
accessibilityHint={_(msg`Create a new Bluesky account`)}>
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="plus"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
<Trans>Add account</Trans>
</Text>
</TouchableOpacity>
</View>
{isSwitchingAccounts ? (
<View style={[pal.view, styles.linkCard]}>
<ActivityIndicator />
</View>
) : (
<SettingsAccountCard
account={currentAccount!}
onPressSwitchAccount={onPressSwitchAccount}
isSwitchingAccounts={isSwitchingAccounts}
/>
)}
{accounts
.filter(a => a.did !== currentAccount?.did)
.map(account => (
<SettingsAccountCard
key={account.did}
account={account}
onPressSwitchAccount={onPressSwitchAccount}
isSwitchingAccounts={isSwitchingAccounts}
/>
))}
<TouchableOpacity
testID="switchToNewAccountBtn"
style={[
styles.linkCard,
pal.view,
isSwitchingAccounts && styles.dimmed,
]}
onPress={isSwitchingAccounts ? undefined : onPressAddAccount}
accessibilityRole="button"
accessibilityLabel={_(msg`Add account`)}
accessibilityHint={_(msg`Create a new Bluesky account`)}>
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="plus"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
<Trans>Add account</Trans>
</Text>
</TouchableOpacity>
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>