Account quick switch on web (#7190)
* account quick switch on web * dont show line if one account * switch account label text * add chevron hover state * swagged up hover state * add icons * tune scale anim and respect prefers-reduced-motion * fix reduced motion * fix placeholder position * move menu components out to separate component * Pipe through outer handlers to Button * Abstract lag in control.isOpen state * add profile info into empty space * fix tablet * Alternative * Revert "Alternative" This reverts commit 050ab9595ef3bbc32529ad6588e4690d37539fbe. * maybe fix flicker issue * just do 50ms when not active * delay other animations --------- Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
@@ -961,6 +961,9 @@ export const atoms = {
|
|||||||
transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)',
|
transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)',
|
||||||
transitionDuration: '100ms',
|
transitionDuration: '100ms',
|
||||||
}),
|
}),
|
||||||
|
transition_delay_50ms: web({
|
||||||
|
transitionDelay: '50ms',
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Layout.SCROLLBAR_OFFSET}
|
* {@link Layout.SCROLLBAR_OFFSET}
|
||||||
|
|||||||
+30
-16
@@ -3,10 +3,12 @@ import {
|
|||||||
AccessibilityProps,
|
AccessibilityProps,
|
||||||
GestureResponderEvent,
|
GestureResponderEvent,
|
||||||
MouseEvent,
|
MouseEvent,
|
||||||
|
NativeSyntheticEvent,
|
||||||
Pressable,
|
Pressable,
|
||||||
PressableProps,
|
PressableProps,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
|
TargetedEvent,
|
||||||
TextProps,
|
TextProps,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
View,
|
View,
|
||||||
@@ -76,6 +78,8 @@ export type ButtonProps = Pick<
|
|||||||
| 'onHoverOut'
|
| 'onHoverOut'
|
||||||
| 'onPressIn'
|
| 'onPressIn'
|
||||||
| 'onPressOut'
|
| 'onPressOut'
|
||||||
|
| 'onFocus'
|
||||||
|
| 'onBlur'
|
||||||
> &
|
> &
|
||||||
AccessibilityProps &
|
AccessibilityProps &
|
||||||
VariantProps & {
|
VariantProps & {
|
||||||
@@ -116,6 +120,12 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
|||||||
style,
|
style,
|
||||||
hoverStyle: hoverStyleProp,
|
hoverStyle: hoverStyleProp,
|
||||||
PressableComponent = Pressable,
|
PressableComponent = Pressable,
|
||||||
|
onPressIn: onPressInOuter,
|
||||||
|
onPressOut: onPressOutOuter,
|
||||||
|
onHoverIn: onHoverInOuter,
|
||||||
|
onHoverOut: onHoverOutOuter,
|
||||||
|
onFocus: onFocusOuter,
|
||||||
|
onBlur: onBlurOuter,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
@@ -127,7 +137,6 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
|||||||
focused: false,
|
focused: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const onPressInOuter = rest.onPressIn
|
|
||||||
const onPressIn = React.useCallback(
|
const onPressIn = React.useCallback(
|
||||||
(e: GestureResponderEvent) => {
|
(e: GestureResponderEvent) => {
|
||||||
setState(s => ({
|
setState(s => ({
|
||||||
@@ -138,7 +147,6 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
|||||||
},
|
},
|
||||||
[setState, onPressInOuter],
|
[setState, onPressInOuter],
|
||||||
)
|
)
|
||||||
const onPressOutOuter = rest.onPressOut
|
|
||||||
const onPressOut = React.useCallback(
|
const onPressOut = React.useCallback(
|
||||||
(e: GestureResponderEvent) => {
|
(e: GestureResponderEvent) => {
|
||||||
setState(s => ({
|
setState(s => ({
|
||||||
@@ -149,7 +157,6 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
|||||||
},
|
},
|
||||||
[setState, onPressOutOuter],
|
[setState, onPressOutOuter],
|
||||||
)
|
)
|
||||||
const onHoverInOuter = rest.onHoverIn
|
|
||||||
const onHoverIn = React.useCallback(
|
const onHoverIn = React.useCallback(
|
||||||
(e: MouseEvent) => {
|
(e: MouseEvent) => {
|
||||||
setState(s => ({
|
setState(s => ({
|
||||||
@@ -160,7 +167,6 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
|||||||
},
|
},
|
||||||
[setState, onHoverInOuter],
|
[setState, onHoverInOuter],
|
||||||
)
|
)
|
||||||
const onHoverOutOuter = rest.onHoverOut
|
|
||||||
const onHoverOut = React.useCallback(
|
const onHoverOut = React.useCallback(
|
||||||
(e: MouseEvent) => {
|
(e: MouseEvent) => {
|
||||||
setState(s => ({
|
setState(s => ({
|
||||||
@@ -171,18 +177,26 @@ export const Button = React.forwardRef<View, ButtonProps>(
|
|||||||
},
|
},
|
||||||
[setState, onHoverOutOuter],
|
[setState, onHoverOutOuter],
|
||||||
)
|
)
|
||||||
const onFocus = React.useCallback(() => {
|
const onFocus = React.useCallback(
|
||||||
setState(s => ({
|
(e: NativeSyntheticEvent<TargetedEvent>) => {
|
||||||
...s,
|
setState(s => ({
|
||||||
focused: true,
|
...s,
|
||||||
}))
|
focused: true,
|
||||||
}, [setState])
|
}))
|
||||||
const onBlur = React.useCallback(() => {
|
onFocusOuter?.(e)
|
||||||
setState(s => ({
|
},
|
||||||
...s,
|
[setState, onFocusOuter],
|
||||||
focused: false,
|
)
|
||||||
}))
|
const onBlur = React.useCallback(
|
||||||
}, [setState])
|
(e: NativeSyntheticEvent<TargetedEvent>) => {
|
||||||
|
setState(s => ({
|
||||||
|
...s,
|
||||||
|
focused: false,
|
||||||
|
}))
|
||||||
|
onBlurOuter?.(e)
|
||||||
|
},
|
||||||
|
[setState, onBlurOuter],
|
||||||
|
)
|
||||||
|
|
||||||
const {baseStyles, hoverStyles} = React.useMemo(() => {
|
const {baseStyles, hoverStyles} = React.useMemo(() => {
|
||||||
const baseStyles: ViewStyle[] = []
|
const baseStyles: ViewStyle[] = []
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ export function Outer({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Item({children, label, onPress, ...rest}: ItemProps) {
|
export function Item({children, label, onPress, style, ...rest}: ItemProps) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {control} = useMenuContext()
|
const {control} = useMenuContext()
|
||||||
const {
|
const {
|
||||||
@@ -248,6 +248,7 @@ export function Item({children, label, onPress, ...rest}: ItemProps) {
|
|||||||
? t.atoms.bg_contrast_25
|
? t.atoms.bg_contrast_25
|
||||||
: t.atoms.bg_contrast_50,
|
: t.atoms.bg_contrast_50,
|
||||||
],
|
],
|
||||||
|
style,
|
||||||
])}
|
])}
|
||||||
{...web({
|
{...web({
|
||||||
onMouseEnter,
|
onMouseEnter,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
@@ -9,28 +10,33 @@ import {
|
|||||||
useNavigationState,
|
useNavigationState,
|
||||||
} from '@react-navigation/native'
|
} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||||
import {getCurrentRoute, isTab} from '#/lib/routes/helpers'
|
import {getCurrentRoute, isTab} from '#/lib/routes/helpers'
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {CommonNavigatorParams} from '#/lib/routes/types'
|
import {CommonNavigatorParams} from '#/lib/routes/types'
|
||||||
import {useGate} from '#/lib/statsig/statsig'
|
import {useGate} from '#/lib/statsig/statsig'
|
||||||
import {isInvalidHandle} from '#/lib/strings/handles'
|
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||||
|
import {isInvalidHandle, sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {emitSoftReset} from '#/state/events'
|
import {emitSoftReset} from '#/state/events'
|
||||||
import {useHomeBadge} from '#/state/home-badge'
|
import {useHomeBadge} from '#/state/home-badge'
|
||||||
import {useFetchHandle} from '#/state/queries/handle'
|
import {useFetchHandle} from '#/state/queries/handle'
|
||||||
import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations'
|
import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations'
|
||||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
import {useProfilesQuery} from '#/state/queries/profile'
|
||||||
import {useSession} from '#/state/session'
|
import {SessionAccount, useSession, useSessionApi} from '#/state/session'
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
import {Link} from '#/view/com/util/Link'
|
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||||
|
import {useCloseAllActiveElements} from '#/state/util'
|
||||||
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {PressableWithHover} from '#/view/com/util/PressableWithHover'
|
import {PressableWithHover} from '#/view/com/util/PressableWithHover'
|
||||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||||
import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
||||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
import {atoms as a, tokens, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
|
import {DialogControlProps} from '#/components/Dialog'
|
||||||
|
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
|
||||||
import {
|
import {
|
||||||
Bell_Filled_Corner0_Rounded as BellFilled,
|
Bell_Filled_Corner0_Rounded as BellFilled,
|
||||||
Bell_Stroke2_Corner0_Rounded as Bell,
|
Bell_Stroke2_Corner0_Rounded as Bell,
|
||||||
@@ -39,6 +45,7 @@ import {
|
|||||||
BulletList_Filled_Corner0_Rounded as ListFilled,
|
BulletList_Filled_Corner0_Rounded as ListFilled,
|
||||||
BulletList_Stroke2_Corner0_Rounded as List,
|
BulletList_Stroke2_Corner0_Rounded as List,
|
||||||
} from '#/components/icons/BulletList'
|
} from '#/components/icons/BulletList'
|
||||||
|
import {DotGrid_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
|
||||||
import {EditBig_Stroke2_Corner0_Rounded as EditBig} from '#/components/icons/EditBig'
|
import {EditBig_Stroke2_Corner0_Rounded as EditBig} from '#/components/icons/EditBig'
|
||||||
import {
|
import {
|
||||||
Hashtag_Filled_Corner0_Rounded as HashtagFilled,
|
Hashtag_Filled_Corner0_Rounded as HashtagFilled,
|
||||||
@@ -54,6 +61,7 @@ import {
|
|||||||
Message_Stroke2_Corner0_Rounded as Message,
|
Message_Stroke2_Corner0_Rounded as Message,
|
||||||
Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
|
Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
|
||||||
} from '#/components/icons/Message'
|
} from '#/components/icons/Message'
|
||||||
|
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
|
||||||
import {
|
import {
|
||||||
SettingsGear2_Filled_Corner0_Rounded as SettingsFilled,
|
SettingsGear2_Filled_Corner0_Rounded as SettingsFilled,
|
||||||
SettingsGear2_Stroke2_Corner0_Rounded as Settings,
|
SettingsGear2_Stroke2_Corner0_Rounded as Settings,
|
||||||
@@ -62,44 +70,231 @@ import {
|
|||||||
UserCircle_Filled_Corner0_Rounded as UserCircleFilled,
|
UserCircle_Filled_Corner0_Rounded as UserCircleFilled,
|
||||||
UserCircle_Stroke2_Corner0_Rounded as UserCircle,
|
UserCircle_Stroke2_Corner0_Rounded as UserCircle,
|
||||||
} from '#/components/icons/UserCircle'
|
} from '#/components/icons/UserCircle'
|
||||||
|
import * as Menu from '#/components/Menu'
|
||||||
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
import {PlatformInfo} from '../../../../modules/expo-bluesky-swiss-army'
|
||||||
import {router} from '../../../routes'
|
import {router} from '../../../routes'
|
||||||
|
|
||||||
const NAV_ICON_WIDTH = 28
|
const NAV_ICON_WIDTH = 28
|
||||||
|
|
||||||
function ProfileCard() {
|
function ProfileCard() {
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount, accounts} = useSession()
|
||||||
const {isLoading, data: profile} = useProfileQuery({did: currentAccount!.did})
|
const {logoutEveryAccount} = useSessionApi()
|
||||||
const {isDesktop} = useWebMediaQueries()
|
const {isLoading, data} = useProfilesQuery({
|
||||||
|
handles: accounts.map(acc => acc.did),
|
||||||
|
})
|
||||||
|
const profiles = data?.profiles
|
||||||
|
const signOutPromptControl = Prompt.usePromptControl()
|
||||||
|
const {gtTablet} = useBreakpoints()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
const t = useTheme()
|
||||||
|
|
||||||
const size = 48
|
const size = 48
|
||||||
|
|
||||||
return !isLoading && profile ? (
|
const profile = profiles?.find(p => p.did === currentAccount!.did)
|
||||||
<Link
|
const otherAccounts = accounts
|
||||||
href={makeProfileLink({
|
.filter(acc => acc.did !== currentAccount!.did)
|
||||||
did: currentAccount!.did,
|
.map(account => ({
|
||||||
handle: currentAccount!.handle,
|
account,
|
||||||
})}
|
profile: profiles?.find(p => p.did === account.did),
|
||||||
style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}
|
}))
|
||||||
title={_(msg`My Profile`)}
|
|
||||||
asAnchor>
|
return (
|
||||||
<UserAvatar
|
<View style={[a.my_md, gtTablet && [a.w_full, a.align_start]]}>
|
||||||
avatar={profile.avatar}
|
{!isLoading && profile ? (
|
||||||
size={size}
|
<Menu.Root>
|
||||||
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
<Menu.Trigger label={_(msg`Switch accounts`)}>
|
||||||
/>
|
{({props, state, control}) => {
|
||||||
</Link>
|
const active = state.hovered || state.focused || control.isOpen
|
||||||
) : (
|
return (
|
||||||
<View style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}>
|
<Button
|
||||||
<LoadingPlaceholder
|
label={props.accessibilityLabel}
|
||||||
width={size}
|
{...props}
|
||||||
height={size}
|
style={[
|
||||||
style={{borderRadius: size}}
|
a.w_full,
|
||||||
|
a.transition_color,
|
||||||
|
active ? t.atoms.bg_contrast_25 : a.transition_delay_50ms,
|
||||||
|
a.rounded_full,
|
||||||
|
a.justify_between,
|
||||||
|
a.align_center,
|
||||||
|
a.flex_row,
|
||||||
|
{gap: 6},
|
||||||
|
gtTablet && [a.pl_lg, a.pr_md],
|
||||||
|
]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
!PlatformInfo.getIsReducedMotionEnabled() && [
|
||||||
|
a.transition_transform,
|
||||||
|
{transitionDuration: '250ms'},
|
||||||
|
!active && a.transition_delay_50ms,
|
||||||
|
],
|
||||||
|
a.relative,
|
||||||
|
a.z_10,
|
||||||
|
active && {
|
||||||
|
transform: [
|
||||||
|
{scale: gtTablet ? 2 / 3 : 0.8},
|
||||||
|
{translateX: gtTablet ? -22 : 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<UserAvatar
|
||||||
|
avatar={profile.avatar}
|
||||||
|
size={size}
|
||||||
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{gtTablet && (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_1,
|
||||||
|
a.transition_opacity,
|
||||||
|
!active && a.transition_delay_50ms,
|
||||||
|
{
|
||||||
|
marginLeft: tokens.space.xl * -1,
|
||||||
|
opacity: active ? 1 : 0,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Text
|
||||||
|
style={[a.font_heavy, a.text_sm, a.leading_snug]}
|
||||||
|
numberOfLines={1}>
|
||||||
|
{sanitizeDisplayName(
|
||||||
|
profile.displayName || profile.handle,
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_xs,
|
||||||
|
a.leading_snug,
|
||||||
|
t.atoms.text_contrast_medium,
|
||||||
|
]}
|
||||||
|
numberOfLines={1}>
|
||||||
|
{sanitizeHandle(profile.handle, '@')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<EllipsisIcon
|
||||||
|
aria-hidden={true}
|
||||||
|
style={[
|
||||||
|
t.atoms.text_contrast_medium,
|
||||||
|
a.transition_opacity,
|
||||||
|
{opacity: active ? 1 : 0},
|
||||||
|
]}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Menu.Trigger>
|
||||||
|
<SwitchMenuItems
|
||||||
|
accounts={otherAccounts}
|
||||||
|
signOutPromptControl={signOutPromptControl}
|
||||||
|
/>
|
||||||
|
</Menu.Root>
|
||||||
|
) : (
|
||||||
|
<LoadingPlaceholder
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
style={[{borderRadius: size}, gtTablet && a.ml_lg]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Prompt.Basic
|
||||||
|
control={signOutPromptControl}
|
||||||
|
title={_(msg`Sign out?`)}
|
||||||
|
description={_(msg`You will be signed out of all your accounts.`)}
|
||||||
|
onConfirm={() => logoutEveryAccount('Settings')}
|
||||||
|
confirmButtonCta={_(msg`Sign out`)}
|
||||||
|
cancelButtonCta={_(msg`Cancel`)}
|
||||||
|
confirmButtonColor="negative"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SwitchMenuItems({
|
||||||
|
accounts,
|
||||||
|
signOutPromptControl,
|
||||||
|
}: {
|
||||||
|
accounts:
|
||||||
|
| {
|
||||||
|
account: SessionAccount
|
||||||
|
profile?: AppBskyActorDefs.ProfileView
|
||||||
|
}[]
|
||||||
|
| undefined
|
||||||
|
signOutPromptControl: DialogControlProps
|
||||||
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const {onPressSwitchAccount, pendingDid} = useAccountSwitcher()
|
||||||
|
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||||
|
const closeEverything = useCloseAllActiveElements()
|
||||||
|
|
||||||
|
const onAddAnotherAccount = () => {
|
||||||
|
setShowLoggedOut(true)
|
||||||
|
closeEverything()
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Menu.Outer>
|
||||||
|
{accounts && accounts.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Menu.Group>
|
||||||
|
<Menu.LabelText>
|
||||||
|
<Trans>Switch account</Trans>
|
||||||
|
</Menu.LabelText>
|
||||||
|
{accounts.map(other => (
|
||||||
|
<Menu.Item
|
||||||
|
disabled={!!pendingDid}
|
||||||
|
style={[{minWidth: 150}]}
|
||||||
|
key={other.account.did}
|
||||||
|
label={_(
|
||||||
|
msg`Switch to ${sanitizeHandle(
|
||||||
|
other.profile?.handle ?? other.account.handle,
|
||||||
|
'@',
|
||||||
|
)}`,
|
||||||
|
)}
|
||||||
|
onPress={() =>
|
||||||
|
onPressSwitchAccount(other.account, 'SwitchAccount')
|
||||||
|
}>
|
||||||
|
<View style={[{marginLeft: tokens.space._2xs * -1}]}>
|
||||||
|
<UserAvatar
|
||||||
|
avatar={other.profile?.avatar}
|
||||||
|
size={20}
|
||||||
|
type={
|
||||||
|
other.profile?.associated?.labeler ? 'labeler' : 'user'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Menu.ItemText>
|
||||||
|
{sanitizeHandle(
|
||||||
|
other.profile?.handle ?? other.account.handle,
|
||||||
|
'@',
|
||||||
|
)}
|
||||||
|
</Menu.ItemText>
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu.Group>
|
||||||
|
<Menu.Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Menu.Item
|
||||||
|
label={_(msg`Add another account`)}
|
||||||
|
onPress={onAddAnotherAccount}>
|
||||||
|
<Menu.ItemIcon icon={PlusIcon} />
|
||||||
|
<Menu.ItemText>
|
||||||
|
<Trans>Add another account</Trans>
|
||||||
|
</Menu.ItemText>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item label={_(msg`Sign out`)} onPress={signOutPromptControl.open}>
|
||||||
|
<Menu.ItemIcon icon={LeaveIcon} />
|
||||||
|
<Menu.ItemText>
|
||||||
|
<Trans>Sign out</Trans>
|
||||||
|
</Menu.ItemText>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.Outer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
interface NavItemProps {
|
interface NavItemProps {
|
||||||
count?: string
|
count?: string
|
||||||
hasNew?: boolean
|
hasNew?: boolean
|
||||||
@@ -539,16 +734,6 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
transform: [],
|
transform: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
profileCard: {
|
|
||||||
marginVertical: 10,
|
|
||||||
width: 90,
|
|
||||||
paddingLeft: 12,
|
|
||||||
},
|
|
||||||
profileCardTablet: {
|
|
||||||
width: 70,
|
|
||||||
},
|
|
||||||
|
|
||||||
backBtn: {
|
backBtn: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 12,
|
top: 12,
|
||||||
|
|||||||
Reference in New Issue
Block a user