Make settings account buttons a little nicer (#4980)
* Replace account dropdown with Menu * Replace card row and add interaction state * Remove testID copy pasta * Sanitize handle * Remove hover from rowzio/stable
parent
990bf306c5
commit
5ec8761b29
|
@ -1,53 +1,59 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable} from 'react-native'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {
|
|
||||||
FontAwesomeIcon,
|
|
||||||
FontAwesomeIconStyle,
|
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {msg} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {SessionAccount, useSessionApi} from '#/state/session'
|
import {SessionAccount, useSessionApi} from '#/state/session'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
import {s} from 'lib/styles'
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
import {useDialogControl} from '#/components/Dialog'
|
import {useDialogControl} from '#/components/Dialog'
|
||||||
|
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
|
||||||
|
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
||||||
|
import * as Menu from '#/components/Menu'
|
||||||
import * as Prompt from '#/components/Prompt'
|
import * as Prompt from '#/components/Prompt'
|
||||||
import * as Toast from '../../com/util/Toast'
|
import * as Toast from '../../com/util/Toast'
|
||||||
import {DropdownItem, NativeDropdown} from './forms/NativeDropdown'
|
|
||||||
|
|
||||||
export function AccountDropdownBtn({account}: {account: SessionAccount}) {
|
export function AccountDropdownBtn({account}: {account: SessionAccount}) {
|
||||||
const pal = usePalette('default')
|
const {_} = useLingui()
|
||||||
const {removeAccount} = useSessionApi()
|
const {removeAccount} = useSessionApi()
|
||||||
const removePromptControl = useDialogControl()
|
const removePromptControl = useDialogControl()
|
||||||
const {_} = useLingui()
|
|
||||||
|
|
||||||
const items: DropdownItem[] = [
|
|
||||||
{
|
|
||||||
label: _(msg`Remove account`),
|
|
||||||
onPress: removePromptControl.open,
|
|
||||||
icon: {
|
|
||||||
ios: {
|
|
||||||
name: 'trash',
|
|
||||||
},
|
|
||||||
android: 'ic_delete',
|
|
||||||
web: ['far', 'trash-can'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Pressable accessibilityRole="button" style={s.pl10}>
|
<Menu.Root>
|
||||||
<NativeDropdown
|
<Menu.Trigger label={_(`Account options`)}>
|
||||||
|
{({props}) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
{...props}
|
||||||
testID="accountSettingsDropdownBtn"
|
testID="accountSettingsDropdownBtn"
|
||||||
items={items}
|
label={_(`Account options`)}
|
||||||
accessibilityLabel={_(msg`Account options`)}
|
hitSlop={HITSLOP_10}
|
||||||
accessibilityHint="">
|
size="xsmall"
|
||||||
<FontAwesomeIcon
|
shape="round"
|
||||||
icon="ellipsis-h"
|
color="secondary"
|
||||||
style={pal.textLight as FontAwesomeIconStyle}
|
variant="ghost">
|
||||||
/>
|
<ButtonIcon icon={Ellipsis} size="sm" />
|
||||||
</NativeDropdown>
|
</Button>
|
||||||
</Pressable>
|
)
|
||||||
|
}}
|
||||||
|
</Menu.Trigger>
|
||||||
|
|
||||||
|
<Menu.Outer style={{minWidth: 170}}>
|
||||||
|
<Menu.Group>
|
||||||
|
<Menu.Item
|
||||||
|
label={_(msg`Remove account`)}
|
||||||
|
onPress={() => {
|
||||||
|
removePromptControl.open()
|
||||||
|
}}>
|
||||||
|
<Menu.ItemText>
|
||||||
|
<Trans>Remove account</Trans>
|
||||||
|
</Menu.ItemText>
|
||||||
|
<Menu.ItemIcon icon={Trash} />
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.Group>
|
||||||
|
</Menu.Outer>
|
||||||
|
</Menu.Root>
|
||||||
|
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
control={removePromptControl}
|
control={removePromptControl}
|
||||||
title={_(msg`Remove from quick access?`)}
|
title={_(msg`Remove from quick access?`)}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {useLingui} from '@lingui/react'
|
||||||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {useModalControls} from '#/state/modals'
|
import {useModalControls} from '#/state/modals'
|
||||||
import {clearStorage} from '#/state/persisted'
|
import {clearStorage} from '#/state/persisted'
|
||||||
|
@ -55,8 +56,11 @@ import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||||
import {ScrollView} from 'view/com/util/Views'
|
import {ScrollView} from 'view/com/util/Views'
|
||||||
import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
|
import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
import {Button, ButtonContext} from '#/components/Button'
|
||||||
import {useDialogControl} from '#/components/Dialog'
|
import {useDialogControl} from '#/components/Dialog'
|
||||||
import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
|
import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
|
||||||
|
import {Link as NewLink} from '#/components/Link'
|
||||||
|
import {Text as NewText} from '#/components/Typography'
|
||||||
import {Email2FAToggle} from './Email2FAToggle'
|
import {Email2FAToggle} from './Email2FAToggle'
|
||||||
import {ExportCarDialog} from './ExportCarDialog'
|
import {ExportCarDialog} from './ExportCarDialog'
|
||||||
|
|
||||||
|
@ -72,62 +76,69 @@ function SettingsAccountCard({
|
||||||
logContext: 'Settings',
|
logContext: 'Settings',
|
||||||
) => void
|
) => void
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {data: profile} = useProfileQuery({did: account.did})
|
const {data: profile} = useProfileQuery({did: account.did})
|
||||||
const isCurrentAccount = account.did === currentAccount?.did
|
const isCurrentAccount = account.did === currentAccount?.did
|
||||||
|
|
||||||
const contents = (
|
const contents = (ctx: ButtonContext) => (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
pal.view,
|
a.w_full,
|
||||||
styles.linkCard,
|
a.flex_row,
|
||||||
account.did === pendingDid && t.atoms.bg_contrast_25,
|
a.align_center,
|
||||||
|
a.gap_md,
|
||||||
|
a.py_md,
|
||||||
|
t.atoms.bg,
|
||||||
|
{
|
||||||
|
paddingHorizontal: 18,
|
||||||
|
marginBottom: 1,
|
||||||
|
},
|
||||||
|
account.did === pendingDid && [t.atoms.bg_contrast_25],
|
||||||
|
ctx.pressed && [t.atoms.bg_contrast_25],
|
||||||
]}>
|
]}>
|
||||||
<View style={styles.avi}>
|
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
size={40}
|
size={40}
|
||||||
avatar={profile?.avatar}
|
avatar={profile?.avatar}
|
||||||
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
<View style={[a.flex_1]}>
|
||||||
<View style={[s.flex1]}>
|
<NewText
|
||||||
<Text type="md-bold" style={[pal.text, a.self_start]} numberOfLines={1}>
|
style={[a.text_md, a.font_bold, a.leading_tight]}
|
||||||
|
numberOfLines={1}>
|
||||||
{profile?.displayName || account.handle}
|
{profile?.displayName || account.handle}
|
||||||
</Text>
|
</NewText>
|
||||||
<Text type="sm" style={pal.textLight} numberOfLines={1}>
|
<NewText
|
||||||
{account.handle}
|
style={[t.atoms.text_contrast_medium, a.leading_tight]}
|
||||||
</Text>
|
numberOfLines={1}>
|
||||||
|
{sanitizeHandle(account.handle, '@')}
|
||||||
|
</NewText>
|
||||||
</View>
|
</View>
|
||||||
<AccountDropdownBtn account={account} />
|
<AccountDropdownBtn account={account} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
||||||
return isCurrentAccount ? (
|
return isCurrentAccount ? (
|
||||||
<Link
|
<NewLink
|
||||||
href={makeProfileLink({
|
to={makeProfileLink({
|
||||||
did: currentAccount?.did,
|
did: currentAccount?.did,
|
||||||
handle: currentAccount?.handle,
|
handle: currentAccount?.handle,
|
||||||
})}
|
})}
|
||||||
title={_(msg`Your profile`)}
|
label={_(msg`Your profile`)}
|
||||||
noFeedback>
|
style={[a.w_full]}>
|
||||||
{contents}
|
{contents}
|
||||||
</Link>
|
</NewLink>
|
||||||
) : (
|
) : (
|
||||||
<TouchableOpacity
|
<Button
|
||||||
testID={`switchToAccountBtn-${account.handle}`}
|
testID={`switchToAccountBtn-${account.handle}`}
|
||||||
key={account.did}
|
|
||||||
onPress={
|
onPress={
|
||||||
pendingDid ? undefined : () => onPressSwitchAccount(account, 'Settings')
|
pendingDid ? undefined : () => onPressSwitchAccount(account, 'Settings')
|
||||||
}
|
}
|
||||||
accessibilityRole="button"
|
label={_(msg`Switch to ${account.handle}`)}
|
||||||
accessibilityLabel={_(msg`Switch to ${account.handle}`)}
|
style={[a.w_full]}>
|
||||||
accessibilityHint={_(msg`Switches the account you are logged in to`)}
|
|
||||||
activeOpacity={0.8}>
|
|
||||||
{contents}
|
{contents}
|
||||||
</TouchableOpacity>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue