Use ALF for the account quick switch dialog (#3327)

* Use ALF for account quick switch

* clean up modal type

* add haptics to dialog opening

* move account list to it's own component and share

* make tick slightly darker
zio/stable
Samuel Newman 2024-04-04 03:18:14 +01:00 committed by GitHub
parent 8cdd8394df
commit 712768dd8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 449 additions and 517 deletions

View File

@ -0,0 +1,141 @@
import React, {useCallback} from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useProfileQuery} from '#/state/queries/profile'
import {type SessionAccount, useSession} from '#/state/session'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme} from '#/alf'
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {ChevronRight_Stroke2_Corner0_Rounded as Chevron} from '#/components/icons/Chevron'
import {Button} from './Button'
import {Text} from './Typography'
export function AccountList({
onSelectAccount,
onSelectOther,
otherLabel,
}: {
onSelectAccount: (account: SessionAccount) => void
onSelectOther: () => void
otherLabel?: string
}) {
const {isSwitchingAccounts, currentAccount, accounts} = useSession()
const t = useTheme()
const {_} = useLingui()
const onPressAddAccount = useCallback(() => {
onSelectOther()
}, [onSelectOther])
return (
<View
style={[
a.rounded_md,
a.overflow_hidden,
a.border,
t.atoms.border_contrast_low,
]}>
{accounts.map(account => (
<React.Fragment key={account.did}>
<AccountItem
account={account}
onSelect={onSelectAccount}
isCurrentAccount={account.did === currentAccount?.did}
/>
<View style={[a.border_b, t.atoms.border_contrast_low]} />
</React.Fragment>
))}
<Button
testID="chooseAddAccountBtn"
style={[a.flex_1]}
onPress={isSwitchingAccounts ? undefined : onPressAddAccount}
label={_(msg`Login to account that is not listed`)}>
{({hovered, pressed}) => (
<View
style={[
a.flex_1,
a.flex_row,
a.align_center,
{height: 48},
(hovered || pressed || isSwitchingAccounts) &&
t.atoms.bg_contrast_25,
]}>
<Text
style={[
a.align_baseline,
a.flex_1,
a.flex_row,
a.py_sm,
{paddingLeft: 48},
]}>
{otherLabel ?? <Trans>Other account</Trans>}
</Text>
<Chevron size="sm" style={[t.atoms.text, a.mr_md]} />
</View>
)}
</Button>
</View>
)
}
function AccountItem({
account,
onSelect,
isCurrentAccount,
}: {
account: SessionAccount
onSelect: (account: SessionAccount) => void
isCurrentAccount: boolean
}) {
const t = useTheme()
const {_} = useLingui()
const {data: profile} = useProfileQuery({did: account.did})
const onPress = React.useCallback(() => {
onSelect(account)
}, [account, onSelect])
return (
<Button
testID={`chooseAccountBtn-${account.handle}`}
key={account.did}
style={[a.flex_1]}
onPress={onPress}
label={
isCurrentAccount
? _(msg`Continue as ${account.handle} (currently signed in)`)
: _(msg`Sign in as ${account.handle}`)
}>
{({hovered, pressed}) => (
<View
style={[
a.flex_1,
a.flex_row,
a.align_center,
{height: 48},
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<View style={a.p_md}>
<UserAvatar avatar={profile?.avatar} size={24} />
</View>
<Text style={[a.align_baseline, a.flex_1, a.flex_row, a.py_sm]}>
<Text style={[a.font_bold]}>
{profile?.displayName || account.handle}{' '}
</Text>
<Text style={[t.atoms.text_contrast_medium]}>{account.handle}</Text>
</Text>
{isCurrentAccount ? (
<Check
size="sm"
style={[{color: t.palette.positive_600}, a.mr_md]}
/>
) : (
<Chevron size="sm" style={[t.atoms.text, a.mr_md]} />
)}
</View>
)}
</Button>
)
}

View File

@ -1,23 +1,23 @@
import React from 'react' import React from 'react'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
import {View} from 'react-native' import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import * as Dialog from '#/components/Dialog' import {cleanError} from '#/lib/strings/errors'
import {Text} from '../Typography'
import {DateInput} from '#/view/com/util/forms/DateInput'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import { import {
usePreferencesQuery, usePreferencesQuery,
usePreferencesSetBirthDateMutation,
UsePreferencesQueryResponse, UsePreferencesQueryResponse,
usePreferencesSetBirthDateMutation,
} from '#/state/queries/preferences' } from '#/state/queries/preferences'
import {Button, ButtonIcon, ButtonText} from '../Button'
import {atoms as a, useTheme} from '#/alf'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {cleanError} from '#/lib/strings/errors' import {DateInput} from '#/view/com/util/forms/DateInput'
import {isIOS, isWeb} from '#/platform/detection' import {atoms as a, useTheme} from '#/alf'
import * as Dialog from '#/components/Dialog'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Button, ButtonIcon, ButtonText} from '../Button'
import {Text} from '../Typography'
export function BirthDateSettingsDialog({ export function BirthDateSettingsDialog({
control, control,

View File

@ -0,0 +1,61 @@
import React, {useCallback} from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
import {type SessionAccount, useSession} from '#/state/session'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util'
import {atoms as a} from '#/alf'
import * as Dialog from '#/components/Dialog'
import {AccountList} from '../AccountList'
import {Text} from '../Typography'
export function SwitchAccountDialog({
control,
}: {
control: Dialog.DialogControlProps
}) {
const {_} = useLingui()
const {currentAccount} = useSession()
const {onPressSwitchAccount} = useAccountSwitcher()
const {setShowLoggedOut} = useLoggedOutViewControls()
const closeAllActiveElements = useCloseAllActiveElements()
const onSelectAccount = useCallback(
(account: SessionAccount) => {
if (account.did === currentAccount?.did) {
control.close()
} else {
onPressSwitchAccount(account, 'SwitchAccount')
}
},
[currentAccount, control, onPressSwitchAccount],
)
const onPressAddAccount = useCallback(() => {
setShowLoggedOut(true)
closeAllActiveElements()
}, [setShowLoggedOut, closeAllActiveElements])
return (
<Dialog.Outer control={control}>
<Dialog.Handle />
<Dialog.ScrollableInner label={_(msg`Switch Account`)}>
<View style={[a.gap_lg]}>
<Text style={[a.text_2xl, a.font_bold]}>
<Trans>Switch Account</Trans>
</Text>
<AccountList
onSelectAccount={onSelectAccount}
onSelectOther={onPressAddAccount}
otherLabel={_(msg`Add account`)}
/>
</View>
</Dialog.ScrollableInner>
</Dialog.Outer>
)
}

View File

@ -5,76 +5,15 @@ import {useLingui} from '@lingui/react'
import {useAnalytics} from '#/lib/analytics/analytics' import {useAnalytics} from '#/lib/analytics/analytics'
import {logEvent} from '#/lib/statsig/statsig' import {logEvent} from '#/lib/statsig/statsig'
import {colors} from '#/lib/styles'
import {useProfileQuery} from '#/state/queries/profile'
import {SessionAccount, useSession, useSessionApi} from '#/state/session' import {SessionAccount, useSession, useSessionApi} from '#/state/session'
import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a} from '#/alf'
import {atoms as a, useTheme} from '#/alf' import {AccountList} from '#/components/AccountList'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
import * as TextField from '#/components/forms/TextField' import * as TextField from '#/components/forms/TextField'
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {ChevronRight_Stroke2_Corner0_Rounded as Chevron} from '#/components/icons/Chevron'
import {Text} from '#/components/Typography'
import {FormContainer} from './FormContainer' import {FormContainer} from './FormContainer'
function AccountItem({
account,
onSelect,
isCurrentAccount,
}: {
account: SessionAccount
onSelect: (account: SessionAccount) => void
isCurrentAccount: boolean
}) {
const t = useTheme()
const {_} = useLingui()
const {data: profile} = useProfileQuery({did: account.did})
const onPress = React.useCallback(() => {
onSelect(account)
}, [account, onSelect])
return (
<Button
testID={`chooseAccountBtn-${account.handle}`}
key={account.did}
style={[a.flex_1]}
onPress={onPress}
label={
isCurrentAccount
? _(msg`Continue as ${account.handle} (currently signed in)`)
: _(msg`Sign in as ${account.handle}`)
}>
{({hovered, pressed}) => (
<View
style={[
a.flex_1,
a.flex_row,
a.align_center,
{height: 48},
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<View style={a.p_md}>
<UserAvatar avatar={profile?.avatar} size={24} />
</View>
<Text style={[a.align_baseline, a.flex_1, a.flex_row, a.py_sm]}>
<Text style={[a.font_bold]}>
{profile?.displayName || account.handle}{' '}
</Text>
<Text style={[t.atoms.text_contrast_medium]}>{account.handle}</Text>
</Text>
{isCurrentAccount ? (
<Check size="sm" style={[{color: colors.green3}, a.mr_md]} />
) : (
<Chevron size="sm" style={[t.atoms.text, a.mr_md]} />
)}
</View>
)}
</Button>
)
}
export const ChooseAccountForm = ({ export const ChooseAccountForm = ({
onSelectAccount, onSelectAccount,
onPressBack, onPressBack,
@ -84,8 +23,7 @@ export const ChooseAccountForm = ({
}) => { }) => {
const {track, screen} = useAnalytics() const {track, screen} = useAnalytics()
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const {currentAccount} = useSession()
const {accounts, currentAccount} = useSession()
const {initSession} = useSessionApi() const {initSession} = useSessionApi()
const {setShowLoggedOut} = useLoggedOutViewControls() const {setShowLoggedOut} = useLoggedOutViewControls()
@ -125,52 +63,10 @@ export const ChooseAccountForm = ({
<TextField.Label> <TextField.Label>
<Trans>Sign in as...</Trans> <Trans>Sign in as...</Trans>
</TextField.Label> </TextField.Label>
<View <AccountList
style={[ onSelectAccount={onSelect}
a.rounded_md, onSelectOther={() => onSelectAccount()}
a.overflow_hidden,
a.border,
t.atoms.border_contrast_low,
]}>
{accounts.map(account => (
<React.Fragment key={account.did}>
<AccountItem
account={account}
onSelect={onSelect}
isCurrentAccount={account.did === currentAccount?.did}
/> />
<View style={[a.border_b, t.atoms.border_contrast_low]} />
</React.Fragment>
))}
<Button
testID="chooseNewAccountBtn"
style={[a.flex_1]}
onPress={() => onSelectAccount(undefined)}
label={_(msg`Login to account that is not listed`)}>
{({hovered, pressed}) => (
<View
style={[
a.flex_1,
a.flex_row,
a.align_center,
{height: 48},
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<Text
style={[
a.align_baseline,
a.flex_1,
a.flex_row,
a.py_sm,
{paddingLeft: 48},
]}>
<Trans>Other account</Trans>
</Text>
<Chevron size="sm" style={[t.atoms.text, a.mr_md]} />
</View>
)}
</Button>
</View>
</View> </View>
<View style={[a.flex_row]}> <View style={[a.flex_row]}>
<Button <Button

View File

@ -1,11 +1,11 @@
import React from 'react' import React from 'react'
import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api'
import {Image as RNImage} from 'react-native-image-crop-picker' import {Image as RNImage} from 'react-native-image-crop-picker'
import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api'
import {ImageModel} from '#/state/models/media/image'
import {GalleryModel} from '#/state/models/media/gallery'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {EmbedPlayerSource} from '#/lib/strings/embed-player' import {EmbedPlayerSource} from '#/lib/strings/embed-player'
import {GalleryModel} from '#/state/models/media/gallery'
import {ImageModel} from '#/state/models/media/image'
import {ThreadgateSetting} from '../queries/threadgate' import {ThreadgateSetting} from '../queries/threadgate'
export interface EditProfileModal { export interface EditProfileModal {
@ -118,10 +118,6 @@ export interface ChangePasswordModal {
name: 'change-password' name: 'change-password'
} }
export interface SwitchAccountModal {
name: 'switch-account'
}
export interface LinkWarningModal { export interface LinkWarningModal {
name: 'link-warning' name: 'link-warning'
text: string text: string
@ -148,7 +144,6 @@ export type Modal =
| VerifyEmailModal | VerifyEmailModal
| ChangeEmailModal | ChangeEmailModal
| ChangePasswordModal | ChangePasswordModal
| SwitchAccountModal
// Curation // Curation
| ContentLanguagesSettingsModal | ContentLanguagesSettingsModal

View File

@ -24,7 +24,6 @@ import * as LinkWarningModal from './LinkWarning'
import * as ListAddUserModal from './ListAddRemoveUsers' import * as ListAddUserModal from './ListAddRemoveUsers'
import * as RepostModal from './Repost' import * as RepostModal from './Repost'
import * as SelfLabelModal from './SelfLabel' import * as SelfLabelModal from './SelfLabel'
import * as SwitchAccountModal from './SwitchAccount'
import * as ThreadgateModal from './Threadgate' import * as ThreadgateModal from './Threadgate'
import * as UserAddRemoveListsModal from './UserAddRemoveLists' import * as UserAddRemoveListsModal from './UserAddRemoveLists'
import * as VerifyEmailModal from './VerifyEmail' import * as VerifyEmailModal from './VerifyEmail'
@ -114,9 +113,6 @@ export function ModalsContainer() {
} else if (activeModal?.name === 'change-password') { } else if (activeModal?.name === 'change-password') {
snapPoints = ChangePasswordModal.snapPoints snapPoints = ChangePasswordModal.snapPoints
element = <ChangePasswordModal.Component /> element = <ChangePasswordModal.Component />
} else if (activeModal?.name === 'switch-account') {
snapPoints = SwitchAccountModal.snapPoints
element = <SwitchAccountModal.Component />
} else if (activeModal?.name === 'link-warning') { } else if (activeModal?.name === 'link-warning') {
snapPoints = LinkWarningModal.snapPoints snapPoints = LinkWarningModal.snapPoints
element = <LinkWarningModal.Component {...activeModal} /> element = <LinkWarningModal.Component {...activeModal} />

View File

@ -1,169 +0,0 @@
import React from 'react'
import {
ActivityIndicator,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native'
import {BottomSheetScrollView} from '@discord/bottom-sheet/src'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useProfileQuery} from '#/state/queries/profile'
import {SessionAccount, useSession, useSessionApi} from '#/state/session'
import {useCloseAllActiveElements} from '#/state/util'
import {useAnalytics} from 'lib/analytics/analytics'
import {Haptics} from 'lib/haptics'
import {useAccountSwitcher} from 'lib/hooks/useAccountSwitcher'
import {usePalette} from 'lib/hooks/usePalette'
import {makeProfileLink} from 'lib/routes/links'
import {s} from 'lib/styles'
import {AccountDropdownBtn} from '../util/AccountDropdownBtn'
import {Link} from '../util/Link'
import {Text} from '../util/text/Text'
import {UserAvatar} from '../util/UserAvatar'
export const snapPoints = ['40%', '90%']
function SwitchAccountCard({account}: {account: SessionAccount}) {
const pal = usePalette('default')
const {_} = useLingui()
const {track} = useAnalytics()
const {isSwitchingAccounts, currentAccount} = useSession()
const {logout} = useSessionApi()
const {data: profile} = useProfileQuery({did: account.did})
const isCurrentAccount = account.did === currentAccount?.did
const {onPressSwitchAccount} = useAccountSwitcher()
const closeAllActiveElements = useCloseAllActiveElements()
const onPressSignout = React.useCallback(() => {
track('Settings:SignOutButtonClicked')
closeAllActiveElements()
// needs to be in timeout or the modal re-opens
setTimeout(() => logout('SwitchAccount'), 0)
}, [track, logout, closeAllActiveElements])
const contents = (
<View style={[pal.view, styles.linkCard]}>
<View style={styles.avi}>
<UserAvatar
size={40}
avatar={profile?.avatar}
type={profile?.associated?.labeler ? 'labeler' : 'user'}
/>
</View>
<View style={[s.flex1]}>
<Text type="md-bold" style={pal.text} numberOfLines={1}>
{profile?.displayName || account?.handle}
</Text>
<Text type="sm" style={pal.textLight} numberOfLines={1}>
{account?.handle}
</Text>
</View>
{isCurrentAccount ? (
<TouchableOpacity
testID="signOutBtn"
onPress={isSwitchingAccounts ? undefined : onPressSignout}
accessibilityRole="button"
accessibilityLabel={_(msg`Sign out`)}
accessibilityHint={_(
msg`Signs ${profile?.displayName} out of Bluesky`,
)}>
<Text type="lg" style={pal.link}>
<Trans>Sign out</Trans>
</Text>
</TouchableOpacity>
) : (
<AccountDropdownBtn account={account} />
)}
</View>
)
return isCurrentAccount ? (
<Link
href={makeProfileLink({
did: currentAccount.did,
handle: currentAccount.handle,
})}
title={_(msg`Your profile`)}
noFeedback>
{contents}
</Link>
) : (
<TouchableOpacity
testID={`switchToAccountBtn-${account.handle}`}
key={account.did}
style={[isSwitchingAccounts && styles.dimmed]}
onPress={
isSwitchingAccounts
? undefined
: () => onPressSwitchAccount(account, 'SwitchAccount')
}
accessibilityRole="button"
accessibilityLabel={_(msg`Switch to ${account.handle}`)}
accessibilityHint={_(msg`Switches the account you are logged in to`)}>
{contents}
</TouchableOpacity>
)
}
export function Component({}: {}) {
const pal = usePalette('default')
const {isSwitchingAccounts, currentAccount, accounts} = useSession()
React.useEffect(() => {
Haptics.default()
})
return (
<BottomSheetScrollView
style={[styles.container, pal.view]}
contentContainerStyle={[styles.innerContainer, pal.view]}>
<Text type="title-xl" style={[styles.title, pal.text]}>
<Trans>Switch Account</Trans>
</Text>
{isSwitchingAccounts || !currentAccount ? (
<View style={[pal.view, styles.linkCard]}>
<ActivityIndicator />
</View>
) : (
<SwitchAccountCard account={currentAccount} />
)}
{accounts
.filter(a => a.did !== currentAccount?.did)
.map(account => (
<SwitchAccountCard key={account.did} account={account} />
))}
</BottomSheetScrollView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
innerContainer: {
paddingBottom: 40,
},
title: {
textAlign: 'center',
marginTop: 12,
marginBottom: 12,
},
linkCard: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 18,
marginBottom: 1,
},
avi: {
marginRight: 12,
},
dimmed: {
opacity: 0.5,
},
})

View File

@ -1,47 +1,49 @@
import React, {ComponentProps} from 'react' import React, {ComponentProps} from 'react'
import {GestureResponderEvent, TouchableOpacity, View} from 'react-native' import {GestureResponderEvent, TouchableOpacity, View} from 'react-native'
import Animated from 'react-native-reanimated' import Animated from 'react-native-reanimated'
import {StackActions} from '@react-navigation/native'
import {BottomTabBarProps} from '@react-navigation/bottom-tabs'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {Text} from 'view/com/util/text/Text' import {msg, Trans} from '@lingui/macro'
import {useAnalytics} from 'lib/analytics/analytics' import {useLingui} from '@lingui/react'
import {clamp} from 'lib/numbers' import {BottomTabBarProps} from '@react-navigation/bottom-tabs'
import {StackActions} from '@react-navigation/native'
import {useAnalytics} from '#/lib/analytics/analytics'
import {Haptics} from '#/lib/haptics'
import {useDedupe} from '#/lib/hooks/useDedupe'
import {useMinimalShellMode} from '#/lib/hooks/useMinimalShellMode'
import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
import {usePalette} from '#/lib/hooks/usePalette'
import { import {
BellIcon,
BellIconSolid,
HashtagIcon,
HomeIcon, HomeIcon,
HomeIconSolid, HomeIconSolid,
MagnifyingGlassIcon2, MagnifyingGlassIcon2,
MagnifyingGlassIcon2Solid, MagnifyingGlassIcon2Solid,
HashtagIcon, } from '#/lib/icons'
BellIcon, import {clamp} from '#/lib/numbers'
BellIconSolid, import {getTabState, TabState} from '#/lib/routes/helpers'
} from 'lib/icons' import {s} from '#/lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {getTabState, TabState} from 'lib/routes/helpers'
import {styles} from './BottomBarStyles'
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
import {useNavigationTabState} from 'lib/hooks/useNavigationTabState'
import {UserAvatar} from 'view/com/util/UserAvatar'
import {useLingui} from '@lingui/react'
import {msg, Trans} from '@lingui/macro'
import {useModalControls} from '#/state/modals'
import {useShellLayout} from '#/state/shell/shell-layout'
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {emitSoftReset} from '#/state/events' import {emitSoftReset} from '#/state/events'
import {useSession} from '#/state/session' import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {useProfileQuery} from '#/state/queries/profile' import {useProfileQuery} from '#/state/queries/profile'
import {useSession} from '#/state/session'
import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useShellLayout} from '#/state/shell/shell-layout'
import {useCloseAllActiveElements} from '#/state/util' import {useCloseAllActiveElements} from '#/state/util'
import {Button} from '#/view/com/util/forms/Button' import {Button} from '#/view/com/util/forms/Button'
import {s} from 'lib/styles' import {Text} from '#/view/com/util/text/Text'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {Logo} from '#/view/icons/Logo' import {Logo} from '#/view/icons/Logo'
import {Logotype} from '#/view/icons/Logotype' import {Logotype} from '#/view/icons/Logotype'
import {useDedupe} from 'lib/hooks/useDedupe' import {useDialogControl} from '#/components/Dialog'
import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount'
import {styles} from './BottomBarStyles'
type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds' type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds'
export function BottomBar({navigation}: BottomTabBarProps) { export function BottomBar({navigation}: BottomTabBarProps) {
const {openModal} = useModalControls()
const {hasSession, currentAccount} = useSession() const {hasSession, currentAccount} = useSession()
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui() const {_} = useLingui()
@ -56,6 +58,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
const {requestSwitchToAccount} = useLoggedOutViewControls() const {requestSwitchToAccount} = useLoggedOutViewControls()
const closeAllActiveElements = useCloseAllActiveElements() const closeAllActiveElements = useCloseAllActiveElements()
const dedupe = useDedupe() const dedupe = useDedupe()
const accountSwitchControl = useDialogControl()
const showSignIn = React.useCallback(() => { const showSignIn = React.useCallback(() => {
closeAllActiveElements() closeAllActiveElements()
@ -99,11 +102,16 @@ export function BottomBar({navigation}: BottomTabBarProps) {
const onPressProfile = React.useCallback(() => { const onPressProfile = React.useCallback(() => {
onPressTab('MyProfile') onPressTab('MyProfile')
}, [onPressTab]) }, [onPressTab])
const onLongPressProfile = React.useCallback(() => { const onLongPressProfile = React.useCallback(() => {
openModal({name: 'switch-account'}) Haptics.default()
}, [openModal]) accountSwitchControl.open()
}, [accountSwitchControl])
return ( return (
<>
<SwitchAccountDialog control={accountSwitchControl} />
<Animated.View <Animated.View
style={[ style={[
styles.bottomBar, styles.bottomBar,
@ -233,7 +241,8 @@ export function BottomBar({navigation}: BottomTabBarProps) {
/> />
</View> </View>
) : ( ) : (
<View style={[styles.ctrlIcon, pal.text, styles.profileIcon]}> <View
style={[styles.ctrlIcon, pal.text, styles.profileIcon]}>
<UserAvatar <UserAvatar
avatar={profile?.avatar} avatar={profile?.avatar}
size={28} size={28}
@ -266,14 +275,16 @@ export function BottomBar({navigation}: BottomTabBarProps) {
paddingRight: 6, paddingRight: 6,
gap: 8, gap: 8,
}}> }}>
<View style={{flexDirection: 'row', alignItems: 'center', gap: 8}}> <View
style={{flexDirection: 'row', alignItems: 'center', gap: 8}}>
<Logo width={28} /> <Logo width={28} />
<View style={{paddingTop: 4}}> <View style={{paddingTop: 4}}>
<Logotype width={80} fill={pal.text.color} /> <Logotype width={80} fill={pal.text.color} />
</View> </View>
</View> </View>
<View style={{flexDirection: 'row', alignItems: 'center', gap: 4}}> <View
style={{flexDirection: 'row', alignItems: 'center', gap: 4}}>
<Button <Button
onPress={showCreateAccount} onPress={showCreateAccount}
accessibilityHint={_(msg`Sign up`)} accessibilityHint={_(msg`Sign up`)}
@ -297,6 +308,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
</> </>
)} )}
</Animated.View> </Animated.View>
</>
) )
} }