about summary refs log tree commit diff
path: root/src/view/shell/desktop/LeftNav.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/shell/desktop/LeftNav.tsx')
-rw-r--r--src/view/shell/desktop/LeftNav.tsx295
1 files changed, 169 insertions, 126 deletions
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index 39271605c..2ed294501 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -1,5 +1,4 @@
 import React from 'react'
-import {observer} from 'mobx-react-lite'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import {PressableWithHover} from 'view/com/util/PressableWithHover'
 import {
@@ -16,7 +15,6 @@ import {UserAvatar} from 'view/com/util/UserAvatar'
 import {Link} from 'view/com/util/Link'
 import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder'
 import {usePalette} from 'lib/hooks/usePalette'
-import {useStores} from 'state/index'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {s, colors} from 'lib/styles'
 import {
@@ -39,18 +37,36 @@ import {getCurrentRoute, isTab, isStateAtTabRoot} from 'lib/routes/helpers'
 import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types'
 import {router} from '../../../routes'
 import {makeProfileLink} from 'lib/routes/links'
+import {useLingui} from '@lingui/react'
+import {Trans, msg} from '@lingui/macro'
+import {useProfileQuery} from '#/state/queries/profile'
+import {useSession} from '#/state/session'
+import {useUnreadNotifications} from '#/state/queries/notifications/unread'
+import {useComposerControls} from '#/state/shell/composer'
+import {useFetchHandle} from '#/state/queries/handle'
+import {emitSoftReset} from '#/state/events'
+import {useQueryClient} from '@tanstack/react-query'
+import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
+import {NavSignupCard} from '#/view/shell/NavSignupCard'
+import {truncateAndInvalidate} from '#/state/queries/util'
 
-const ProfileCard = observer(function ProfileCardImpl() {
-  const store = useStores()
+function ProfileCard() {
+  const {currentAccount} = useSession()
+  const {isLoading, data: profile} = useProfileQuery({did: currentAccount!.did})
   const {isDesktop} = useWebMediaQueries()
+  const {_} = useLingui()
   const size = 48
-  return store.me.handle ? (
+
+  return !isLoading && profile ? (
     <Link
-      href={makeProfileLink(store.me)}
+      href={makeProfileLink({
+        did: currentAccount!.did,
+        handle: currentAccount!.handle,
+      })}
       style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}
-      title="My Profile"
+      title={_(msg`My Profile`)}
       asAnchor>
-      <UserAvatar avatar={store.me.avatar} size={size} />
+      <UserAvatar avatar={profile.avatar} size={size} />
     </Link>
   ) : (
     <View style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}>
@@ -61,12 +77,13 @@ const ProfileCard = observer(function ProfileCardImpl() {
       />
     </View>
   )
-})
+}
 
 function BackBtn() {
   const {isTablet} = useWebMediaQueries()
   const pal = usePalette('default')
   const navigation = useNavigation<NavigationProp>()
+  const {_} = useLingui()
   const shouldShow = useNavigationState(state => !isStateAtTabRoot(state))
 
   const onPressBack = React.useCallback(() => {
@@ -86,7 +103,7 @@ function BackBtn() {
       onPress={onPressBack}
       style={styles.backBtn}
       accessibilityRole="button"
-      accessibilityLabel="Go back"
+      accessibilityLabel={_(msg`Go back`)}
       accessibilityHint="">
       <FontAwesomeIcon
         size={24}
@@ -104,15 +121,10 @@ interface NavItemProps {
   iconFilled: JSX.Element
   label: string
 }
-const NavItem = observer(function NavItemImpl({
-  count,
-  href,
-  icon,
-  iconFilled,
-  label,
-}: NavItemProps) {
+function NavItem({count, href, icon, iconFilled, label}: NavItemProps) {
   const pal = usePalette('default')
-  const store = useStores()
+  const queryClient = useQueryClient()
+  const {currentAccount} = useSession()
   const {isDesktop, isTablet} = useWebMediaQueries()
   const [pathName] = React.useMemo(() => router.matchPath(href), [href])
   const currentRouteInfo = useNavigationState(state => {
@@ -125,7 +137,7 @@ const NavItem = observer(function NavItemImpl({
     currentRouteInfo.name === 'Profile'
       ? isTab(currentRouteInfo.name, pathName) &&
         (currentRouteInfo.params as CommonNavigatorParams['Profile']).name ===
-          store.me.handle
+          currentAccount?.handle
       : isTab(currentRouteInfo.name, pathName)
   const {onPress} = useLinkProps({to: href})
   const onPressWrapped = React.useCallback(
@@ -135,12 +147,16 @@ const NavItem = observer(function NavItemImpl({
       }
       e.preventDefault()
       if (isCurrent) {
-        store.emitScreenSoftReset()
+        emitSoftReset()
       } else {
+        if (href === '/notifications') {
+          // fetch new notifs on view
+          truncateAndInvalidate(queryClient, NOTIFS_RQKEY())
+        }
         onPress()
       }
     },
-    [onPress, isCurrent, store],
+    [onPress, isCurrent, queryClient, href],
   )
 
   return (
@@ -179,12 +195,16 @@ const NavItem = observer(function NavItemImpl({
       )}
     </PressableWithHover>
   )
-})
+}
 
 function ComposeBtn() {
-  const store = useStores()
+  const {currentAccount} = useSession()
   const {getState} = useNavigation()
+  const {openComposer} = useComposerControls()
+  const {_} = useLingui()
   const {isTablet} = useWebMediaQueries()
+  const [isFetchingHandle, setIsFetchingHandle] = React.useState(false)
+  const fetchHandle = useFetchHandle()
 
   const getProfileHandle = async () => {
     const {routes} = getState()
@@ -196,13 +216,21 @@ function ComposeBtn() {
       ).name
 
       if (handle.startsWith('did:')) {
-        const cached = await store.profiles.cache.get(handle)
-        const profile = cached ? cached.data : undefined
-        // if we can't resolve handle, set to undefined
-        handle = profile?.handle || undefined
+        try {
+          setIsFetchingHandle(true)
+          handle = await fetchHandle(handle)
+        } catch (e) {
+          handle = undefined
+        } finally {
+          setIsFetchingHandle(false)
+        }
       }
 
-      if (!handle || handle === store.me.handle || handle === 'handle.invalid')
+      if (
+        !handle ||
+        handle === currentAccount?.handle ||
+        handle === 'handle.invalid'
+      )
         return undefined
 
       return handle
@@ -212,17 +240,18 @@ function ComposeBtn() {
   }
 
   const onPressCompose = async () =>
-    store.shell.openComposer({mention: await getProfileHandle()})
+    openComposer({mention: await getProfileHandle()})
 
   if (isTablet) {
     return null
   }
   return (
     <TouchableOpacity
+      disabled={isFetchingHandle}
       style={[styles.newPostBtn]}
       onPress={onPressCompose}
       accessibilityRole="button"
-      accessibilityLabel="New post"
+      accessibilityLabel={_(msg`New post`)}
       accessibilityHint="">
       <View style={styles.newPostBtnIconWrapper}>
         <ComposeIcon2
@@ -232,16 +261,18 @@ function ComposeBtn() {
         />
       </View>
       <Text type="button" style={styles.newPostBtnLabel}>
-        New Post
+        <Trans>New Post</Trans>
       </Text>
     </TouchableOpacity>
   )
 }
 
-export const DesktopLeftNav = observer(function DesktopLeftNav() {
-  const store = useStores()
+export function DesktopLeftNav() {
+  const {hasSession, currentAccount} = useSession()
   const pal = usePalette('default')
+  const {_} = useLingui()
   const {isDesktop, isTablet} = useWebMediaQueries()
+  const numUnread = useUnreadNotifications()
 
   return (
     <View
@@ -251,8 +282,16 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
         pal.view,
         pal.border,
       ]}>
-      {store.session.hasSession && <ProfileCard />}
+      {hasSession ? (
+        <ProfileCard />
+      ) : isDesktop ? (
+        <View style={{paddingHorizontal: 12}}>
+          <NavSignupCard />
+        </View>
+      ) : null}
+
       <BackBtn />
+
       <NavItem
         href="/"
         icon={<HomeIcon size={isDesktop ? 24 : 28} style={pal.text} />}
@@ -263,7 +302,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
             style={pal.text}
           />
         }
-        label="Home"
+        label={_(msg`Home`)}
       />
       <NavItem
         href="/search"
@@ -281,7 +320,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
             style={pal.text}
           />
         }
-        label="Search"
+        label={_(msg`Search`)}
       />
       <NavItem
         href="/feeds"
@@ -299,105 +338,109 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
             size={isDesktop ? 24 : 28}
           />
         }
-        label="Feeds"
+        label={_(msg`Feeds`)}
       />
-      <NavItem
-        href="/notifications"
-        count={store.me.notifications.unreadCountLabel}
-        icon={
-          <BellIcon
-            strokeWidth={2}
-            size={isDesktop ? 24 : 26}
-            style={pal.text}
+
+      {hasSession && (
+        <>
+          <NavItem
+            href="/notifications"
+            count={numUnread}
+            icon={
+              <BellIcon
+                strokeWidth={2}
+                size={isDesktop ? 24 : 26}
+                style={pal.text}
+              />
+            }
+            iconFilled={
+              <BellIconSolid
+                strokeWidth={1.5}
+                size={isDesktop ? 24 : 26}
+                style={pal.text}
+              />
+            }
+            label={_(msg`Notifications`)}
           />
-        }
-        iconFilled={
-          <BellIconSolid
-            strokeWidth={1.5}
-            size={isDesktop ? 24 : 26}
-            style={pal.text}
+          <NavItem
+            href="/lists"
+            icon={
+              <ListIcon
+                style={pal.text}
+                size={isDesktop ? 26 : 30}
+                strokeWidth={2}
+              />
+            }
+            iconFilled={
+              <ListIcon
+                style={pal.text}
+                size={isDesktop ? 26 : 30}
+                strokeWidth={3}
+              />
+            }
+            label={_(msg`Lists`)}
           />
-        }
-        label="Notifications"
-      />
-      <NavItem
-        href="/lists"
-        icon={
-          <ListIcon
-            style={pal.text}
-            size={isDesktop ? 26 : 30}
-            strokeWidth={2}
+          <NavItem
+            href="/moderation"
+            icon={
+              <HandIcon
+                style={pal.text}
+                size={isDesktop ? 24 : 27}
+                strokeWidth={5.5}
+              />
+            }
+            iconFilled={
+              <FontAwesomeIcon
+                icon="hand"
+                style={pal.text as FontAwesomeIconStyle}
+                size={isDesktop ? 20 : 26}
+              />
+            }
+            label={_(msg`Moderation`)}
           />
-        }
-        iconFilled={
-          <ListIcon
-            style={pal.text}
-            size={isDesktop ? 26 : 30}
-            strokeWidth={3}
+          <NavItem
+            href={currentAccount ? makeProfileLink(currentAccount) : '/'}
+            icon={
+              <UserIcon
+                strokeWidth={1.75}
+                size={isDesktop ? 28 : 30}
+                style={pal.text}
+              />
+            }
+            iconFilled={
+              <UserIconSolid
+                strokeWidth={1.75}
+                size={isDesktop ? 28 : 30}
+                style={pal.text}
+              />
+            }
+            label="Profile"
           />
-        }
-        label="Lists"
-      />
-      <NavItem
-        href="/moderation"
-        icon={
-          <HandIcon
-            style={pal.text}
-            size={isDesktop ? 24 : 27}
-            strokeWidth={5.5}
+          <NavItem
+            href="/settings"
+            icon={
+              <CogIcon
+                strokeWidth={1.75}
+                size={isDesktop ? 28 : 32}
+                style={pal.text}
+              />
+            }
+            iconFilled={
+              <CogIconSolid
+                strokeWidth={1.5}
+                size={isDesktop ? 28 : 32}
+                style={pal.text}
+              />
+            }
+            label={_(msg`Settings`)}
           />
-        }
-        iconFilled={
-          <FontAwesomeIcon
-            icon="hand"
-            style={pal.text as FontAwesomeIconStyle}
-            size={isDesktop ? 20 : 26}
-          />
-        }
-        label="Moderation"
-      />
-      {store.session.hasSession && (
-        <NavItem
-          href={makeProfileLink(store.me)}
-          icon={
-            <UserIcon
-              strokeWidth={1.75}
-              size={isDesktop ? 28 : 30}
-              style={pal.text}
-            />
-          }
-          iconFilled={
-            <UserIconSolid
-              strokeWidth={1.75}
-              size={isDesktop ? 28 : 30}
-              style={pal.text}
-            />
-          }
-          label="Profile"
-        />
+
+          <ComposeBtn />
+        </>
       )}
-      <NavItem
-        href="/settings"
-        icon={
-          <CogIcon
-            strokeWidth={1.75}
-            size={isDesktop ? 28 : 32}
-            style={pal.text}
-          />
-        }
-        iconFilled={
-          <CogIconSolid
-            strokeWidth={1.5}
-            size={isDesktop ? 28 : 32}
-            style={pal.text}
-          />
-        }
-        label="Settings"
-      />
-      {store.session.hasSession && <ComposeBtn />}
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
   leftNav: {