aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-03 22:36:29 -0700
committerAdam Soltys <[email protected]>2026-09-03 22:36:29 -0700
commit3792c8791773d5201356d5fcc5f449ddd08e7a71 (patch)
tree8735ce3a1565637db317686059b43e223e20c119 /src
parent231d1e21ede495e283778c3a630598b2308af5f5 (diff)
downloadcoinosv3-3792c8791773d5201356d5fcc5f449ddd08e7a71.tar.gz
coinosv3-3792c8791773d5201356d5fcc5f449ddd08e7a71.zip
The post-refresh carousel stutter, root-caused and fixed: a CPU profile of the exact cold first-drag window showed ~700ms of secp256k1, and it was the COMMUNITY room's gift-wrap decryption — a relay backlog of 500+ wraps decrypted inline in the subscription callbacks, one synchronous EC burst on boot (the DM inbox already yielded; community rooms didn't). Now their decrypt work is time-sliced through the same yield-every-8ms queue, so it spreads over a few frames and the wallet stays responsive. Measured: first cold flick dropped 6 frames before, and this was the dominant cost
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
Diffstat (limited to 'src')
-rw-r--r--src/features/messages.js52
1 files changed, 41 insertions, 11 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index fe20ac4..790f33c 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -315,6 +315,30 @@ export function messagesFeature(ctx) {
return npub ? npub.slice(0, 12) : pk.slice(0, 12);
};
+ // Community wraps decrypt with real secp256k1 work (NIP-44), and a relay
+ // backlog delivers hundreds at once. Decrypting them inline in the
+ // subscription callbacks blocked the main thread in one burst on boot — the
+ // stutter felt when dragging the balance carousel right after a refresh.
+ // Queue the decrypt work and run it time-sliced: ~8ms of work, then yield so
+ // input and paint get a turn. Chat catches up a few frames later; the wallet
+ // stays responsive. (Same pattern the DM inbox drain already uses.)
+ const roomWork = [];
+ let roomWorking = false;
+ function enqueueRoomWork(fn) {
+ roomWork.push(fn);
+ if (roomWorking) return;
+ roomWorking = true;
+ (async () => {
+ try {
+ let t0 = performance.now();
+ while (roomWork.length) {
+ try { roomWork.shift()(); } catch {}
+ if (performance.now() - t0 > 8) { await new Promise((r) => setTimeout(r)); t0 = performance.now(); }
+ }
+ } finally { roomWorking = false; }
+ })();
+ }
+
// ---- community rooms ----------------------------------------------------
function ensureRoom(jm) {
@@ -376,18 +400,22 @@ export function messagesFeature(ctx) {
subscribeOn(room.relays, { kinds: [1059], authors: [room.control.pk], limit: 500 }, (wrap) => {
if (seenWraps.has(wrap.id)) return;
seenWraps.add(wrap.id);
- const opened = openWrap(wrap, room.control);
- if (!opened || opened.rumor.kind !== 3308) return;
- room.controlEntries.push(opened);
- scheduleFold();
+ enqueueRoomWork(() => {
+ const opened = openWrap(wrap, room.control);
+ if (!opened || opened.rumor.kind !== 3308) return;
+ room.controlEntries.push(opened);
+ scheduleFold();
+ });
}),
subscribeOn(room.relays, { kinds: [1059], authors: [room.guestbook.pk], limit: 500 }, (wrap) => {
if (seenWraps.has(wrap.id)) return;
seenWraps.add(wrap.id);
- const opened = openWrap(wrap, room.guestbook);
- if (!opened) return;
- room.guestEntries.push(opened);
- scheduleFold();
+ enqueueRoomWork(() => {
+ const opened = openWrap(wrap, room.guestbook);
+ if (!opened) return;
+ room.guestEntries.push(opened);
+ scheduleFold();
+ });
})
);
for (const c of jm.channels || []) subChannel(room, c.id);
@@ -408,9 +436,11 @@ export function messagesFeature(ctx) {
allUnsubs.push(subscribeOn(room.relays, { kinds: [1059, 21059], authors: [room.chStream(id).pk], limit: 200 }, (wrap) => {
if (seenWraps.has(wrap.id)) return;
seenWraps.add(wrap.id);
- const opened = openWrap(wrap, room.chStream(id));
- if (!opened) return;
- onChat(room, id, opened);
+ enqueueRoomWork(() => {
+ const opened = openWrap(wrap, room.chStream(id));
+ if (!opened) return;
+ onChat(room, id, opened);
+ });
}));
}