refactor: organize desktop vs. mobile into separate folders

This commit is contained in:
Arjun Patel
2026-04-29 08:42:56 -07:00
parent 3d9fe67936
commit 3a11a82cd3
194 changed files with 213 additions and 213 deletions
+126
View File
@@ -0,0 +1,126 @@
import { useCallback, useState } from "react";
import { useNavigate, useParams, useSearchParams } 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 { ComposeQuotaIndicator } from "./compose/compose-quota-indicator";
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 [composeActive, setComposeActive] = useState(false);
const [searchParams, setSearchParams] = useSearchParams();
const statusTab: "open" | "closed" =
searchParams.get("status") === "closed" ? "closed" : "open";
const setStatusTab = (next: "open" | "closed") => {
setSearchParams(
(prev) => {
const params = new URLSearchParams(prev);
if (next === "open") params.delete("status");
else params.set("status", next);
return params;
},
{ replace: true },
);
};
const { streams, isLoading, canLoadMore, loadMore } = useStreamParticles(path, {
status: statusTab,
});
const { selectedIndex } = useStreamKeyboardNav({
streams,
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 p-1 border-b">
<Tabs
value={statusTab}
onValueChange={(v) => setStatusTab(v === "closed" ? "closed" : "open")}
>
<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={streams}
networkId={networkId!}
isLoading={isLoading}
selectedIndex={selectedIndex}
canLoadMore={canLoadMore}
onLoadMore={loadMore}
/>
</div>
<ComposeOverlay networkId={networkId!} onActiveChange={setComposeActive} />
{!composeActive && (
<div className="pointer-events-none absolute inset-x-0 bottom-16 z-20 flex justify-center px-3">
<ComposeQuotaIndicator networkId={networkId!} />
</div>
)}
<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>
);
}