about summary refs log tree commit diff
path: root/src/view/shell
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-02-25 09:20:37 -0800
committerGitHub <noreply@github.com>2025-02-25 09:20:37 -0800
commitcc8369e868ec684120a220dfa66845dad324b4a1 (patch)
tree9a5abaaa526644d16785e3a530a16010ab2b0107 /src/view/shell
parent04378386494fac38002fa7459464952040b58d55 (diff)
downloadvoidsky-cc8369e868ec684120a220dfa66845dad324b4a1.tar.zst
Better tablet layout (#7656)
* better tablet layout

* adjust left nav spacing

* add right nav to pwi

* clearer logic

* fix a couple screens that don't need the tablet layout

* fix horiz scroll bar

* fix double trending

* fix ts-ignore

* fix labeller screen

* don't offset things within dialogs

* fix load latest button (and add scale animation)

* center loader on home screen

* adjust break points

* adjust left nav spacing

* fix load latest btn (again)

* add lang select to right nav if left nav is minimal

* fix double scrollbar on tiny screens

* fix scrollbar

* fix type errors
Diffstat (limited to 'src/view/shell')
-rw-r--r--src/view/shell/createNativeStackNavigatorWithAuth.tsx7
-rw-r--r--src/view/shell/desktop/LeftNav.tsx86
-rw-r--r--src/view/shell/desktop/RightNav.tsx36
3 files changed, 66 insertions, 63 deletions
diff --git a/src/view/shell/createNativeStackNavigatorWithAuth.tsx b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
index 35a46b427..018ff3d97 100644
--- a/src/view/shell/createNativeStackNavigatorWithAuth.tsx
+++ b/src/view/shell/createNativeStackNavigatorWithAuth.tsx
@@ -150,11 +150,10 @@ function NativeStackNavigator({
           descriptors={newDescriptors}
         />
       </View>
-      {isWeb && showBottomBar && <BottomBarWeb />}
-      {isWeb && !showBottomBar && (
+      {isWeb && (
         <>
-          <DesktopLeftNav />
-          <DesktopRightNav routeName={activeRoute.name} />
+          {showBottomBar ? <BottomBarWeb /> : <DesktopLeftNav />}
+          {!isMobile && <DesktopRightNav routeName={activeRoute.name} />}
         </>
       )}
     </NavigationContent>
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index 62fbf5cae..59055c6dc 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -1,7 +1,6 @@
 import React from 'react'
 import {StyleSheet, View} from 'react-native'
 import {AppBskyActorDefs} from '@atproto/api'
-import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
 import {msg, plural, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {
@@ -33,7 +32,7 @@ import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
 import {PressableWithHover} from '#/view/com/util/PressableWithHover'
 import {UserAvatar} from '#/view/com/util/UserAvatar'
 import {NavSignupCard} from '#/view/shell/NavSignupCard'
-import {atoms as a, tokens, useBreakpoints, useTheme} from '#/alf'
+import {atoms as a, tokens, useLayoutBreakpoints, useTheme} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {DialogControlProps} from '#/components/Dialog'
 import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
@@ -86,7 +85,7 @@ function ProfileCard() {
   })
   const profiles = data?.profiles
   const signOutPromptControl = Prompt.usePromptControl()
-  const {gtTablet} = useBreakpoints()
+  const {leftNavMinimal} = useLayoutBreakpoints()
   const {_} = useLingui()
   const t = useTheme()
 
@@ -101,7 +100,7 @@ function ProfileCard() {
     }))
 
   return (
-    <View style={[a.my_md, gtTablet && [a.w_full, a.align_start]]}>
+    <View style={[a.my_md, !leftNavMinimal && [a.w_full, a.align_start]]}>
       {!isLoading && profile ? (
         <Menu.Root>
           <Menu.Trigger label={_(msg`Switch accounts`)}>
@@ -120,7 +119,7 @@ function ProfileCard() {
                     a.align_center,
                     a.flex_row,
                     {gap: 6},
-                    gtTablet && [a.pl_lg, a.pr_md],
+                    !leftNavMinimal && [a.pl_lg, a.pr_md],
                   ]}>
                   <View
                     style={[
@@ -133,8 +132,8 @@ function ProfileCard() {
                       a.z_10,
                       active && {
                         transform: [
-                          {scale: gtTablet ? 2 / 3 : 0.8},
-                          {translateX: gtTablet ? -22 : 0},
+                          {scale: !leftNavMinimal ? 2 / 3 : 0.8},
+                          {translateX: !leftNavMinimal ? -22 : 0},
                         ],
                       },
                     ]}>
@@ -144,7 +143,7 @@ function ProfileCard() {
                       type={profile?.associated?.labeler ? 'labeler' : 'user'}
                     />
                   </View>
-                  {gtTablet && (
+                  {!leftNavMinimal && (
                     <>
                       <View
                         style={[
@@ -197,7 +196,7 @@ function ProfileCard() {
         <LoadingPlaceholder
           width={size}
           height={size}
-          style={[{borderRadius: size}, gtTablet && a.ml_lg]}
+          style={[{borderRadius: size}, !leftNavMinimal && a.ml_lg]}
         />
       )}
       <Prompt.Basic
@@ -307,8 +306,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
   const t = useTheme()
   const {_} = useLingui()
   const {currentAccount} = useSession()
-  const {gtMobile, gtTablet} = useBreakpoints()
-  const isTablet = gtMobile && !gtTablet
+  const {leftNavMinimal} = useLayoutBreakpoints()
   const [pathName] = React.useMemo(() => router.matchPath(href), [href])
   const currentRouteInfo = useNavigationState(state => {
     if (!state) {
@@ -350,9 +348,8 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
         a.transition_color,
       ]}
       hoverStyle={t.atoms.bg_contrast_25}
-      // @ts-ignore the function signature differs on web -prf
+      // @ts-expect-error the function signature differs on web -prf
       onPress={onPressWrapped}
-      // @ts-ignore web only -prf
       href={href}
       dataSet={{noUnderline: 1}}
       role="link"
@@ -367,7 +364,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
             width: 24,
             height: 24,
           },
-          isTablet && {
+          leftNavMinimal && {
             width: 40,
             height: 40,
           },
@@ -407,7 +404,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
                   paddingVertical: 1,
                   minWidth: 16,
                 },
-                isTablet && [
+                leftNavMinimal && [
                   {
                     top: '10%',
                     left: count.length === 1 ? 20 : 16,
@@ -429,7 +426,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
                 right: -1,
                 top: -3,
               },
-              isTablet && {
+              leftNavMinimal && {
                 right: 6,
                 top: 4,
               },
@@ -437,7 +434,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
           />
         ) : null}
       </View>
-      {gtTablet && (
+      {!leftNavMinimal && (
         <Text style={[a.text_xl, isCurrent ? a.font_heavy : a.font_normal]}>
           {label}
         </Text>
@@ -451,7 +448,7 @@ function ComposeBtn() {
   const {getState} = useNavigation()
   const {openComposer} = useComposerControls()
   const {_} = useLingui()
-  const {isTablet} = useWebMediaQueries()
+  const {leftNavMinimal} = useLayoutBreakpoints()
   const [isFetchingHandle, setIsFetchingHandle] = React.useState(false)
   const fetchHandle = useFetchHandle()
 
@@ -491,9 +488,10 @@ function ComposeBtn() {
   const onPressCompose = async () =>
     openComposer({mention: await getProfileHandle()})
 
-  if (isTablet) {
+  if (leftNavMinimal) {
     return null
   }
+
   return (
     <View style={[a.flex_row, a.pl_md, a.pt_xl]}>
       <Button
@@ -541,7 +539,8 @@ export function DesktopLeftNav() {
   const {hasSession, currentAccount} = useSession()
   const pal = usePalette('default')
   const {_} = useLingui()
-  const {isDesktop, isTablet} = useWebMediaQueries()
+  const {isDesktop} = useWebMediaQueries()
+  const {leftNavMinimal, centerColumnOffset} = useLayoutBreakpoints()
   const numUnreadNotifications = useUnreadNotifications()
   const hasHomeBadge = useHomeBadge()
   const gate = useGate()
@@ -556,8 +555,14 @@ export function DesktopLeftNav() {
       style={[
         a.px_xl,
         styles.leftNav,
-        isTablet && styles.leftNavTablet,
-        pal.border,
+        leftNavMinimal && styles.leftNavMinimal,
+        {
+          transform: [
+            {translateX: centerColumnOffset ? -450 : -300},
+            {translateX: '-100%'},
+            ...a.scrollbar_offset.transform,
+          ],
+        },
       ]}>
       {hasSession ? (
         <ProfileCard />
@@ -630,14 +635,14 @@ export function DesktopLeftNav() {
             href="/feeds"
             icon={
               <Hashtag
-                style={pal.text as FontAwesomeIconStyle}
+                style={pal.text}
                 aria-hidden={true}
                 width={NAV_ICON_WIDTH}
               />
             }
             iconFilled={
               <HashtagFilled
-                style={pal.text as FontAwesomeIconStyle}
+                style={pal.text}
                 aria-hidden={true}
                 width={NAV_ICON_WIDTH}
               />
@@ -708,36 +713,25 @@ export function DesktopLeftNav() {
 
 const styles = StyleSheet.create({
   leftNav: {
-    // @ts-ignore web only
     position: 'fixed',
-    top: 10,
-    // @ts-ignore web only
+    top: 0,
+    paddingTop: 10,
+    paddingBottom: 10,
     left: '50%',
-    transform: [
-      {
-        translateX: -300,
-      },
-      {
-        translateX: '-100%',
-      },
-      ...a.scrollbar_offset.transform,
-    ],
     width: 240,
-    // @ts-ignore web only
-    maxHeight: 'calc(100vh - 10px)',
+    // @ts-expect-error web only
+    maxHeight: '100vh',
     overflowY: 'auto',
   },
-  leftNavTablet: {
-    top: 0,
-    left: 0,
-    right: 'auto',
-    borderRightWidth: 1,
-    height: '100%',
-    width: 76,
+  leftNavMinimal: {
+    paddingTop: 0,
+    paddingBottom: 0,
     paddingLeft: 0,
     paddingRight: 0,
+    height: '100%',
+    width: 86,
     alignItems: 'center',
-    transform: [],
+    overflowX: 'hidden',
   },
   backBtn: {
     position: 'absolute',
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
index 363294aa5..510d505cd 100644
--- a/src/view/shell/desktop/RightNav.tsx
+++ b/src/view/shell/desktop/RightNav.tsx
@@ -1,17 +1,23 @@
-import React from 'react'
+import {useEffect, useState} from 'react'
 import {View} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useNavigation} from '@react-navigation/core'
 
 import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
 import {useKawaiiMode} from '#/state/preferences/kawaii'
 import {useSession} from '#/state/session'
 import {DesktopFeeds} from '#/view/shell/desktop/Feeds'
 import {DesktopSearch} from '#/view/shell/desktop/Search'
 import {SidebarTrendingTopics} from '#/view/shell/desktop/SidebarTrendingTopics'
-import {atoms as a, useGutters, useTheme, web} from '#/alf'
+import {
+  atoms as a,
+  useGutters,
+  useLayoutBreakpoints,
+  useTheme,
+  web,
+} from '#/alf'
+import {AppLanguageDropdown} from '#/components/AppLanguageDropdown'
 import {Divider} from '#/components/Divider'
 import {InlineLinkText} from '#/components/Link'
 import {ProgressGuideList} from '#/components/ProgressGuide/List'
@@ -19,16 +25,15 @@ import {Text} from '#/components/Typography'
 
 function useWebQueryParams() {
   const navigation = useNavigation()
-  const [params, setParams] = React.useState<Record<string, string>>({})
+  const [params, setParams] = useState<Record<string, string>>({})
 
-  React.useEffect(() => {
+  useEffect(() => {
     return navigation.addListener('state', e => {
       try {
         const {state} = e.data
         const lastRoute = state.routes[state.routes.length - 1]
-        const {params} = lastRoute
-        setParams(params)
-      } catch (e) {}
+        setParams(lastRoute.params)
+      } catch (err) {}
     })
   }, [navigation, setParams])
 
@@ -45,9 +50,10 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
   const webqueryParams = useWebQueryParams()
   const searchQuery = webqueryParams?.q
   const showTrending = !isSearchScreen || (isSearchScreen && !!searchQuery)
+  const {rightNavVisible, centerColumnOffset, leftNavMinimal} =
+    useLayoutBreakpoints()
 
-  const {isTablet} = useWebMediaQueries()
-  if (isTablet) {
+  if (!rightNavVisible) {
     return null
   }
 
@@ -60,9 +66,7 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
           position: 'fixed',
           left: '50%',
           transform: [
-            {
-              translateX: 300,
-            },
+            {translateX: centerColumnOffset ? 150 : 300},
             ...a.scrollbar_offset.transform,
           ],
           width: 300 + gutters.paddingLeft,
@@ -125,6 +129,12 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
           </Trans>
         </Text>
       )}
+
+      {!hasSession && leftNavMinimal && (
+        <View style={[a.w_full, {height: 32}]}>
+          <AppLanguageDropdown style={{marginTop: 0}} />
+        </View>
+      )}
     </View>
   )
 }