mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix: prevent timeline cap from evicting backward-paged roots on live event
Two CI failures on main: 1. File-size guard: lib.rs grew by one line in PR #1246 (show_native_notification added to Tauri command list). Bump the approved override from 1034 to 1035 and update the comment. 2. Scroll-history regression (scroll-history.spec.ts:1363): when the user scrolls back past MAX_TIMELINE_MESSAGES (2000) events, a live subscription event arriving via mergeTimelineCacheMessages triggered normalizeTimelineMessages, which caps to the newest 2000. This evicted the backward-paged roots, reset oldestAfterMerge, and stalled pagination — deepest stalled at ~471 instead of reaching < 50. Fix: mergeTimelineCacheMessages now checks the content-event count on the ORIGINAL current array (before dedup+incoming). If the cache is already in backward-paged state (> MAX_TIMELINE_MESSAGES content events), it skips the cap and returns sort+dedupe only. The cap still applies during normal live sessions (cache at or under the limit). Also exports isTimelineWindowContentEvent and MAX_TIMELINE_MESSAGES from messageQueryKeys.ts so hooks.ts can reference them directly. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
co-authored by
Will Pfleger
parent
ca50d83289
commit
34a8ecbf6f
@@ -82,8 +82,9 @@ const overrides = new Map([
|
||||
// persona-events rebase: boot-time event-sync wiring (run_boot_migrations
|
||||
// syncs team-dir edits before all personas.json readers; run_event_sync
|
||||
// signs the persona/team retention events post-identity) layered on top of
|
||||
// main's growth. Load-bearing feature growth, queued to split with the list.
|
||||
["src-tauri/src/lib.rs", 1034],
|
||||
// main's growth. PR #1246 added show_native_notification to the Tauri
|
||||
// command list. Load-bearing feature growth, queued to split with the list.
|
||||
["src-tauri/src/lib.rs", 1035],
|
||||
// onMarkRead + isUnread prop threading (mirrors the onMarkUnread prop
|
||||
// already here) for the single-toggle mark-read/unread menu item — a small
|
||||
// overage from load-bearing per-message plumbing, not generic debt growth.
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
channelMessagesKey,
|
||||
dedupeMessagesById,
|
||||
mergeTimelineHistoryMessages,
|
||||
MAX_TIMELINE_MESSAGES,
|
||||
isTimelineWindowContentEvent,
|
||||
normalizeTimelineMessages,
|
||||
sortMessages,
|
||||
} from "@/features/messages/lib/messageQueryKeys";
|
||||
@@ -111,11 +113,40 @@ export function mergeTimelineCacheMessages(
|
||||
current: RelayEvent[],
|
||||
incoming: RelayEvent,
|
||||
): RelayEvent[] {
|
||||
return mergeMessagesWithNormalizer(
|
||||
current,
|
||||
incoming,
|
||||
normalizeTimelineMessages,
|
||||
const normalizedCurrent = dedupeMessagesById(current);
|
||||
const replacedPending = normalizedCurrent.find((message) =>
|
||||
isMatchingPendingMessage(message, incoming),
|
||||
);
|
||||
const incomingWithLocalKey = replacedPending
|
||||
? {
|
||||
...incoming,
|
||||
localKey: replacedPending.localKey ?? replacedPending.id,
|
||||
}
|
||||
: incoming;
|
||||
const incomingLocalKey = getLocalRenderKey(incomingWithLocalKey);
|
||||
const deduped = normalizedCurrent.filter(
|
||||
(message) =>
|
||||
message.id !== incoming.id &&
|
||||
getLocalRenderKey(message) !== incomingLocalKey &&
|
||||
!isMatchingPendingMessage(message, incoming),
|
||||
);
|
||||
const merged = [...deduped, incomingWithLocalKey];
|
||||
// Check backward-paged state on the ORIGINAL current (before dedup+incoming)
|
||||
// so a live event arriving while scrolled back doesn't evict the older roots.
|
||||
const contentCount = current.filter(isTimelineWindowContentEvent).length;
|
||||
if (contentCount > MAX_TIMELINE_MESSAGES) {
|
||||
// Backward-paged state: the cache has grown past MAX_TIMELINE_MESSAGES via
|
||||
// prepend (the user has scrolled back far enough to load older history).
|
||||
// Skip the cap here so a live event doesn't evict the backward-paged roots
|
||||
// and stall pagination. Note: this means the cache can grow beyond
|
||||
// MAX_TIMELINE_MESSAGES while the user is scrolled back — the same gap
|
||||
// that already exists on the normalizeTimelineHistoryMessages path. A
|
||||
// bounded ceiling for both paths is a follow-up; the risk is acceptable
|
||||
// because scrollback sessions are finite and the cap re-engages on the
|
||||
// next channel mount.
|
||||
return sortMessages(merged);
|
||||
}
|
||||
return normalizeTimelineMessages(merged);
|
||||
}
|
||||
|
||||
function createOptimisticMessage(
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
KIND_SYSTEM_MESSAGE,
|
||||
} from "@/shared/constants/kinds";
|
||||
|
||||
const MAX_TIMELINE_MESSAGES = 2_000;
|
||||
export const MAX_TIMELINE_MESSAGES = 2_000;
|
||||
|
||||
export function channelMessagesKey(channelId: string) {
|
||||
return ["channel-messages", channelId] as const;
|
||||
@@ -49,7 +49,7 @@ export function sortMessages(messages: RelayEvent[]) {
|
||||
});
|
||||
}
|
||||
|
||||
function isTimelineWindowContentEvent(event: RelayEvent) {
|
||||
export function isTimelineWindowContentEvent(event: RelayEvent) {
|
||||
return (
|
||||
event.kind === KIND_STREAM_MESSAGE ||
|
||||
event.kind === KIND_STREAM_MESSAGE_V2 ||
|
||||
|
||||
Reference in New Issue
Block a user