about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
Diffstat (limited to 'src/view')
-rw-r--r--src/view/com/modals/Modal.tsx4
-rw-r--r--src/view/com/modals/SwitchAccount.tsx169
-rw-r--r--src/view/shell/bottom-bar/BottomBar.tsx440
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>
+    </>
   )
 }