From 8cbc04fff761bc6cb3c460cd6146bd49a3fbf868 Mon Sep 17 00:00:00 2001 From: talksik Date: Wed, 8 Apr 2026 08:56:04 -0700 Subject: [PATCH] fix: list of streams glitching to top This was due to real-time changes to items leading to scrolling to top. Closes #123 --- js/src/features/particles/particle-list-view.tsx | 15 +++++++++++---- js/src/hooks/use-stream-keyboard-nav.ts | 9 +++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/js/src/features/particles/particle-list-view.tsx b/js/src/features/particles/particle-list-view.tsx index eb39062..578cb51 100644 --- a/js/src/features/particles/particle-list-view.tsx +++ b/js/src/features/particles/particle-list-view.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, useRef, useEffect, memo } from "react"; import { useNavigate } from "react-router-dom"; import { Radio, @@ -75,7 +75,7 @@ function getMessagePreview(particle: Particle): string { } } -function StreamRow({ +const StreamRow = memo(function StreamRow({ particle, networkId, onClick, @@ -234,7 +234,7 @@ function StreamRow({ )} ); -} +}); interface ParticleListViewProps { streams: StreamParticle[]; @@ -248,6 +248,13 @@ interface ParticleListViewProps { */ export function ParticleListView({ streams, networkId, isLoading, selectedIndex }: ParticleListViewProps) { const navigate = useNavigate(); + const rowRefs = useRef<(HTMLDivElement | null)[]>([]); + + useEffect(() => { + if (selectedIndex !== null && selectedIndex !== undefined && selectedIndex >= 0) { + rowRefs.current[selectedIndex]?.scrollIntoView({ block: "nearest" }); + } + }, [selectedIndex]); if (isLoading) { return ; @@ -269,7 +276,7 @@ export function ParticleListView({ streams, networkId, isLoading, selectedIndex {streams.map((stream, index) => (
el?.scrollIntoView({ block: "nearest" }) : undefined} + ref={(el) => { rowRefs.current[index] = el; }} > (null); - // Reset selection to first item when streams change or view mode switches + // Initialize selection when streams first load; clear if streams become empty. + // Do NOT reset on every Firestore update — that would scroll the list to the top. useEffect(() => { - setSelectedIndex(streams.length > 0 ? 0 : null); + setSelectedIndex((prev) => { + if (streams.length === 0) return null; + if (prev === null) return 0; + return prev; + }); }, [streams.length]); useEffect(() => {