diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/modals/Modal.tsx | 4 | ||||
-rw-r--r-- | src/view/com/modals/SwitchAccount.tsx | 169 | ||||
-rw-r--r-- | src/view/shell/bottom-bar/BottomBar.tsx | 440 |
3 files changed, 226 insertions, 387 deletions
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index af86f13a3..85ffccf12 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -24,7 +24,6 @@ import * as LinkWarningModal from './LinkWarning' import * as ListAddUserModal from './ListAddRemoveUsers' import * as RepostModal from './Repost' import * as SelfLabelModal from './SelfLabel' -import * as SwitchAccountModal from './SwitchAccount' import * as ThreadgateModal from './Threadgate' import * as UserAddRemoveListsModal from './UserAddRemoveLists' import * as VerifyEmailModal from './VerifyEmail' @@ -114,9 +113,6 @@ export function ModalsContainer() { } else if (activeModal?.name === 'change-password') { snapPoints = ChangePasswordModal.snapPoints element = <ChangePasswordModal.Component /> - } else if (activeModal?.name === 'switch-account') { - snapPoints = SwitchAccountModal.snapPoints - element = <SwitchAccountModal.Component /> } else if (activeModal?.name === 'link-warning') { snapPoints = LinkWarningModal.snapPoints element = <LinkWarningModal.Component {...activeModal} /> diff --git a/src/view/com/modals/SwitchAccount.tsx b/src/view/com/modals/SwitchAccount.tsx deleted file mode 100644 index 03bef719e..000000000 --- a/src/view/com/modals/SwitchAccount.tsx +++ /dev/null @@ -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, - }, -}) diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index 8a19a0b4f..f41631a96 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -1,47 +1,49 @@ import React, {ComponentProps} from 'react' import {GestureResponderEvent, TouchableOpacity, View} from 'react-native' 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 {Text} from 'view/com/util/text/Text' -import {useAnalytics} from 'lib/analytics/analytics' -import {clamp} from 'lib/numbers' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +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 { + BellIcon, + BellIconSolid, + HashtagIcon, HomeIcon, HomeIconSolid, MagnifyingGlassIcon2, MagnifyingGlassIcon2Solid, - HashtagIcon, - BellIcon, - BellIconSolid, -} from 'lib/icons' -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' +} from '#/lib/icons' +import {clamp} from '#/lib/numbers' +import {getTabState, TabState} from '#/lib/routes/helpers' +import {s} from '#/lib/styles' import {emitSoftReset} from '#/state/events' -import {useSession} from '#/state/session' +import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useProfileQuery} from '#/state/queries/profile' +import {useSession} from '#/state/session' import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import {useShellLayout} from '#/state/shell/shell-layout' import {useCloseAllActiveElements} from '#/state/util' 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 {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' export function BottomBar({navigation}: BottomTabBarProps) { - const {openModal} = useModalControls() const {hasSession, currentAccount} = useSession() const pal = usePalette('default') const {_} = useLingui() @@ -56,6 +58,7 @@ export function BottomBar({navigation}: BottomTabBarProps) { const {requestSwitchToAccount} = useLoggedOutViewControls() const closeAllActiveElements = useCloseAllActiveElements() const dedupe = useDedupe() + const accountSwitchControl = useDialogControl() const showSignIn = React.useCallback(() => { closeAllActiveElements() @@ -99,204 +102,213 @@ export function BottomBar({navigation}: BottomTabBarProps) { const onPressProfile = React.useCallback(() => { onPressTab('MyProfile') }, [onPressTab]) + const onLongPressProfile = React.useCallback(() => { - openModal({name: 'switch-account'}) - }, [openModal]) + Haptics.default() + accountSwitchControl.open() + }, [accountSwitchControl]) return ( - <Animated.View - style={[ - styles.bottomBar, - pal.view, - pal.border, - {paddingBottom: clamp(safeAreaInsets.bottom, 15, 30)}, - footerMinimalShellTransform, - ]} - onLayout={e => { - footerHeight.value = e.nativeEvent.layout.height - }}> - {hasSession ? ( - <> - <Btn - testID="bottomBarHomeBtn" - icon={ - isAtHome ? ( - <HomeIconSolid - strokeWidth={4} - size={24} - style={[styles.ctrlIcon, pal.text, styles.homeIcon]} - /> - ) : ( - <HomeIcon - strokeWidth={4} - size={24} - style={[styles.ctrlIcon, pal.text, styles.homeIcon]} - /> - ) - } - onPress={onPressHome} - accessibilityRole="tab" - accessibilityLabel={_(msg`Home`)} - accessibilityHint="" - /> - <Btn - testID="bottomBarSearchBtn" - icon={ - isAtSearch ? ( - <MagnifyingGlassIcon2Solid - size={25} - style={[styles.ctrlIcon, pal.text, styles.searchIcon]} - strokeWidth={1.8} - /> - ) : ( - <MagnifyingGlassIcon2 - size={25} - style={[styles.ctrlIcon, pal.text, styles.searchIcon]} - strokeWidth={1.8} - /> - ) - } - onPress={onPressSearch} - accessibilityRole="search" - accessibilityLabel={_(msg`Search`)} - accessibilityHint="" - /> - <Btn - testID="bottomBarFeedsBtn" - icon={ - isAtFeeds ? ( - <HashtagIcon - size={24} - style={[styles.ctrlIcon, pal.text, styles.feedsIcon]} - strokeWidth={4} - /> - ) : ( - <HashtagIcon - size={24} - style={[styles.ctrlIcon, pal.text, styles.feedsIcon]} - strokeWidth={2.25} - /> - ) - } - onPress={onPressFeeds} - accessibilityRole="tab" - accessibilityLabel={_(msg`Feeds`)} - accessibilityHint="" - /> - <Btn - testID="bottomBarNotificationsBtn" - icon={ - isAtNotifications ? ( - <BellIconSolid - size={24} - strokeWidth={1.9} - style={[styles.ctrlIcon, pal.text, styles.bellIcon]} - /> - ) : ( - <BellIcon - size={24} - strokeWidth={1.9} - style={[styles.ctrlIcon, pal.text, styles.bellIcon]} - /> - ) - } - onPress={onPressNotifications} - notificationCount={numUnreadNotifications} - accessible={true} - accessibilityRole="tab" - accessibilityLabel={_(msg`Notifications`)} - accessibilityHint={ - numUnreadNotifications === '' - ? '' - : `${numUnreadNotifications} unread` - } - /> - <Btn - testID="bottomBarProfileBtn" - icon={ - <View style={styles.ctrlIconSizingWrapper}> - {isAtMyProfile ? ( - <View - style={[ - styles.ctrlIcon, - pal.text, - styles.profileIcon, - styles.onProfile, - {borderColor: pal.text.color}, - ]}> - <UserAvatar - avatar={profile?.avatar} - size={27} - // See https://github.com/bluesky-social/social-app/pull/1801: - usePlainRNImage={true} - type={profile?.associated?.labeler ? 'labeler' : 'user'} - /> - </View> + <> + <SwitchAccountDialog control={accountSwitchControl} /> + + <Animated.View + style={[ + styles.bottomBar, + pal.view, + pal.border, + {paddingBottom: clamp(safeAreaInsets.bottom, 15, 30)}, + footerMinimalShellTransform, + ]} + onLayout={e => { + footerHeight.value = e.nativeEvent.layout.height + }}> + {hasSession ? ( + <> + <Btn + testID="bottomBarHomeBtn" + icon={ + isAtHome ? ( + <HomeIconSolid + strokeWidth={4} + size={24} + style={[styles.ctrlIcon, pal.text, styles.homeIcon]} + /> ) : ( - <View style={[styles.ctrlIcon, pal.text, styles.profileIcon]}> - <UserAvatar - avatar={profile?.avatar} - size={28} - // See https://github.com/bluesky-social/social-app/pull/1801: - usePlainRNImage={true} - type={profile?.associated?.labeler ? 'labeler' : 'user'} - /> - </View> - )} - </View> - } - onPress={onPressProfile} - onLongPress={onLongPressProfile} - accessibilityRole="tab" - accessibilityLabel={_(msg`Profile`)} - accessibilityHint="" - /> - </> - ) : ( - <> - <View - style={{ - width: '100%', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingTop: 14, - paddingBottom: 2, - paddingLeft: 14, - paddingRight: 6, - gap: 8, - }}> - <View style={{flexDirection: 'row', alignItems: 'center', gap: 8}}> - <Logo width={28} /> - <View style={{paddingTop: 4}}> - <Logotype width={80} fill={pal.text.color} /> + <HomeIcon + strokeWidth={4} + size={24} + style={[styles.ctrlIcon, pal.text, styles.homeIcon]} + /> + ) + } + onPress={onPressHome} + accessibilityRole="tab" + accessibilityLabel={_(msg`Home`)} + accessibilityHint="" + /> + <Btn + testID="bottomBarSearchBtn" + icon={ + isAtSearch ? ( + <MagnifyingGlassIcon2Solid + size={25} + style={[styles.ctrlIcon, pal.text, styles.searchIcon]} + strokeWidth={1.8} + /> + ) : ( + <MagnifyingGlassIcon2 + size={25} + style={[styles.ctrlIcon, pal.text, styles.searchIcon]} + strokeWidth={1.8} + /> + ) + } + onPress={onPressSearch} + accessibilityRole="search" + accessibilityLabel={_(msg`Search`)} + accessibilityHint="" + /> + <Btn + testID="bottomBarFeedsBtn" + icon={ + isAtFeeds ? ( + <HashtagIcon + size={24} + style={[styles.ctrlIcon, pal.text, styles.feedsIcon]} + strokeWidth={4} + /> + ) : ( + <HashtagIcon + size={24} + style={[styles.ctrlIcon, pal.text, styles.feedsIcon]} + strokeWidth={2.25} + /> + ) + } + onPress={onPressFeeds} + accessibilityRole="tab" + accessibilityLabel={_(msg`Feeds`)} + accessibilityHint="" + /> + <Btn + testID="bottomBarNotificationsBtn" + icon={ + isAtNotifications ? ( + <BellIconSolid + size={24} + strokeWidth={1.9} + style={[styles.ctrlIcon, pal.text, styles.bellIcon]} + /> + ) : ( + <BellIcon + size={24} + strokeWidth={1.9} + style={[styles.ctrlIcon, pal.text, styles.bellIcon]} + /> + ) + } + onPress={onPressNotifications} + notificationCount={numUnreadNotifications} + accessible={true} + accessibilityRole="tab" + accessibilityLabel={_(msg`Notifications`)} + accessibilityHint={ + numUnreadNotifications === '' + ? '' + : `${numUnreadNotifications} unread` + } + /> + <Btn + testID="bottomBarProfileBtn" + icon={ + <View style={styles.ctrlIconSizingWrapper}> + {isAtMyProfile ? ( + <View + style={[ + styles.ctrlIcon, + pal.text, + styles.profileIcon, + styles.onProfile, + {borderColor: pal.text.color}, + ]}> + <UserAvatar + avatar={profile?.avatar} + size={27} + // See https://github.com/bluesky-social/social-app/pull/1801: + usePlainRNImage={true} + type={profile?.associated?.labeler ? 'labeler' : 'user'} + /> + </View> + ) : ( + <View + style={[styles.ctrlIcon, pal.text, styles.profileIcon]}> + <UserAvatar + avatar={profile?.avatar} + size={28} + // See https://github.com/bluesky-social/social-app/pull/1801: + usePlainRNImage={true} + type={profile?.associated?.labeler ? 'labeler' : 'user'} + /> + </View> + )} + </View> + } + onPress={onPressProfile} + onLongPress={onLongPressProfile} + accessibilityRole="tab" + accessibilityLabel={_(msg`Profile`)} + accessibilityHint="" + /> + </> + ) : ( + <> + <View + style={{ + width: '100%', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingTop: 14, + paddingBottom: 2, + paddingLeft: 14, + paddingRight: 6, + gap: 8, + }}> + <View + style={{flexDirection: 'row', alignItems: 'center', gap: 8}}> + <Logo width={28} /> + <View style={{paddingTop: 4}}> + <Logotype width={80} fill={pal.text.color} /> + </View> </View> - </View> - <View style={{flexDirection: 'row', alignItems: 'center', gap: 4}}> - <Button - onPress={showCreateAccount} - accessibilityHint={_(msg`Sign up`)} - accessibilityLabel={_(msg`Sign up`)}> - <Text type="md" style={[{color: 'white'}, s.bold]}> - <Trans>Sign up</Trans> - </Text> - </Button> + <View + style={{flexDirection: 'row', alignItems: 'center', gap: 4}}> + <Button + onPress={showCreateAccount} + accessibilityHint={_(msg`Sign up`)} + accessibilityLabel={_(msg`Sign up`)}> + <Text type="md" style={[{color: 'white'}, s.bold]}> + <Trans>Sign up</Trans> + </Text> + </Button> - <Button - type="default" - onPress={showSignIn} - accessibilityHint={_(msg`Sign in`)} - accessibilityLabel={_(msg`Sign in`)}> - <Text type="md" style={[pal.text, s.bold]}> - <Trans>Sign in</Trans> - </Text> - </Button> + <Button + type="default" + onPress={showSignIn} + accessibilityHint={_(msg`Sign in`)} + accessibilityLabel={_(msg`Sign in`)}> + <Text type="md" style={[pal.text, s.bold]}> + <Trans>Sign in</Trans> + </Text> + </Button> + </View> </View> - </View> - </> - )} - </Animated.View> + </> + )} + </Animated.View> + </> ) } |