From 872da3b12c87f290df446f36cdb91eedc12d5c65 Mon Sep 17 00:00:00 2001 From: siddharthd Date: Mon, 10 Aug 2026 19:46:58 +1000 Subject: [PATCH] =?UTF-8?q?shared=20page:=20window=20the=20rendered=20rows?= =?UTF-8?q?=20=E2=80=94=20DOM=20no=20longer=20grows=20with=20history?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The table rendered every split row (1,279 today, ~600/yr growth), so page weight was unbounded even after the re-render fix. Render the first 100 of the filtered/sorted set with Show more (+100) / Show all; any filter or sort change resets the window. Data stays fully client-side on purpose — search still counts matches across all history — only the DOM is capped. Headless-measured expand stall: 165ms -> 35ms. --- src/app/shared/page.tsx | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/app/shared/page.tsx b/src/app/shared/page.tsx index 2fd6081..4b66966 100644 --- a/src/app/shared/page.tsx +++ b/src/app/shared/page.tsx @@ -498,6 +498,17 @@ export default function SharedPage() { const [paymentModal, setPaymentModal] = useState<{ id: number; name: string; balance: number } | null>(null); const [showHistory, setShowHistory] = useState(null); const [editModal, setEditModal] = useState(null); + // Rendering is windowed even though the data is not: the query returns every + // split row (1,279 as of 2026-08, growing ~600/yr) so search and sort stay + // instant over full history, but the DOM stops at visibleCount — an unbounded + // table was what made expanding a receipt freeze the browser. "Show more" + // extends the window; changing any filter resets it. + const [visibleCount, setVisibleCount] = useState(100); + useEffect(() => { + setVisibleCount(100); + }, [search, sortCol, sortDir, tagIds, participantId]); + const visible = (transactions as SharedTransactionRow[]).slice(0, visibleCount); + // Receipt expansion, same pattern as the transactions page. The shared // viewer is a split participant, so /api/transactions/[id]/order // authorises them — the item list is part of what was shared. @@ -681,7 +692,7 @@ export default function SharedPage() { - {(transactions as SharedTransactionRow[]).map((tx) => ( + {visible.map((tx) => ( )} + {!txLoading && transactions.length > visible.length && ( +
+ + + + showing {visible.length} of {transactions.length} + +
+ )} {/* Payment modal */}