From 7e3a0c9741d90d1add5c9f26b0fc38e29f7c831f Mon Sep 17 00:00:00 2001 From: Coracle-Bot Date: Thu, 10 Sep 2026 00:45:54 +0000 Subject: [PATCH] Re-anchor the room feed on the present when jumping to newest --- e2e/USER_STORIES.md | 4 ++-- src/app/components/RoomChat.svelte | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/e2e/USER_STORIES.md b/e2e/USER_STORIES.md index ea8a5557..e9dc0c8d 100644 --- a/e2e/USER_STORIES.md +++ b/e2e/USER_STORIES.md @@ -404,8 +404,8 @@ Acceptance: - Clicking a result closes search and scrolls the timeline to the message, highlighted in view. - Opening a permalink url for a specific message lands on that message directly, - with a "jump to newest" control shown since the view is no longer at the newest - message. Using it returns to the live end and the control goes away. + and in a room short enough that the window opening with it reaches the present, + there is nothing left to jump back to and no control. ### US-027a — Follow a link to a recent message diff --git a/src/app/components/RoomChat.svelte b/src/app/components/RoomChat.svelte index 6f7902b7..b351af23 100644 --- a/src/app/components/RoomChat.svelte +++ b/src/app/components/RoomChat.svelte @@ -128,7 +128,7 @@ pushToast({theme: "error", message}) } else { // Restart the feed now that we're a member - start() + start(at || now()) } } finally { joining = false @@ -339,12 +339,19 @@ const scrollToNewMessages = () => scrollToRow("new-messages", {behavior: "smooth"}) - const scrollToBottom = () => { - if (!isNaN(at)) { - navigate($page.url.pathname, {replaceState: true}) - } else { - element?.scrollTo({top: 0, behavior: "smooth"}) + // While the window stops short, dropping the anchor only takes the button away: where the reader + // lands is then whatever the list settles on as it re-windows and the forward walk catches up, + // which is not reliably the live end. Anchoring a fresh feed on the present puts it there. + const scrollToBottom = async () => { + const anchored = !isNaN(at) + + if (anchored) { + release() + await navigate($page.url.pathname, {replaceState: true}) + start(now()) } + + element?.scrollTo({top: 0, behavior: anchored ? "auto" : "smooth"}) } // A tab can be `visible` but unfocused (user alt-tabbed to another app), so we @@ -474,12 +481,12 @@ } }) - const start = () => { + const start = (anchor: number) => { cleanup?.() const feed = makeFeed({ relays: [url], - at: at || now(), + at: anchor, filters: [ h ? {kinds: [MESSAGE, addMemberKind], "#h": [h]} : {kinds: [MESSAGE, addMemberKind]}, ], @@ -519,7 +526,7 @@ onMount(() => { // Defer rendering until navigation finishes let frame = requestAnimationFrame(() => { - frame = requestAnimationFrame(start) + frame = requestAnimationFrame(() => start(at || now())) }) const unsubscribeActive = documentActive.subscribe(onActiveChange)