diff --git a/app/layout.tsx b/app/layout.tsx index 20cbed2..17c3243 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,12 @@ import './globals.css'; import type { Metadata, Viewport } from 'next'; +import { cookies } from 'next/headers'; +import { SidebarPreferenceProvider } from '@/components/providers/sidebar-preference-provider'; import { QueryProvider } from '@/components/providers/query-provider'; +import { + SIDEBAR_PREFERENCE_KEY, + parseSidebarPreference +} from '@/lib/sidebar-preference'; export const metadata: Metadata = { title: 'Fiscal Clone', @@ -14,11 +20,18 @@ export const viewport: Viewport = { themeColor: '#121417' }; -export default function RootLayout({ children }: { children: React.ReactNode }) { +export default async function RootLayout({ children }: { children: React.ReactNode }) { + const cookieStore = await cookies(); + const initialSidebarCollapsed = parseSidebarPreference( + cookieStore.get(SIDEBAR_PREFERENCE_KEY)?.value + ); + return ( - {children} + + {children} + ); diff --git a/components/providers/sidebar-preference-provider.tsx b/components/providers/sidebar-preference-provider.tsx new file mode 100644 index 0000000..c0a6596 --- /dev/null +++ b/components/providers/sidebar-preference-provider.tsx @@ -0,0 +1,32 @@ +'use client'; + +import { createContext, useContext } from 'react'; + +type SidebarPreferenceContextValue = { + initialSidebarCollapsed: boolean; +}; + +const SidebarPreferenceContext = + createContext({ + initialSidebarCollapsed: false + }); + +type SidebarPreferenceProviderProps = { + children: React.ReactNode; + initialSidebarCollapsed: boolean; +}; + +export function SidebarPreferenceProvider({ + children, + initialSidebarCollapsed +}: SidebarPreferenceProviderProps) { + return ( + + {children} + + ); +} + +export function useSidebarPreference() { + return useContext(SidebarPreferenceContext); +} diff --git a/components/shell/app-shell.tsx b/components/shell/app-shell.tsx index 0be6fc7..6139adc 100644 --- a/components/shell/app-shell.tsx +++ b/components/shell/app-shell.tsx @@ -23,6 +23,7 @@ import { useEffect, useMemo, useState } from "react"; import { authClient } from "@/lib/auth-client"; import { TaskDetailModal } from "@/components/notifications/task-detail-modal"; import { TaskNotificationsTrigger } from "@/components/notifications/task-notifications-trigger"; +import { useSidebarPreference } from "@/components/providers/sidebar-preference-provider"; import { companyAnalysisQueryOptions, companyFinancialStatementsQueryOptions, @@ -37,6 +38,7 @@ import { buildGraphingHref } from "@/lib/graphing/catalog"; import type { ActiveContext, NavGroup, NavItem } from "@/lib/types"; import { Button } from "@/components/ui/button"; import { useTaskNotificationsCenter } from "@/hooks/use-task-notifications-center"; +import { SIDEBAR_PREFERENCE_KEY } from "@/lib/sidebar-preference"; import { cn } from "@/lib/utils"; type AppShellProps = { @@ -148,8 +150,6 @@ const GROUP_LABELS: Record = { portfolio: "Portfolio", }; -const SIDEBAR_PREFERENCE_KEY = "fiscal-shell-sidebar-collapsed"; - function normalizeTicker(value: string | null | undefined) { const normalized = value?.trim().toUpperCase() ?? ""; return normalized.length > 0 ? normalized : null; @@ -260,12 +260,16 @@ export function AppShell({ const router = useRouter(); const searchParams = useSearchParams(); const queryClient = useQueryClient(); + const { initialSidebarCollapsed } = useSidebarPreference(); const [isSigningOut, setIsSigningOut] = useState(false); const [isMoreOpen, setIsMoreOpen] = useState(false); - const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); - const [hasLoadedSidebarPreference, setHasLoadedSidebarPreference] = + const [isSidebarCollapsed, setIsSidebarCollapsed] = useState( + initialSidebarCollapsed, + ); + const [hasResolvedSidebarPreference, setHasResolvedSidebarPreference] = useState(false); + const [hasMounted, setHasMounted] = useState(false); const notifications = useTaskNotificationsCenter(); const { data: session } = authClient.useSession(); const sessionUser = (session?.user ?? null) as { @@ -433,14 +437,14 @@ export function AppShell({ const storedPreference = window.localStorage.getItem( SIDEBAR_PREFERENCE_KEY, ); - if (storedPreference === "true") { - setIsSidebarCollapsed(true); + if (storedPreference === "true" || storedPreference === "false") { + setIsSidebarCollapsed(storedPreference === "true"); } - setHasLoadedSidebarPreference(true); + setHasResolvedSidebarPreference(true); }, []); useEffect(() => { - if (!hasLoadedSidebarPreference) { + if (!hasResolvedSidebarPreference) { return; } @@ -448,7 +452,12 @@ export function AppShell({ SIDEBAR_PREFERENCE_KEY, String(isSidebarCollapsed), ); - }, [hasLoadedSidebarPreference, isSidebarCollapsed]); + document.cookie = `${SIDEBAR_PREFERENCE_KEY}=${String(isSidebarCollapsed)}; path=/; max-age=31536000; samesite=lax`; + }, [hasResolvedSidebarPreference, isSidebarCollapsed]); + + useEffect(() => { + setHasMounted(true); + }, []); useEffect(() => { const browserWindow = window as Window & { @@ -520,7 +529,8 @@ export function AppShell({ >