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:
@@ -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} />
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Small } from "@/components/ui/typography";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Particle, StreamProperties } from "@/api/types";
|
||||
import type { StreamParticle } from "@/hooks/use-stream-particles";
|
||||
import { useNetwork } from "@/hooks/use-networks";
|
||||
@@ -241,12 +242,22 @@ interface ParticleListViewProps {
|
||||
networkId: string;
|
||||
isLoading: boolean;
|
||||
selectedIndex?: number | null;
|
||||
/** When true, render a footer that invokes onLoadMore. */
|
||||
canLoadMore?: boolean;
|
||||
onLoadMore?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of stream particles for a container (network root, folder, etc.).
|
||||
*/
|
||||
export function ParticleListView({ streams, networkId, isLoading, selectedIndex }: ParticleListViewProps) {
|
||||
export function ParticleListView({
|
||||
streams,
|
||||
networkId,
|
||||
isLoading,
|
||||
selectedIndex,
|
||||
canLoadMore,
|
||||
onLoadMore,
|
||||
}: ParticleListViewProps) {
|
||||
const navigate = useNavigate();
|
||||
const rowRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
|
||||
@@ -294,6 +305,13 @@ export function ParticleListView({ streams, networkId, isLoading, selectedIndex
|
||||
</div>
|
||||
</StreamContextMenu>
|
||||
))}
|
||||
{canLoadMore && onLoadMore && (
|
||||
<div className="flex justify-center p-3">
|
||||
<Button variant="ghost" size="sm" onClick={onLoadMore}>
|
||||
Load more
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user