Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions public/channels.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,24 @@
}

let autoScroll = true;
// #314: the #chMessages scroll handler. A stable module-level reference,
// not a fresh closure per init(): a repeat addEventListener of the same
// function is a DOM no-op, so a visit leaves at most one listener, and
// destroy() can take it off the element it was added to again (the same
// leak class as #259/#282, scoped to the element instead of `document`).
// _chScrollEl is only the bookkeeping for add/remove; the handler reads the
// pane off the event, so it works whether or not that pane is still in the
// document.
let _chScrollEl = null;
function onChMessagesScroll(e) {
const el = e.currentTarget;
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 60;
autoScroll = atBottom;
// Optional-chained like every other #chScrollBtn call site in this file:
// a scroll event can reach a detached #chMessages after the page is gone,
// and the button is no longer in the document by then.
document.getElementById('chScrollBtn')?.classList.toggle('hidden', atBottom);
}
let nodeCache = {};
let selectedNode = null;
let observerIataById = {};
Expand Down Expand Up @@ -1660,12 +1678,12 @@
});

const msgEl = document.getElementById('chMessages');
msgEl.addEventListener('scroll', () => {
const atBottom = msgEl.scrollHeight - msgEl.scrollTop - msgEl.clientHeight < 60;
autoScroll = atBottom;
document.getElementById('chScrollBtn').classList.toggle('hidden', atBottom);
});
document.getElementById('chScrollBtn').addEventListener('click', scrollToBottom);
// #314: drop the previous #chMessages before taking the new one, so a
// re-init cannot leave a live handler on a detached element.
if (_chScrollEl && _chScrollEl !== msgEl) _chScrollEl.removeEventListener('scroll', onChMessagesScroll);
_chScrollEl = msgEl;
msgEl.addEventListener('scroll', onChMessagesScroll);
document.getElementById('chScrollBtn')?.addEventListener('click', scrollToBottom);

// Event delegation for node clicks and hovers (click + touchend for mobile reliability)
function handleNodeTap(e) {
Expand Down Expand Up @@ -2021,6 +2039,9 @@

function destroy() {
if (window.ChannelProposals) window.ChannelProposals.unmount();
// #314: the message pane's scroll listener goes with the page.
if (_chScrollEl) _chScrollEl.removeEventListener('scroll', onChMessagesScroll);
_chScrollEl = null;
if (wsHandler) offWS(wsHandler);
wsHandler = null;
if (timeAgoTimer) clearInterval(timeAgoTimer);
Expand Down
14 changes: 14 additions & 0 deletions public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4515,6 +4515,20 @@ button.region-pill-active:hover { opacity: 0.85; color: var(--text-on-accent); }
.multi-select-trigger {
height: 48px;
}
/* #314: two controls the #239 sweep did not cover. Measured at 375x812
with a touch user agent, the swipe tip's "Got it" was 26 px tall and
the packets reveal pill 18 px. Both are compact by design on desktop
(a hint chip and a column-header badge), so the bump is touch-only,
like the rest of this block. Both need the flex centring too: their
labels are smaller than the box they now sit in. */
.gesture-hint-dismiss,
.col-hidden-pill {
min-height: 48px;
min-width: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
}
}

/* #630-3: Status text labels — visually hidden text for screen readers */
Expand Down
1 change: 1 addition & 0 deletions test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ run test-issue-258-column-widths.js
run test-issue-259-nodes-esc-listener.js
run test-issue-282-pktesc-listener.js
run test-issue-282-comment-guards.js
run test-issue-314-chscroll-listener.js
run test-issue-322-comment-guards.js
run test-perf-go-runtime.js
run test-channel-psk-ux.js
Expand Down
Loading
Loading