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
This commit is contained in:
Samuel Newman
2025-02-25 09:20:37 -08:00
committed by GitHub
parent 0437838649
commit cc8369e868
17 changed files with 211 additions and 122 deletions
+40 -46
View File
@@ -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',
+23 -13
View File
@@ -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>
)
}