Commit e590609b6343
tarball2 files changed+20-3
@@ -107,6 +107,7 @@
107107
import { isRoomMuted, mutingSpace, type MutedSpaces } from './lib/mute-model';108108
import { paneBadges } from './lib/pane-badges';109109
import { withoutLiveMarker } from './lib/read-marker';110
+ import { selectedRow } from './lib/selected-row';110111
import { loadMutedSpaces, mutedSpaces } from './lib/muted-spaces';111112
import { loadMessageSound } from './lib/message-sound';112113
import {@@ -767,6 +768,19 @@
767768
return count;768769
}769770
771
+ /** A selected message's toolbar and menu would slide away under the pointer if new messages kept pulling the view down. */772
+ $: followHeld = $selectedRow !== null;773
+ let wasHeld = false;774
+ $: releaseFollow(followHeld);775
+776
+ function releaseFollow(held: boolean): void {777
+ if (wasHeld && !held && stickToBottom) {778
+ scrollToBottom(true);779
+ queueReadReceipt();780
+ }781
+ wasHeld = held;782
+ }783
+770784
let windowActive = document.visibilityState === 'visible' && document.hasFocus();771785
let liveFrom: string | null = null;772786
let liveRoom: string | null = null;@@ -850,7 +864,7 @@
850864
timelineViewport = viewport;851865
const observer = new ResizeObserver(() => {852866
if (timelineViewport !== viewport || viewport.roomId !== $selectedRoomId) return;853
- if (stickToBottom) viewport.scrollToBottom(false);867
+ if (stickToBottom && !followHeld) viewport.scrollToBottom(false);854868
else {855869
viewport.restore();856870
measureLatest();@@ -878,7 +892,7 @@
878892
const tail = `${roomId}|${lastEventId(list)}`;879893
if (!followedTail.startsWith(`${roomId}|`)) followedSince = performance.now();880894
const arrived = tail !== followedTail && performance.now() - followedSince > SETTLE_MS;881
- if (stickToBottom) timelineViewport.scrollToBottom(arrived && !motionActive());895
+ if (stickToBottom && !followHeld) timelineViewport.scrollToBottom(arrived && !motionActive());882896
else timelineViewport.restore();883897
followedTail = tail;884898
queueReadReceipt();@@ -923,7 +937,7 @@
923937
async function sendReadReceipt(): Promise<void> {924938
const roomId = $selectedRoomId;925939
if (!roomId || roomId === peeking) return;926
- if (document.hidden || !document.hasFocus() || !stickToBottom) return;940
+ if (document.hidden || !document.hasFocus() || !stickToBottom || followHeld) return;927941
const list = $timelines[roomId] ?? [];928942
for (let i = list.length - 1; i >= 0; i -= 1) {929943
const entry = list[i];@@ -280,6 +280,7 @@
280280
function react(key: string, shortcode: string | null) {281281
if (shortcode && isImageKey(key)) onReact(event.event_id ?? '', key, shortcode);282282
else onReact(event.event_id ?? '', key);283
+ select(false);283284
}284285
285286
function reply() {@@ -288,6 +289,7 @@
288289
event_id: event.event_id,289290
details: { kind: 'Ready', sender: event.sender, content: event.content, timestamp_ms: event.timestamp_ms }290291
});292
+ select(false);291293
}292294
293295
$: editableBody =@@ -298,6 +300,7 @@
298300
function edit() {299301
if (!event.event_id || editableBody === null || !onEdit) return;300302
onEdit(event.event_id, editableBody);303
+ select(false);301304
}302305
303306
function reactorNames(senders: string[]): string {