feat: paginate closed streams tab (#148)

Closed streams grow unbounded as teams age and can reach 1000s of items,
while open streams stay bounded by active work. Switch the streams list
to subscribe per-status so each tab only pulls what it renders, and cap
the closed tab to a 50-item window that grows via a Load more action.

The open tab remains unbounded so per-row realtime features — autoplay,
huddle indicators — keep full coverage of the streams that matter.
Closed streams are archived and don't need push behavior, which side-
steps the autoplay baseline-reset and huddle-on-page-2 risks that made
pagination complicated for the open tab.

Refs flowy-live/llink#138.

Co-authored-by: Claude <[email protected]>
This commit was merged in pull request #148.
This commit is contained in:
Arjun Patel
2026-04-12 11:55:23 -07:00
committed by GitHub
co-authored by Claude
parent bea723a90a
commit f03868e65b
5 changed files with 100 additions and 20 deletions
+13 -8
View File
@@ -1,4 +1,4 @@
import { useCallback, useMemo, useState } from "react";
import { useCallback, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { CircleDot, CircleCheckBig } from "lucide-react";
import { particlePath } from "@/lib/particle-path";
@@ -18,17 +18,15 @@ export default function NetworkRoot() {
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 { streams, isLoading, canLoadMore, loadMore } = useStreamParticles(path, {
status: statusTab,
});
const { selectedIndex } = useStreamKeyboardNav({
streams: filteredStreams,
streams,
enabled: !composeActive,
onNavigate: useCallback(
(streamId: string) => navigate(`/${networkId}/${streamId}`),
@@ -53,7 +51,14 @@ export default function NetworkRoot() {
{/* 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} />
<ParticleListView
streams={streams}
networkId={networkId!}
isLoading={isLoading}
selectedIndex={selectedIndex}
canLoadMore={canLoadMore}
onLoadMore={loadMore}
/>
</div>
<ComposeOverlay networkId={networkId!} onActiveChange={setComposeActive} />