74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
import { useSdk } from "@lynkd/messaging-inbox-sdk";
|
|
import { useUi } from "@/lib/ui-state";
|
|
import clsx from "clsx";
|
|
import { WorkspaceRail } from "@/components/nav/WorkspaceRail";
|
|
import { Sidebar } from "@/components/nav/Sidebar";
|
|
import { ThreadPanel } from "@/components/message/ThreadPanel";
|
|
import { CommandPalette } from "@/components/overlays/CommandPalette";
|
|
import { NotificationsPanel } from "@/components/overlays/NotificationsPanel";
|
|
import { ProfileDrawer } from "@/components/overlays/ProfileDrawer";
|
|
import { ThemingPanel } from "@/components/overlays/ThemingPanel";
|
|
import { HuddleBar } from "@/components/overlays/HuddleBar";
|
|
import { Modals } from "@/components/overlays/Modals";
|
|
import { AiPanel } from "@/components/overlays/AiPanel";
|
|
import { Toaster } from "@/components/overlays/Toaster";
|
|
|
|
export function AppShell({ children }: { children: React.ReactNode }) {
|
|
const { loading } = useSdk();
|
|
const { thread, sidebarOpen, setSidebarOpen, sidebarCollapsed } = useUi();
|
|
const pathname = usePathname();
|
|
|
|
// close the mobile drawer whenever the route changes
|
|
useEffect(() => {
|
|
setSidebarOpen(false);
|
|
}, [pathname, setSidebarOpen]);
|
|
|
|
return (
|
|
<div className="flex h-[100dvh] w-full overflow-hidden bg-bg text-ink">
|
|
<WorkspaceRail />
|
|
|
|
<div
|
|
role={sidebarOpen ? "dialog" : undefined}
|
|
aria-modal={sidebarOpen ? true : undefined}
|
|
aria-hidden={!sidebarOpen ? true : undefined}
|
|
className={clsx(
|
|
"fixed inset-y-0 left-0 z-40 transition-transform md:static md:z-auto md:translate-x-0 md:!pointer-events-auto",
|
|
sidebarOpen ? "translate-x-0" : "pointer-events-none -translate-x-full md:pointer-events-auto",
|
|
sidebarCollapsed && "md:hidden",
|
|
)}
|
|
>
|
|
<Sidebar />
|
|
</div>
|
|
{sidebarOpen && <div className="fixed inset-0 z-30 bg-black/50 md:hidden" onClick={() => setSidebarOpen(false)} />}
|
|
|
|
<main className="flex min-w-0 flex-1">{loading ? <BootSkeleton /> : children}</main>
|
|
|
|
{thread && <ThreadPanel />}
|
|
|
|
<CommandPalette />
|
|
<NotificationsPanel />
|
|
<ProfileDrawer />
|
|
<ThemingPanel />
|
|
<HuddleBar />
|
|
<Modals />
|
|
<AiPanel />
|
|
<Toaster />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function BootSkeleton() {
|
|
return (
|
|
<div className="flex flex-1 items-center justify-center">
|
|
<div className="flex flex-col items-center gap-3 text-muted">
|
|
<div className="brand-mark h-10 w-10 animate-pulse rounded-card" />
|
|
<span className="text-[13px]">Loading workspace…</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|