From d27cc01c873718428b30e72043bf998f0772a0f6 Mon Sep 17 00:00:00 2001 From: gbanyan Date: Fri, 13 Feb 2026 22:03:13 +0800 Subject: [PATCH] feat: add mobile sidebar access via FAB and slide-over drawer - Extract RightSidebarContent for reuse in desktop and mobile - Add floating action button (FAB) on narrow screens to open sidebar - Slide-over drawer from right with author card, Mastodon feed, tags - Lazy load Mastodon feed when drawer opens (forceLoadFeed prop) Co-authored-by: Cursor --- components/right-sidebar.tsx | 29 +++++++---- components/sidebar-layout.tsx | 93 +++++++++++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 14 deletions(-) diff --git a/components/right-sidebar.tsx b/components/right-sidebar.tsx index ddc6a0c..b5ff9ee 100644 --- a/components/right-sidebar.tsx +++ b/components/right-sidebar.tsx @@ -15,12 +15,16 @@ const MastodonFeed = dynamic(() => import('./mastodon-feed').then(mod => ({ defa ssr: false, }); -export function RightSidebar() { - const [shouldLoadFeed, setShouldLoadFeed] = useState(false); +/** Shared sidebar content for desktop aside and mobile drawer */ +export function RightSidebarContent({ forceLoadFeed = false }: { forceLoadFeed?: boolean }) { + const [shouldLoadFeed, setShouldLoadFeed] = useState(forceLoadFeed); const feedRef = useRef(null); useEffect(() => { - // Use Intersection Observer to lazy load MastodonFeed when sidebar is visible + if (forceLoadFeed) { + setShouldLoadFeed(true); + return; + } if (!feedRef.current) return; const observer = new IntersectionObserver( @@ -30,13 +34,12 @@ export function RightSidebar() { observer.disconnect(); } }, - { rootMargin: '100px' } // Start loading 100px before it's visible + { rootMargin: '100px' } ); observer.observe(feedRef.current); - return () => observer.disconnect(); - }, []); + }, [forceLoadFeed]); const tags = getAllTagsWithCount().slice(0, 5); @@ -68,9 +71,8 @@ export function RightSidebar() { ].filter(Boolean) as { key: string; href: string; icon: any; label: string }[]; return ( -