From 057de9595b43838c5288592fdc6ce6cef933a41c Mon Sep 17 00:00:00 2001 From: codytseng Date: Fri, 17 Oct 2025 23:34:56 +0800 Subject: [PATCH] refactor: sidebar --- src/components/Sidebar/AccountButton.tsx | 22 ++++---- src/components/Sidebar/ExploreButton.tsx | 5 +- src/components/Sidebar/HomeButton.tsx | 11 ++-- src/components/Sidebar/NotificationButton.tsx | 5 +- src/components/Sidebar/PostButton.tsx | 8 +-- src/components/Sidebar/ProfileButton.tsx | 5 +- src/components/Sidebar/SearchButton.tsx | 5 +- src/components/Sidebar/SettingsButton.tsx | 6 +-- src/components/Sidebar/SidebarItem.tsx | 11 ++-- src/components/Sidebar/index.tsx | 50 ++++++++++++++----- src/constants.ts | 1 + src/providers/UserPreferencesProvider.tsx | 13 ++++- src/services/local-storage.service.ts | 12 +++++ 13 files changed, 110 insertions(+), 44 deletions(-) diff --git a/src/components/Sidebar/AccountButton.tsx b/src/components/Sidebar/AccountButton.tsx index 54a305db..c187049c 100644 --- a/src/components/Sidebar/AccountButton.tsx +++ b/src/components/Sidebar/AccountButton.tsx @@ -9,6 +9,7 @@ import { } from '@/components/ui/dropdown-menu' import { toWallet } from '@/lib/link' import { formatPubkey, generateImageByPubkey } from '@/lib/pubkey' +import { cn } from '@/lib/utils' import { usePrimaryPage, useSecondaryPage } from '@/PageManager' import { useNostr } from '@/providers/NostrProvider' import { ArrowDownUp, LogIn, LogOut, UserRound, Wallet } from 'lucide-react' @@ -18,17 +19,17 @@ import LoginDialog from '../LoginDialog' import LogoutDialog from '../LogoutDialog' import SidebarItem from './SidebarItem' -export default function AccountButton() { +export default function AccountButton({ collapse }: { collapse: boolean }) { const { pubkey } = useNostr() if (pubkey) { - return + return } else { - return + return } } -function ProfileButton() { +function ProfileButton({ collapse }: { collapse: boolean }) { const { t } = useTranslation() const { account, profile } = useNostr() const pubkey = account?.pubkey @@ -46,7 +47,10 @@ function ProfileButton() { @@ -88,12 +92,12 @@ function ProfileButton() { ) } -function LoginButton() { +function LoginButton({ collapse }: { collapse: boolean }) { const { checkLogin } = useNostr() return ( - checkLogin()} title="Login"> - + checkLogin()} title="Login" collapse={collapse}> + ) } diff --git a/src/components/Sidebar/ExploreButton.tsx b/src/components/Sidebar/ExploreButton.tsx index 8495d113..22a884d7 100644 --- a/src/components/Sidebar/ExploreButton.tsx +++ b/src/components/Sidebar/ExploreButton.tsx @@ -3,7 +3,7 @@ import { Compass } from 'lucide-react' import { useTranslation } from 'react-i18next' import SidebarItem from './SidebarItem' -export default function RelaysButton() { +export default function RelaysButton({ collapse }: { collapse: boolean }) { const { t } = useTranslation() const { navigate, current } = usePrimaryPage() @@ -12,8 +12,9 @@ export default function RelaysButton() { title={t('Explore')} onClick={() => navigate('explore')} active={current === 'explore'} + collapse={collapse} > - + ) } diff --git a/src/components/Sidebar/HomeButton.tsx b/src/components/Sidebar/HomeButton.tsx index 0b3db67e..9dfd8dcd 100644 --- a/src/components/Sidebar/HomeButton.tsx +++ b/src/components/Sidebar/HomeButton.tsx @@ -2,12 +2,17 @@ import { usePrimaryPage } from '@/PageManager' import { Home } from 'lucide-react' import SidebarItem from './SidebarItem' -export default function HomeButton() { +export default function HomeButton({ collapse }: { collapse: boolean }) { const { navigate, current } = usePrimaryPage() return ( - navigate('home')} active={current === 'home'}> - + navigate('home')} + active={current === 'home'} + collapse={collapse} + > + ) } diff --git a/src/components/Sidebar/NotificationButton.tsx b/src/components/Sidebar/NotificationButton.tsx index d0bae5bd..2a3a5af1 100644 --- a/src/components/Sidebar/NotificationButton.tsx +++ b/src/components/Sidebar/NotificationButton.tsx @@ -4,7 +4,7 @@ import { useNotification } from '@/providers/NotificationProvider' import { Bell } from 'lucide-react' import SidebarItem from './SidebarItem' -export default function NotificationsButton() { +export default function NotificationsButton({ collapse }: { collapse: boolean }) { const { checkLogin } = useNostr() const { navigate, current } = usePrimaryPage() const { hasNewNotification } = useNotification() @@ -14,9 +14,10 @@ export default function NotificationsButton() { title="Notifications" onClick={() => checkLogin(() => navigate('notifications'))} active={current === 'notifications'} + collapse={collapse} >
- + {hasNewNotification && (
)} diff --git a/src/components/Sidebar/PostButton.tsx b/src/components/Sidebar/PostButton.tsx index 39258a55..7882d8ee 100644 --- a/src/components/Sidebar/PostButton.tsx +++ b/src/components/Sidebar/PostButton.tsx @@ -1,10 +1,11 @@ import PostEditor from '@/components/PostEditor' +import { cn } from '@/lib/utils' import { useNostr } from '@/providers/NostrProvider' import { PencilLine } from 'lucide-react' import { useState } from 'react' import SidebarItem from './SidebarItem' -export default function PostButton() { +export default function PostButton({ collapse }: { collapse: boolean }) { const { checkLogin } = useNostr() const [open, setOpen] = useState(false) @@ -20,9 +21,10 @@ export default function PostButton() { }) }} variant="default" - className="bg-primary xl:justify-center gap-2" + className={cn('bg-primary gap-2', !collapse && 'justify-center')} + collapse={collapse} > - +
diff --git a/src/components/Sidebar/ProfileButton.tsx b/src/components/Sidebar/ProfileButton.tsx index d7070159..eb8e3814 100644 --- a/src/components/Sidebar/ProfileButton.tsx +++ b/src/components/Sidebar/ProfileButton.tsx @@ -3,7 +3,7 @@ import { useNostr } from '@/providers/NostrProvider' import { UserRound } from 'lucide-react' import SidebarItem from './SidebarItem' -export default function ProfileButton() { +export default function ProfileButton({ collapse }: { collapse: boolean }) { const { navigate, current } = usePrimaryPage() const { checkLogin } = useNostr() @@ -12,8 +12,9 @@ export default function ProfileButton() { title="Profile" onClick={() => checkLogin(() => navigate('profile'))} active={current === 'profile'} + collapse={collapse} > - + ) } diff --git a/src/components/Sidebar/SearchButton.tsx b/src/components/Sidebar/SearchButton.tsx index 126a8911..8c0eae5a 100644 --- a/src/components/Sidebar/SearchButton.tsx +++ b/src/components/Sidebar/SearchButton.tsx @@ -2,7 +2,7 @@ import { usePrimaryPage } from '@/PageManager' import { Search } from 'lucide-react' import SidebarItem from './SidebarItem' -export default function SearchButton() { +export default function SearchButton({ collapse }: { collapse: boolean }) { const { navigate, current, display } = usePrimaryPage() return ( @@ -10,8 +10,9 @@ export default function SearchButton() { title="Search" onClick={() => navigate('search')} active={current === 'search' && display} + collapse={collapse} > - + ) } diff --git a/src/components/Sidebar/SettingsButton.tsx b/src/components/Sidebar/SettingsButton.tsx index e373a3a5..78ddcdd4 100644 --- a/src/components/Sidebar/SettingsButton.tsx +++ b/src/components/Sidebar/SettingsButton.tsx @@ -3,12 +3,12 @@ import { useSecondaryPage } from '@/PageManager' import { Settings } from 'lucide-react' import SidebarItem from './SidebarItem' -export default function SettingsButton() { +export default function SettingsButton({ collapse }: { collapse: boolean }) { const { push } = useSecondaryPage() return ( - push(toSettings())}> - + push(toSettings())} collapse={collapse}> + ) } diff --git a/src/components/Sidebar/SidebarItem.tsx b/src/components/Sidebar/SidebarItem.tsx index aff2d7aa..8e669b63 100644 --- a/src/components/Sidebar/SidebarItem.tsx +++ b/src/components/Sidebar/SidebarItem.tsx @@ -5,14 +5,17 @@ import { useTranslation } from 'react-i18next' const SidebarItem = forwardRef< HTMLButtonElement, - ButtonProps & { title: string; description?: string; active?: boolean } ->(({ children, title, description, className, active, ...props }, ref) => { + ButtonProps & { title: string; collapse: boolean; description?: string; active?: boolean } +>(({ children, title, description, className, active, collapse, ...props }, ref) => { const { t } = useTranslation() return ( ) }) diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index df67b3d0..77db53eb 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -1,6 +1,9 @@ import Icon from '@/assets/Icon' import Logo from '@/assets/Logo' +import { cn } from '@/lib/utils' import { useScreenSize } from '@/providers/ScreenSizeProvider' +import { useUserPreferences } from '@/providers/UserPreferencesProvider' +import { ChevronsLeft, ChevronsRight } from 'lucide-react' import AccountButton from './AccountButton' import RelaysButton from './ExploreButton' import HomeButton from './HomeButton' @@ -12,24 +15,45 @@ import SettingsButton from './SettingsButton' export default function PrimaryPageSidebar() { const { isSmallScreen } = useScreenSize() + const { sidebarCollapse, updateSidebarCollapse } = useUserPreferences() + if (isSmallScreen) return null return ( -
+
-
- - -
- - - - - - - + {sidebarCollapse ? ( +
+ +
+ ) : ( +
+ +
+ )} + + + + + + +
- + +
) } diff --git a/src/constants.ts b/src/constants.ts index 1cf525ac..5b7b46f3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -48,6 +48,7 @@ export const StorageKey = { NOTIFICATION_LIST_STYLE: 'notificationListStyle', MEDIA_AUTO_LOAD_POLICY: 'mediaAutoLoadPolicy', SHOWN_CREATE_WALLET_GUIDE_TOAST_PUBKEYS: 'shownCreateWalletGuideToastPubkeys', + SIDEBAR_COLLAPSE: 'sidebarCollapse', MEDIA_UPLOAD_SERVICE: 'mediaUploadService', // deprecated HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', // deprecated ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', // deprecated diff --git a/src/providers/UserPreferencesProvider.tsx b/src/providers/UserPreferencesProvider.tsx index e82d09d3..cc2292ea 100644 --- a/src/providers/UserPreferencesProvider.tsx +++ b/src/providers/UserPreferencesProvider.tsx @@ -8,6 +8,9 @@ type TUserPreferencesContext = { muteMedia: boolean updateMuteMedia: (mute: boolean) => void + + sidebarCollapse: boolean + updateSidebarCollapse: (collapse: boolean) => void } const UserPreferencesContext = createContext(undefined) @@ -25,19 +28,27 @@ export function UserPreferencesProvider({ children }: { children: React.ReactNod storage.getNotificationListStyle() ) const [muteMedia, setMuteMedia] = useState(true) + const [sidebarCollapse, setSidebarCollapse] = useState(storage.getSidebarCollapse()) const updateNotificationListStyle = (style: TNotificationStyle) => { setNotificationListStyle(style) storage.setNotificationListStyle(style) } + const updateSidebarCollapse = (collapse: boolean) => { + setSidebarCollapse(collapse) + storage.setSidebarCollapse(collapse) + } + return ( {children} diff --git a/src/services/local-storage.service.ts b/src/services/local-storage.service.ts index c04f3954..2522b1dc 100644 --- a/src/services/local-storage.service.ts +++ b/src/services/local-storage.service.ts @@ -48,6 +48,7 @@ class LocalStorageService { private notificationListStyle: TNotificationStyle = NOTIFICATION_LIST_STYLE.DETAILED private mediaAutoLoadPolicy: TMediaAutoLoadPolicy = MEDIA_AUTO_LOAD_POLICY.ALWAYS private shownCreateWalletGuideToastPubkeys: Set = new Set() + private sidebarCollapse: boolean = false constructor() { if (!LocalStorageService.instance) { @@ -193,6 +194,8 @@ class LocalStorageService { ? new Set(JSON.parse(shownCreateWalletGuideToastPubkeysStr)) : new Set() + this.sidebarCollapse = window.localStorage.getItem(StorageKey.SIDEBAR_COLLAPSE) === 'true' + // Clean up deprecated data window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP) window.localStorage.removeItem(StorageKey.ACCOUNT_FOLLOW_LIST_EVENT_MAP) @@ -476,6 +479,15 @@ class LocalStorageService { JSON.stringify(Array.from(this.shownCreateWalletGuideToastPubkeys)) ) } + + getSidebarCollapse() { + return this.sidebarCollapse + } + + setSidebarCollapse(collapse: boolean) { + this.sidebarCollapse = collapse + window.localStorage.setItem(StorageKey.SIDEBAR_COLLAPSE, collapse.toString()) + } } const instance = new LocalStorageService()