Skip to content
File

Blob: src/client/components/share/layout.tsx

typescript124 lines
1import { Outlet, useParams, useRouterState } from "@tanstack/react-router";
2import { SharedActivePageBoundary } from "@/client/components/active-page/shared";
3import { ShareViewProvider } from "@/client/components/share/view-provider";
4import { useShareView } from "@/client/components/share/use-share-view";
5import { ShareHeader } from "@/client/components/share/header";
6import { ShareSidebar } from "@/client/components/sidebar/share-sidebar";
7import { Footer } from "@/client/components/footer";
8import { useMobileDrawer } from "@/client/hooks/use-mobile-drawer";
9import { PageErrorState } from "@/client/components/ui/page-error-state";
10import { PageLoadingSkeleton } from "@/client/components/ui/page-loading-skeleton";
11import { Skeleton } from "@/client/components/ui/skeleton";
12 
13function ShareLoadingShell() {
14 return (
15 <>
16 <header className="z-50 shrink-0 border-b border-zinc-800/60 bg-zinc-900/95 backdrop-blur-sm">
17 <div className="flex items-center px-4 py-3 sm:px-6">
18 <Skeleton className="h-9 w-9 rounded-lg" />
19 <div className="ml-3 hidden space-y-1.5 sm:block">
20 <Skeleton className="h-3.5 w-16" />
21 <Skeleton className="h-3 w-28" />
22 </div>
23 </div>
24 </header>
25 <div className="flex flex-1 overflow-hidden">
26 <nav
27 className="hidden w-[260px] shrink-0 border-r border-zinc-800/60 bg-zinc-900 px-2 py-4 md:block"
28 aria-hidden="true"
29 >
30 <div className="flex h-8 items-center gap-1 px-2">
31 <Skeleton className="h-5 w-5 shrink-0 rounded" />
32 <Skeleton className="h-3.5 w-32" />
33 </div>
34 <div className="mt-1 space-y-1 pl-3">
35 <div className="flex h-8 items-center gap-1 px-2">
36 <Skeleton className="h-5 w-5 shrink-0 rounded" />
37 <Skeleton className="h-3.5 w-28" />
38 </div>
39 <div className="flex h-8 items-center gap-1 px-2">
40 <Skeleton className="h-5 w-5 shrink-0 rounded" />
41 <Skeleton className="h-3.5 w-24" />
42 </div>
43 <div className="flex h-8 items-center gap-1 px-2">
44 <Skeleton className="h-5 w-5 shrink-0 rounded" />
45 <Skeleton className="h-3.5 w-20" />
46 </div>
47 </div>
48 </nav>
49 <main id="main-content" tabIndex={-1} className="flex-1 overflow-y-auto outline-none">
50 <PageLoadingSkeleton kind="unknown" />
51 </main>
52 </div>
53 <Footer expanded={true} />
54 </>
55 );
56}
57 
58function ShareReadyShell() {
59 const { open: mobileDrawerOpen, close: closeMobileDrawer, toggle: toggleMobileDrawer } = useMobileDrawer();
60 
61 return (
62 <>
63 <ShareHeader onToggleMobileSidebar={toggleMobileDrawer} />
64 <div className="flex flex-1 overflow-hidden">
65 <ShareSidebar mobileOpen={mobileDrawerOpen} onMobileClose={closeMobileDrawer} />
66 <main id="main-content" tabIndex={-1} className="flex-1 overflow-y-auto outline-none">
67 <SharedActivePageBoundary>
68 <Outlet />
69 </SharedActivePageBoundary>
70 </main>
71 </div>
72 <Footer expanded={true} />
73 </>
74 );
75}
76 
77function ShareLayoutInner() {
78 const { status, error } = useShareView();
79 
80 if (status === "loading") {
81 return <ShareLoadingShell />;
82 }
83 
84 if (status === "error") {
85 return (
86 <main id="main-content" tabIndex={-1} className="flex-1 overflow-y-auto outline-none">
87 <PageErrorState
88 message={error ?? "This link's either wrong or done. Ask whoever sent it for a new one."}
89 className="h-full"
90 action={{
91 label: "Go home",
92 onClick: () => {
93 window.location.href = "/";
94 },
95 }}
96 />
97 </main>
98 );
99 }
100 
101 return <ShareReadyShell />;
102}
103 
104export function ShareLayout() {
105 const { token } = useParams({ strict: false }) as { token: string };
106 const activePage = useRouterState({
107 select: (s) => (s.location.search as { page?: string }).page,
108 });
109 
110 return (
111 <div className="flex h-screen flex-col">
112 <a
113 href="#main-content"
114 className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[200] focus:rounded-lg focus:bg-zinc-800 focus:px-4 focus:py-2 focus:text-accent-400 focus:ring-2 focus:ring-accent-500/50"
115 >
116 Skip to content
117 </a>
118 <ShareViewProvider key={token} token={token} activePage={activePage}>
119 <ShareLayoutInner />
120 </ShareViewProvider>
121 </div>
122 );
123}