Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
7.7 kB · 237 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238import { LivestreamProvider } from "@/components/stream/livestream-provider";import { Button } from "@/components/ui/button";import { useFullscreen } from "@/contexts/fullscreen-context";import { useSession } from "@/lib/session";import { useStore } from "@/lib/store";import { Link, Outlet, useMatchRoute } from "@tanstack/react-router";import { ArrowLeft, ArrowUpFromLine, Key, LayoutGrid, ListVideo, Radio, Share2, Video, Webhook,} from "lucide-react";import { useState } from "react";import { useTranslation } from "react-i18next";import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger,} from "../ui/sidebar";import { DashboardMetricsProvider } from "./dashboard-metrics";import { DashboardStoreContext } from "./dashboard-store-context";
interface NavLink { to: string; icon: React.ComponentType<{ className?: string }>; labelKey: string;}
/** Main nav; appears above the Settings group. */const MAIN_NAV: NavLink[] = [ { to: "/dashboard", icon: LayoutGrid, labelKey: "control-panel" }, { to: "/dashboard/upload", icon: ArrowUpFromLine, labelKey: "upload-videos", }, { to: "/dashboard/videos", icon: Video, labelKey: "my-videos", },];
/** Settings group; appears under a "Settings" header. */const SETTINGS_NAV: NavLink[] = [ { to: "/dashboard/stream", icon: Radio, labelKey: "stream-settings" }, { to: "/dashboard/keys", icon: Key, labelKey: "key-manager" }, { to: "/dashboard/multistream", icon: Share2, labelKey: "multistream" }, { to: "/dashboard/recommendations", icon: ListVideo, labelKey: "recommendations-to-others", }, { to: "/dashboard/webhooks", icon: Webhook, labelKey: "webhooks" },];
const DASHBOARD_NAV_OPEN_KEY = "streamplace:dashboard-nav-open";
export default function DashboardChrome() { const { t } = useTranslation("settings"); const { state } = useSession(); const { theatre } = useFullscreen();
const [open, setOpen] = useState<boolean>(() => { if (typeof localStorage === "undefined") return true; return localStorage.getItem(DASHBOARD_NAV_OPEN_KEY) !== "false"; });
if (state.status !== "authenticated") { // "loading" means the OAuth client is restoring the session; don't // flash the "please log in" prompt on refresh for an already-logged-in // user. if (state.status === "loading") { return ( <div className="flex h-full items-center justify-center"> <div className="h-5 w-5 animate-spin rounded-full border-2 border-(--color-border) border-t-(--color-accent)" /> </div> ); } return ( <div className="flex h-full flex-col items-center justify-center gap-3 text-sm text-(--color-fg-muted)"> <p> {t("login-required", { defaultValue: "Please log in to access the dashboard.", })} </p> <Button onClick={() => useStore.getState().openLoginModal()}> {t("log-in")} </Button> </div> ); }
const user = state.session.did;
// are we in /dashboard/? if so hide the header const isDashboardRoot = window.location.pathname === "/dashboard";
return ( <LivestreamProvider user={user}> {(store) => ( <DashboardStoreContext.Provider value={store}> <DashboardMetricsProvider> <SidebarProvider className="h-svh" open={theatre ? false : open} onOpenChange={(o) => { setOpen(o); if (typeof localStorage !== "undefined") { localStorage.setItem(DASHBOARD_NAV_OPEN_KEY, String(o)); } }} > {!theatre && <DashboardSidebar />} <SidebarInset> {!theatre && !isDashboardRoot && ( <header className="bg-sidebar z-99 flex h-12 items-center gap-2 px-4"> <SidebarTrigger className="-ml-1" /> <h1 className="font-display text-lg font-semibold"> {t("nav-dashboard", { defaultValue: "Creator Dashboard", })} </h1> </header> )} <div className="mx-auto flex min-h-0 w-full flex-1 flex-col"> <Outlet /> </div> </SidebarInset> </SidebarProvider> </DashboardMetricsProvider> </DashboardStoreContext.Provider> )} </LivestreamProvider> );}
function DashboardSidebar() { const { t } = useTranslation("settings"); const { state } = useSession(); const matchRoute = useMatchRoute(); if (state.status !== "authenticated") { return null; } const user = state.session.did;
// The control panel (/dashboard) is the parent of every dashboard // page, so it matches exactly; the rest are leaf routes. const isNavActive = (to: string) => matchRoute({ to, fuzzy: to !== "/dashboard" }) !== false;
return ( <Sidebar side="left" collapsible="icon" variant="sidebar"> <SidebarContent> {/* Back to Streamplace */} <SidebarGroup> <SidebarGroupContent> <SidebarMenu> <SidebarMenuItem> <Link to={"/$user"} params={{ user }} className="w-full"> <SidebarMenuButton> <ArrowLeft /> <span>Back to Streamplace</span> </SidebarMenuButton> </Link> </SidebarMenuItem> </SidebarMenu> </SidebarGroupContent> </SidebarGroup>
{/* Main nav: Control Panel + Stream Settings */} <SidebarGroup> <SidebarGroupContent> <SidebarMenu> {MAIN_NAV.map((item) => { const Icon = item.icon; return ( <SidebarMenuItem key={item.to}> <Link to={item.to} className="w-full"> <SidebarMenuButton tooltip={t(item.labelKey)} isActive={isNavActive(item.to)} > <Icon /> <span>{t(item.labelKey)}</span> </SidebarMenuButton> </Link> </SidebarMenuItem> ); })} </SidebarMenu> </SidebarGroupContent> </SidebarGroup>
{/* Settings group: stream keys, multistream, recommendations, webhooks */} <SidebarGroup> <SidebarGroupLabel> {t("settings-group", { defaultValue: "Settings" })} </SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> {SETTINGS_NAV.map((item) => { const Icon = item.icon; return ( <SidebarMenuItem key={item.to}> <Link to={item.to} className="w-full"> <SidebarMenuButton tooltip={t(item.labelKey)} isActive={isNavActive(item.to)} > <Icon /> <span>{t(item.labelKey)}</span> </SidebarMenuButton> </Link> </SidebarMenuItem> ); })} </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> </Sidebar> );}