From cef0f20aa1063bb3091165840707c44c6e001567 Mon Sep 17 00:00:00 2001 From: Wes Date: Mon, 13 Jul 2026 10:25:28 -0600 Subject: [PATCH] fix(desktop): retain live rows in exhausted windows A complete short channel window has no unloaded older boundary. Preserve same-second live rows there even when their event id sorts beyond the current oldest row; otherwise valid messages are silently discarded. Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co> Signed-off-by: Wes --- .../messages/lib/channelWindowStore.test.mjs | 17 ++++++++++++++++- .../features/messages/lib/channelWindowStore.ts | 6 ++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/desktop/src/features/messages/lib/channelWindowStore.test.mjs b/desktop/src/features/messages/lib/channelWindowStore.test.mjs index 7db4050bf..95408b389 100644 --- a/desktop/src/features/messages/lib/channelWindowStore.test.mjs +++ b/desktop/src/features/messages/lib/channelWindowStore.test.mjs @@ -154,7 +154,7 @@ test("live backdated rows stay outside pages and render in order", () => { ); }); -test("live rows below the oldest retained boundary wait for paging", () => { +test("live rows below an open oldest boundary wait for paging", () => { const store = replaceNewestChannelWindow( emptyChannelWindowStore(), page(null, [event("n", 110), event("a", 100)]), @@ -162,6 +162,21 @@ test("live rows below the oldest retained boundary wait for paging", () => { assert.equal(mergeLiveChannelWindowEvent(store, event("old", 90)), store); }); +test("same-second live rows enter an exhausted short window regardless of id order", () => { + const store = replaceNewestChannelWindow( + emptyChannelWindowStore(), + page(null, [event("a", 100), event("m", 100)], { hasMore: false }), + ); + const live = event("z", 100); + const withLive = mergeLiveChannelWindowEvent(store, live); + + assert.notEqual(withLive, store); + assert.deepEqual( + flattenChannelWindowEvents(withLive).map((item) => item.content), + ["z", "m", "a"], + ); +}); + test("live aux stays separate from authoritative page closure", () => { const store = replaceNewestChannelWindow( emptyChannelWindowStore(), diff --git a/desktop/src/features/messages/lib/channelWindowStore.ts b/desktop/src/features/messages/lib/channelWindowStore.ts index c20593989..15cd879d1 100644 --- a/desktop/src/features/messages/lib/channelWindowStore.ts +++ b/desktop/src/features/messages/lib/channelWindowStore.ts @@ -178,7 +178,7 @@ export function mergeLiveThreadSummary( /** * Merge a live top-level event without mutating authoritative page boundaries. - * Events below the oldest loaded boundary wait for ordinary relay pagination. + * Events below an open oldest boundary wait for ordinary relay pagination. */ export function mergeLiveChannelWindowEvent( current: ChannelWindowStore, @@ -205,7 +205,9 @@ export function mergeLiveChannelWindowEvent( } const oldestPage = current.pages[current.pages.length - 1]; const oldest = oldestPage?.rows[oldestPage.rows.length - 1]?.event; - if (oldest && compareRelayOrder(event, oldest) >= 0) return current; + if (oldestPage?.hasMore && oldest && compareRelayOrder(event, oldest) >= 0) { + return current; + } return { ...current, liveOverlay: current.liveOverlay