diff options
| author | Adam Soltys <[email protected]> | 2026-10-09 20:48:38 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-09 20:48:38 -0700 |
| commit | 557831a04d71a155699124395f55d394ad5b2f4f (patch) | |
| tree | 41d1fb0523692d2db1914239ef48da94cc972c2f /src/features | |
| parent | 095caadcba5a63f5969e50777d7e482c1d32174e (diff) | |
| download | coinosv3-557831a04d71a155699124395f55d394ad5b2f4f.tar.gz coinosv3-557831a04d71a155699124395f55d394ad5b2f4f.zip | |
Messages: a tap on a conversation's face opens THAT person. The avatar's tap handlers were attached with addEventListener, holding the person it was first drawn for; the morph transplants only h()'s on* properties, so when a new message re-sorted the list and the rows were patched in place, a face kept its old handler — a tap on Bill opened someone else's profile, and once that page had opened, a fresh render hid the evidence. The avatar now sets onclick/onpointerdown claimed in _evs (transplanted like any h() handler), and conversation rows are keyed by person so a re-sort moves them. tools/dm-row-resort-test.js (on the old code, Bill's face opened Alice).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
Diffstat (limited to 'src/features')
| -rw-r--r-- | src/features/messages.js | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 90b080b..b3066c0 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -3699,8 +3699,15 @@ export function messagesFeature(ctx) { // and unlike prefetching every row on paint (which cost sixty requests // for twenty rows and starved the profile batch) this only ever asks // about the person actually being tapped. - node.addEventListener('pointerdown', () => { try { prefetchProfilePage(pk); } catch {} }, { passive: true }); - node.addEventListener('click', (e) => { e.stopPropagation(); openProfile(pk); }); + // As on* properties claimed in _evs, the way h() attaches handlers, so + // the morph transplants them: a list that re-sorts (a new message + // lifting a conversation to the top) patches this node into a + // DIFFERENT person's avatar, and an addEventListener'd handler stayed + // behind holding the old person — a tap on Bill's row opened someone + // else's profile, until the next fresh render. + node.onpointerdown = () => { try { prefetchProfilePage(pk); } catch {} }; + node.onclick = (e) => { e.stopPropagation(); openProfile(pk); }; + (node._evs || (node._evs = new Set())).add('onpointerdown').add('onclick'); } return hook('wrapAvatar', pk, node) || hatStamp(pk, node) || node; }; @@ -12407,6 +12414,9 @@ export function messagesFeature(ctx) { ? h('div', { class: 'list' }, shownDms.map((row) => row.g ? wnGroupRow(row.g) : (({ peer, last, unread }) => h('div', { class: 'item chat-thread-row' + (unread ? ' unread' : ''), + // keyed: a new message re-sorts the list, and the row moves + // with its person instead of being repainted as someone else + 'data-key': 'dm:' + peer, onClick: () => { ui.msgView = 'dm'; ui.msgPeer = peer; ui.msgStick = true; render(); }, }, avatar(peer), @@ -12838,6 +12848,7 @@ export function messagesFeature(ctx) { for (const pk of wn.accounts(g).slice(0, 12)) profileOf(pk); return h('div', { class: 'item chat-thread-row' + (unread ? ' unread' : ''), + 'data-key': 'grp:' + g.id, onClick: () => { ui.msgView = 'grp'; ui.msgPeer = g.id; ui.msgStick = true; ui.msgMembers = false; render(); }, }, h('div', { class: 'chat-avatar fallback' }, name.slice(0, 2)), |
