diff --git a/eslint-suppressions.json b/eslint-suppressions.json index acbd6f39cf..20427ab32b 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -59,11 +59,6 @@ "count": 1 } }, - "src/components/Layout/const.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 2 - } - }, "src/components/Lists.tsx": { "@typescript-eslint/no-explicit-any": { "count": 1 diff --git a/src/components/Layout/Header/index.tsx b/src/components/Layout/Header/index.tsx index 3d2ee0acda..3840b34d69 100644 --- a/src/components/Layout/Header/index.tsx +++ b/src/components/Layout/Header/index.tsx @@ -24,6 +24,7 @@ import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu' import { BUTTON_VISUAL_ALIGNMENT_OFFSET, CENTER_COLUMN_OFFSET, + CENTER_COLUMN_WIDTH, HEADER_SLOT_SIZE, SCROLLBAR_OFFSET, } from '#/components/Layout/const' @@ -65,7 +66,7 @@ export function Outer({ web: [a.py_xs, {minHeight: 52}], }), t.atoms.border_contrast_low, - gtMobile && [a.mx_auto, {maxWidth: 600}], + gtMobile && [a.mx_auto, {maxWidth: CENTER_COLUMN_WIDTH}], !isWithinOffsetView && !isWithinSplitView && { transform: [ diff --git a/src/components/Layout/const.ts b/src/components/Layout/const.ts index 2721bed21e..7bcfd545bd 100644 --- a/src/components/Layout/const.ts +++ b/src/components/Layout/const.ts @@ -1,7 +1,7 @@ export const SCROLLBAR_OFFSET = - 'calc(-1 * var(--removed-body-scroll-bar-size, 0px) / 2)' as any + 'calc(-1 * var(--removed-body-scroll-bar-size, 0px) / 2)' as `${number}%` export const SCROLLBAR_OFFSET_POSITIVE = - 'calc(var(--removed-body-scroll-bar-size, 0px) / 2)' as any + 'calc(var(--removed-body-scroll-bar-size, 0px) / 2)' as `${number}%` /** * Useful for visually aligning icons within header buttons with the elements @@ -19,3 +19,8 @@ export const HEADER_SLOT_SIZE = 33 * How far to shift the center column when in the tablet breakpoint */ export const CENTER_COLUMN_OFFSET = -105 + +/** + * How wide the center column is + */ +export const CENTER_COLUMN_WIDTH = 600 diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index abda8fdf44..c98dd7aed8 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -22,7 +22,11 @@ import { web, } from '#/alf' import {useDialogContext} from '#/components/Dialog' -import {CENTER_COLUMN_OFFSET, SCROLLBAR_OFFSET} from '#/components/Layout/const' +import { + CENTER_COLUMN_OFFSET, + CENTER_COLUMN_WIDTH, + SCROLLBAR_OFFSET, +} from '#/components/Layout/const' import {ScrollbarOffsetContext} from '#/components/Layout/context' import {IS_WEB} from '#/env' @@ -152,7 +156,7 @@ export const Center = memo(function LayoutCenter({ a.w_full, !isWithinSplitView && a.mx_auto, gtMobile && { - maxWidth: 600, + maxWidth: CENTER_COLUMN_WIDTH, }, !isWithinOffsetView && !isWithinSplitView && { diff --git a/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx b/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx index 91428a08eb..a3f2314e85 100644 --- a/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx +++ b/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx @@ -7,10 +7,11 @@ import {type NativeStackNavigationProp} from '@react-navigation/native-stack' import {type FlatNavigatorParams} from '#/lib/routes/types' import {ScrollProvider} from '#/lib/ScrollContext' import {type NativeStackNavigationOptionsWithAuth} from '#/view/shell/createNativeStackNavigatorWithAuth' +import {LEFT_NAV_MINIMAL_WIDTH} from '#/view/shell/desktop/LeftNav' import {atoms as a, useLayoutBreakpoints, useTheme, web} from '#/alf' import {useDialogControl} from '#/components/Dialog' import {NewChat} from '#/components/dms/dialogs/NewChatDialog' -import {SCROLLBAR_OFFSET} from '#/components/Layout' +import {CENTER_COLUMN_WIDTH, SCROLLBAR_OFFSET} from '#/components/Layout' import {LockScroll} from '#/components/LockScroll' import {useAgeAssurance} from '#/ageAssurance' import {IS_WEB} from '#/env' @@ -18,12 +19,6 @@ import {ChatList, Header as ChatListHeader} from '../../ChatList' import {SplitViewProvider} from './context' import {splitViewLeftScroll} from './leftColumnScroll' -const CENTER_COLUMN_WIDTH = 600 -const LEFT_NAV_FULL_WIDTH = 245 -const LEFT_NAV_MINIMAL_WIDTH = 86 -const RIGHT_NAV_FULL_WIDTH = 330 -const RIGHT_NAV_MINIMAL_WIDTH = 280 - type MessageScreens = | 'Messages' | 'MessagesConversation' @@ -72,25 +67,14 @@ function MessagesSplitViewLayout({children, navigation, route}: LayoutProps) { ? route.params.conversation : undefined - const rightNavWidth = centerColumnOffset - ? RIGHT_NAV_MINIMAL_WIDTH - : RIGHT_NAV_FULL_WIDTH + const halfLeftNavWidth = LEFT_NAV_MINIMAL_WIDTH / 2 - const leftNavWidth = centerColumnOffset - ? LEFT_NAV_MINIMAL_WIDTH - : LEFT_NAV_FULL_WIDTH - LEFT_NAV_MINIMAL_WIDTH + const leftColumnWidth = 360 - // slight reduce width for smaller breakpoint - const centerColumnWidth = centerColumnOffset - ? CENTER_COLUMN_WIDTH - 50 - : CENTER_COLUMN_WIDTH + const rightColumnWidth = + CENTER_COLUMN_WIDTH - (centerColumnOffset ? halfLeftNavWidth + 30 : 0) - // nasty magic numbers here, sorry :( - const offset = centerColumnOffset - ? LEFT_NAV_MINIMAL_WIDTH - 34 - : LEFT_NAV_MINIMAL_WIDTH + 5 - - const containerWidth = leftNavWidth + centerColumnWidth + rightNavWidth + const containerWidth = leftColumnWidth + rightColumnWidth return ( @@ -129,7 +117,7 @@ function MessagesSplitViewLayout({children, navigation, route}: LayoutProps) { style={[ a.border_x, t.atoms.border_contrast_low, - {width: centerColumnWidth}, + {width: rightColumnWidth}, ]}> {children} diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx index 416dfe55f5..cc09fccf4e 100644 --- a/src/view/com/util/Views.web.tsx +++ b/src/view/com/util/Views.web.tsx @@ -28,7 +28,7 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {addStyle} from '#/lib/styles' import {useLayoutBreakpoints} from '#/alf' import {useDialogContext} from '#/components/Dialog' -import {CENTER_COLUMN_OFFSET} from '#/components/Layout' +import {CENTER_COLUMN_OFFSET, CENTER_COLUMN_WIDTH} from '#/components/Layout' interface AddedProps { desktopFixedHeight?: boolean | number @@ -175,7 +175,7 @@ const styles = StyleSheet.create({ }, container: { width: '100%', - maxWidth: 600, + maxWidth: CENTER_COLUMN_WIDTH, marginLeft: 'auto', marginRight: 'auto', }, @@ -184,7 +184,7 @@ const styles = StyleSheet.create({ }, containerScroll: { width: '100%', - maxWidth: 600, + maxWidth: CENTER_COLUMN_WIDTH, marginLeft: 'auto', marginRight: 'auto', }, diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 37c32f231a..0c7f63b829 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -1,9 +1,8 @@ -import {type JSX, useCallback, useMemo, useState} from 'react' +import {useCallback, useMemo, useState} from 'react' import {StyleSheet, View} from 'react-native' import {type AppBskyActorDefs} from '@atproto/api' -import {msg, plural} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' -import {Trans} from '@lingui/react/macro' +import {plural} from '@lingui/core/macro' +import {Trans, useLingui} from '@lingui/react/macro' import {useNavigation, useNavigationState} from '@react-navigation/native' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' @@ -40,42 +39,46 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {type DialogControlProps} from '#/components/Dialog' import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft' import { - Bell_Filled_Corner0_Rounded as BellFilled, - Bell_Stroke2_Corner0_Rounded as Bell, + Bell_Filled_Corner0_Rounded as BellFilledIcon, + Bell_Stroke2_Corner0_Rounded as BellIcon, } from '#/components/icons/Bell' -import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' import { - BulletList_Filled_Corner0_Rounded as ListFilled, - BulletList_Stroke2_Corner0_Rounded as List, + Bookmark as BookmarkIcon, + BookmarkFilled as BookmarkFilledIcon, +} from '#/components/icons/Bookmark' +import { + BulletList_Filled_Corner0_Rounded as ListFilledIcon, + BulletList_Stroke2_Corner0_Rounded as ListIcon, } from '#/components/icons/BulletList' +import {type Props as SVGIconProps} from '#/components/icons/common' import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid' import {EditBig_Stroke2_Corner2_Rounded as EditBigIcon} from '#/components/icons/EditBig' import { - Hashtag_Filled_Corner0_Rounded as HashtagFilled, - Hashtag_Stroke2_Corner0_Rounded as Hashtag, + Hashtag_Filled_Corner0_Rounded as HashtagFilledIcon, + Hashtag_Stroke2_Corner0_Rounded as HashtagIcon, } from '#/components/icons/Hashtag' import { - HomeOpen_Filled_Corner0_Rounded as HomeFilled, - HomeOpen_Stoke2_Corner0_Rounded as Home, + HomeOpen_Filled_Corner0_Rounded as HomeFilledIcon, + HomeOpen_Stoke2_Corner0_Rounded as HomeIcon, } from '#/components/icons/HomeOpen' import { - MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, - MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, + MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilledIcon, + MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon, } from '#/components/icons/MagnifyingGlass' import { - Message_Stroke2_Corner0_Rounded as Message, - Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, + Message_Stroke2_Corner0_Rounded as MessageIcon, + Message_Stroke2_Corner0_Rounded_Filled as MessageFilledIcon, } from '#/components/icons/Message' import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' import { - SettingsGear2_Filled_Corner0_Rounded as SettingsFilled, - SettingsGear2_Stroke2_Corner0_Rounded as Settings, + SettingsGear2_Filled_Corner0_Rounded as SettingsFilledIcon, + SettingsGear2_Stroke2_Corner0_Rounded as SettingsIcon, } from '#/components/icons/SettingsGear2' import { - UserCircle_Filled_Corner0_Rounded as UserCircleFilled, - UserCircle_Stroke2_Corner0_Rounded as UserCircle, + UserCircle_Filled_Corner0_Rounded as UserCircleFilledIcon, + UserCircle_Stroke2_Corner0_Rounded as UserCircleIcon, } from '#/components/icons/UserCircle' -import {CENTER_COLUMN_OFFSET} from '#/components/Layout' +import {CENTER_COLUMN_OFFSET, CENTER_COLUMN_WIDTH} from '#/components/Layout' import * as Menu from '#/components/Menu' import * as Prompt from '#/components/Prompt' import {Text} from '#/components/Typography' @@ -84,8 +87,13 @@ import {useActorStatus} from '#/features/liveNow' import {router} from '#/routes' import {PlatformInfo} from '../../../../modules/expo-bluesky-swiss-army' +const LARGE_ELEMENT_SIZE = 48 const NAV_ICON_WIDTH = 28 +export const LEFT_NAV_STANDARD_WIDTH = 240 +export const LEFT_NAV_MINIMAL_WIDTH = 80 +const LEFT_NAV_PWI_WIDTH = 245 + function ProfileCard({minimal}: {minimal: boolean}) { const {currentAccount, accounts} = useSession() const {logoutEveryAccount} = useSessionApi() @@ -94,11 +102,9 @@ function ProfileCard({minimal}: {minimal: boolean}) { }) const profiles = data?.profiles const signOutPromptControl = Prompt.usePromptControl() - const {_} = useLingui() + const {t: l} = useLingui() const t = useTheme() - const size = 48 - const profile = profiles?.find(p => p.did === currentAccount!.did) const otherAccounts = accounts .filter(acc => acc.did !== currentAccount!.did) @@ -110,10 +116,10 @@ function ProfileCard({minimal}: {minimal: boolean}) { const {isActive: live} = useActorStatus(profile) return ( - + {!isLoading && profile ? ( - + {({props, state, control}) => { const active = state.hovered || state.focused || control.isOpen return ( @@ -149,7 +155,7 @@ function ProfileCard({minimal}: {minimal: boolean}) { ]}> @@ -205,18 +211,18 @@ function ProfileCard({minimal}: {minimal: boolean}) { ) : ( )} logoutEveryAccount('Settings')} - confirmButtonCta={_(msg`Sign out`)} - cancelButtonCta={_(msg`Cancel`)} + confirmButtonCta={l`Sign out`} + cancelButtonCta={l`Cancel`} confirmButtonColor="negative" /> @@ -235,7 +241,7 @@ function SwitchMenuItems({ | undefined signOutPromptControl: DialogControlProps }) { - const {_} = useLingui() + const {t: l} = useLingui() const {setShowLoggedOut} = useLoggedOutViewControls() const closeEverything = useCloseAllActiveElements() @@ -264,15 +270,13 @@ function SwitchMenuItems({ )} - + Add another account - + Sign out @@ -283,7 +287,7 @@ function SwitchMenuItems({ } function SwitcherMenuProfileLink() { - const {_} = useLingui() + const {t: l} = useLingui() const {currentAccount} = useSession() const navigation = useNavigation() const context = Menu.useMenuContext() @@ -326,11 +330,11 @@ function SwitcherMenuProfileLink() { ) return ( - + Go to profile @@ -345,7 +349,7 @@ function SwitchMenuItem({ account: SessionAccount profile: AppBskyActorDefs.ProfileViewDetailed | undefined }) { - const {_} = useLingui() + const {t: l} = useLingui() const {onPressSwitchAccount, pendingDid} = useAccountSwitcher() const {isActive: live} = useActorStatus(profile) @@ -354,12 +358,10 @@ function SwitchMenuItem({ disabled={!!pendingDid} style={[a.gap_sm, {minWidth: 150}]} key={account.did} - label={_( - msg`Switch to ${sanitizeHandle( - profile?.handle ?? account.handle, - '@', - )}`, - )} + label={l`Switch to ${sanitizeHandle( + profile?.handle ?? account.handle, + '@', + )}`} onPress={() => void onPressSwitchAccount(account, 'SwitchAccount')}> + active: React.ComponentType + } label: string minimal: boolean } -function NavItem({ - count, - hasNew, - href, - icon, - iconFilled, - label, - minimal, -}: NavItemProps) { +function NavItem({count, hasNew, href, icons, label, minimal}: NavItemProps) { const t = useTheme() - const {_} = useLingui() + const {t: l} = useLingui() const {currentAccount} = useSession() const [pathName] = useMemo(() => router.matchPath(href), [href]) @@ -431,6 +427,8 @@ function NavItem({ [navigation, href, isCurrent], ) + const Icon = isCurrent || isRelated ? icons.active : icons.inactive + return ( - {isCurrent || isRelated ? iconFilled : icon} + {typeof count === 'string' && count ? (