89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
import { useCallback, useEffect, useState } from "react";
|
|
import { useNavigate, useParams } from "react-router-dom";
|
|
import { List, LayoutGrid } from "lucide-react";
|
|
import { particlePath } from "@/lib/particle-path";
|
|
import { ParticleListView } from "@/features/particles/particle-list-view";
|
|
import { ParticleGridView } from "@/features/particles/particle-grid-view";
|
|
import { AutoplayOverlay } from "@/features/particles/autoplay-overlay";
|
|
import ControlsIndicator from "@/features/compose/controls-indicator";
|
|
import { ComposeOverlay } from "./compose/compose-overlay";
|
|
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
|
import { useViewModeStore } from "@/stores/view-mode-store";
|
|
import { useStreamParticles } from "@/hooks/use-stream-particles";
|
|
import { useStreamKeyboardNav } from "@/hooks/use-stream-keyboard-nav";
|
|
import { useDockBadge } from "@/hooks/use-dock-badge";
|
|
import { useAuthStore } from "@/stores/auth-store";
|
|
|
|
/**
|
|
* 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 viewMode = useViewModeStore((s) => s.viewMode);
|
|
const setViewMode = useViewModeStore((s) => s.setViewMode);
|
|
|
|
useEffect(() => {
|
|
window.electronWindow.openAutoplay();
|
|
window.electronWindow.openHuddle();
|
|
}, []);
|
|
|
|
const { streams } = useStreamParticles(path);
|
|
const userId = useAuthStore((s) => s.user?.id);
|
|
useDockBadge(streams, userId);
|
|
const [composeActive, setComposeActive] = useState(false);
|
|
|
|
const { selectedIndex } = useStreamKeyboardNav({
|
|
streams,
|
|
viewMode,
|
|
enabled: !composeActive,
|
|
onNavigate: useCallback(
|
|
(streamId: string) => navigate(`/${networkId}/${streamId}`),
|
|
[navigate, networkId],
|
|
),
|
|
});
|
|
|
|
return (
|
|
<div className="relative min-h-0 flex-1">
|
|
{/* Scrollable content */}
|
|
<div className="h-full overflow-y-auto overscroll-contain pt-10 pb-14">
|
|
{viewMode === "list" ? (
|
|
<ParticleListView path={path} selectedIndex={selectedIndex} />
|
|
) : (
|
|
<ParticleGridView path={path} selectedIndex={selectedIndex} />
|
|
)}
|
|
</div>
|
|
|
|
{/* Fixed overlays */}
|
|
<div className="pointer-events-none absolute inset-x-0 top-0 flex justify-end px-3 pt-2">
|
|
<ToggleGroup
|
|
type="single"
|
|
value={viewMode}
|
|
onValueChange={(v) => {
|
|
if (v) setViewMode(v as "list" | "grid");
|
|
}}
|
|
size="sm"
|
|
className="pointer-events-auto"
|
|
>
|
|
<ToggleGroupItem value="list" aria-label="List view">
|
|
<List className="size-3.5" />
|
|
</ToggleGroupItem>
|
|
<ToggleGroupItem value="grid" aria-label="Grid view">
|
|
<LayoutGrid className="size-3.5" />
|
|
</ToggleGroupItem>
|
|
</ToggleGroup>
|
|
</div>
|
|
|
|
<AutoplayOverlay networkId={networkId!} />
|
|
<ComposeOverlay networkId={networkId!} onActiveChange={setComposeActive} />
|
|
<div className="pointer-events-none absolute inset-x-0 bottom-0 flex justify-center p-3">
|
|
<div className="pointer-events-auto">
|
|
<ControlsIndicator type={"new"} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|