Files
llink/js/src/features/network-root.tsx
T
talksik 6fe5af8d8d fix: dock app badge shows incorrect number
Closes #122

We now only show number based on open streams
2026-04-09 12:31:14 -07:00

103 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useCallback, useMemo, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { CircleDot, CircleCheckBig } from "lucide-react";
import { particlePath } from "@/lib/particle-path";
import { ParticleListView } from "@/features/particles/particle-list-view";
import { VideoAudioToggle } from "@/components/video-audio-toggle";
import { ComposeOverlay } from "./compose/compose-overlay";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useStreamParticles } from "@/hooks/use-stream-particles";
import { useStreamKeyboardNav } from "@/hooks/use-stream-keyboard-nav";
/**
* Route-level component for /:networkId (index).
* Shows root-level particles for the selected network.
*/
export default function NetworkRoot() {
const { networkId } = useParams();
const navigate = useNavigate();
const path = particlePath(networkId!, []);
const { streams, isLoading } = useStreamParticles(path);
const [composeActive, setComposeActive] = useState(false);
const [statusTab, setStatusTab] = useState<"open" | "closed">("open");
const filteredStreams = useMemo(
() => streams.filter((s) => s.status === statusTab),
[streams, statusTab],
);
const { selectedIndex } = useStreamKeyboardNav({
streams: filteredStreams,
enabled: !composeActive,
onNavigate: useCallback(
(streamId: string) => navigate(`/${networkId}/${streamId}`),
[navigate, networkId],
),
});
return (
<div className="relative flex min-h-0 flex-1 flex-col">
{/* Top bar — stays in place */}
<div className="flex shrink-0 items-center justify-between p-1 border-b">
<Tabs
value={statusTab}
onValueChange={(v) => setStatusTab(v as "open" | "closed")}
>
<TabsList>
<TabsTrigger value="open"><CircleDot className="size-3 text-green-500" /> Open</TabsTrigger>
<TabsTrigger value="closed"><CircleCheckBig className="size-3" /> Closed</TabsTrigger>
</TabsList>
</Tabs>
</div>
{/* Scrollable content */}
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain pb-14 py-2">
<ParticleListView streams={filteredStreams} networkId={networkId!} isLoading={isLoading} selectedIndex={selectedIndex} />
</div>
<ComposeOverlay networkId={networkId!} onActiveChange={setComposeActive} />
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-20 flex justify-center p-3">
<div className="pointer-events-auto">
<NetworkRootControls />
</div>
</div>
</div>
);
}
function NetworkRootControls() {
return (
<div className="flex items-center gap-4 text-sm text-white/50">
<span>
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
↑↓
</kbd>{" "}
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
Enter
</kbd>{" "}
navigate
</span>
<span>
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
19
</kbd>{" "}
jump
</span>
<VideoAudioToggle />
<span>
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
Hold `
</kbd>{" "}
to start
</span>
<span>
<kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono text-xs">
T
</kbd>{" "}
text
</span>
</div>
);
}