fix(ux): cap the in-app toast stack; stable "Unread First" room sort
- Toast queue: a burst of notifications appended unboundedly and could cover the viewport. Cap at 5 in the atom writer, dropping the OLDEST non-sticky toast (sticky = action toasts requiring a click, never dropped). The drop scan excludes the just-appended newest (`length - 1` bound) so a fresh toast is never the one eaten when the cap is full of stickies — it stretches instead. Container gains a maxHeight + overflowY safety net and scrolls the newest (bottom) toast into view if the stack ever overflows. +4 unit tests incl. the cap-full-of-stickies boundary. - "Unread First" room sort left the entire read tail (all counts tie at 0) in arbitrary Map order. factoryRoomIdByUnread now breaks ties by recent activity. Relocated from Home.tsx (module-private) to utils/sort.ts (exported, pure) and unit-tested (equal-count and read-tail cases fall back to activity). Bug-hunt findings from LOTUS_TODO. Three review passes: the second caught that the cap could silently drop the newest notification when full of stickies (real bug, untested boundary) — fixed and covered; a third traced the corrected loop. Gate-green (tsc, eslint, prettier, 920 tests, build). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import { Unread } from '../../types/matrix/room';
|
||||
|
||||
export type SortFunc<T> = (a: T, b: T) => number;
|
||||
|
||||
@@ -42,6 +43,26 @@ export const factoryRoomIdByUnreadCount =
|
||||
return bT - aT;
|
||||
};
|
||||
|
||||
// "Unread First": rooms with unread sort before those without, then by unread
|
||||
// count desc, then — crucially for the large all-read tail where counts tie —
|
||||
// by recent activity, so it isn't left in arbitrary order.
|
||||
export const factoryRoomIdByUnread = (
|
||||
roomToUnread: Map<string, Unread>,
|
||||
mx: MatrixClient,
|
||||
): SortFunc<string> => {
|
||||
const byActivity = factoryRoomIdByActivity(mx);
|
||||
return (a, b) => {
|
||||
const aUnread = roomToUnread.get(a);
|
||||
const bUnread = roomToUnread.get(b);
|
||||
const aHas = (aUnread?.total ?? 0) > 0;
|
||||
const bHas = (bUnread?.total ?? 0) > 0;
|
||||
if (aHas !== bHas) return aHas ? -1 : 1;
|
||||
const byCount = (bUnread?.total ?? 0) - (aUnread?.total ?? 0);
|
||||
if (byCount !== 0) return byCount;
|
||||
return byActivity(a, b);
|
||||
};
|
||||
};
|
||||
|
||||
export const byTsOldToNew: SortFunc<number> = (a, b) => a - b;
|
||||
|
||||
export const byOrderKey: SortFunc<string | undefined> = (a, b) => {
|
||||
|
||||
Reference in New Issue
Block a user