Skip to content

Commit 21a5d09

Browse files
JSKittyclaude
andcommitted
perf(frontend): kill O(n^2) scans in group-member sort + chat-open system events
Two more quadratics: - group-member picker sort ran arrSelectedGroupMembers.indexOf AND arrChats.find inside the comparator -> O(profiles * (members + chats) * log). Pre-index both into Maps; comparator is O(1) per lookup. - on chat open, system-event ingest deduped against loaded messages via initialMessages.find and checked known profiles via arrProfiles.some, both per event -> O(systemEvents * messages) + O(systemEvents * profiles). Hoist both to Sets, O(1) per check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 57dfb62 commit 21a5d09

1 file changed

Lines changed: 14 additions & 6 deletions

File tree

src/main.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8371,16 +8371,20 @@ async function openChat(contact) {
83718371
// messages into view (same on-demand windowing as messages).
83728372
try {
83738373
const systemEvents = await invoke('get_system_events', { conversationId: contact });
8374+
// Index once: dedup-vs-loaded-messages and the known-profile check were each O(n) per
8375+
// system event (O(systemEvents * messages) + O(systemEvents * profiles)) on chat open.
8376+
const initialMsgIds = new Set(initialMessages.map(m => m.id));
8377+
const knownProfileIds = new Set(arrProfiles.map(p => p.id));
83748378
const buffer = (systemEvents || [])
8375-
.filter(event => !initialMessages.find(m => m.id === event.id))
8379+
.filter(event => !initialMsgIds.has(event.id))
83768380
.map(event => ({
83778381
id: event.id,
83788382
at: event.at,
83798383
// Rebuild from the actor's CURRENT cached name rather than the npub-baked
83808384
// stored content. Fetch unknown profiles so the next open resolves them.
83818385
content: (() => {
83828386
const np = event.member_npub;
8383-
if (np && !arrProfiles.some(p => p.id === np) && !strangerProfileRequested.has(np)) {
8387+
if (np && !knownProfileIds.has(np) && !strangerProfileRequested.has(np)) {
83848388
strangerProfileRequested.add(np);
83858389
invoke('load_profile', { npub: np }).catch(() => {});
83868390
}
@@ -12892,9 +12896,13 @@ function renderCreateGroupList(filterText = '') {
1289212896
}
1289312897

1289412898
// Sort profiles: selected members first (by selection order), then unselected by last message time
12899+
// Pre-index selection order + chat timestamps so the comparator is O(1) per lookup — `indexOf`
12900+
// + `arrChats.find` per comparison made this O(profiles * (members + chats) * log).
12901+
const selIndexById = new Map(arrSelectedGroupMembers.map((id, i) => [id, i]));
12902+
const chatTsById = new Map(arrChats.map(c => [c.id, getChatSortTimestamp(c)]));
1289512903
const sortedProfiles = [...arrProfiles].sort((a, b) => {
12896-
const aSelectedIndex = arrSelectedGroupMembers.indexOf(a?.id);
12897-
const bSelectedIndex = arrSelectedGroupMembers.indexOf(b?.id);
12904+
const aSelectedIndex = selIndexById.get(a?.id) ?? -1;
12905+
const bSelectedIndex = selIndexById.get(b?.id) ?? -1;
1289812906
const aSelected = aSelectedIndex !== -1;
1289912907
const bSelected = bSelectedIndex !== -1;
1290012908

@@ -12908,8 +12916,8 @@ function renderCreateGroupList(filterText = '') {
1290812916
}
1290912917

1291012918
// For unselected members: sort by last message time (most recent first)
12911-
const aChatTimestamp = getChatSortTimestamp(arrChats.find(c => c.id === a?.id) || {});
12912-
const bChatTimestamp = getChatSortTimestamp(arrChats.find(c => c.id === b?.id) || {});
12919+
const aChatTimestamp = chatTsById.get(a?.id) || 0;
12920+
const bChatTimestamp = chatTsById.get(b?.id) || 0;
1291312921

1291412922
// If both have timestamps, sort by most recent
1291512923
if (aChatTimestamp && bChatTimestamp) {

0 commit comments

Comments
 (0)