fix(frontend): bound Call-to-Play projection work

A valid remote view could make the renderer reduce and mount thousands of calls
every second. A single long chat also re-sorted its growing message array after
every append during every reduction.

Keep only the highest-ranked 128 nominations and latest 256 messages for UI
projection. Events are sorted once before folding, so chat messages remain in
stable order without repeated growing-array sorts. Backend author history and
creator authority are unchanged.

Test Plan:
- `just frontend-test` -- passed; 94 frontend tests passed.
- `just fmt` -- TypeScript formatting completed; the recipe then hit the
  pre-existing generated security-report Markdown-lint failures.
- `git diff --cached --check` -- passed.
This commit is contained in:
ddidderr committed 2026-09-12 12:28:25 +02:00
1 parent 30663fea34
commit 078294ac83
2 files changed
+42 -2

No files matched your search

@@ -9,6 +9,8 @@ import {
export const CHECKIN_LEAD_MS = 15 * 60_000;
export const EXPIRED_RETENTION_MS = 5 * 60_000;
export const TERMINAL_RETENTION_MS = 15 * 60_000;
export const MAX_VISIBLE_CALL_TO_PLAY_NOMINATIONS = 128;
export const MAX_VISIBLE_CALL_TO_PLAY_MESSAGES = 256;
export const CALL_TO_PLAY_CONNECTING_MESSAGE =
'Call to Play is still connecting to the LAN. Try again in a moment.';
@@ -138,7 +140,7 @@ export const reduceCallToPlayEvents = (
const nominations = [...groupEvents(input).values()]
.map(events => deriveNomination(events, now))
.filter((nomination): nomination is Nomination => nomination !== null);
return sortNominations(nominations);
return sortNominations(nominations).slice(0, MAX_VISIBLE_CALL_TO_PLAY_NOMINATIONS);
};
const groupEvents = (
@@ -189,6 +191,9 @@ const deriveNomination = (
for (const event of events) {
if (compareEvents(event, create) > 0) applyEvent(nomination, event);
}
if (nomination.messages.length > MAX_VISIBLE_CALL_TO_PLAY_MESSAGES) {
nomination.messages = nomination.messages.slice(-MAX_VISIBLE_CALL_TO_PLAY_MESSAGES);
}
if (nomination.state === 'open'
&& (readyCountOf(nomination, now) >= nomination.maxPlayers || now >= nomination.deadline)
@@ -237,7 +242,6 @@ const applyEvent = (nomination: MutableNomination, event: CallToPlayViewEvent):
text: message.text,
at: event.at,
});
nomination.messages.sort((a, b) => a.at - b.at || a.id.localeCompare(b.id));
return;
}