From 5d40a9bdc5b5a566aa42fc5ad1f0a13df6277042 Mon Sep 17 00:00:00 2001 From: Coracle-Bot Date: Fri, 18 Sep 2026 17:41:46 +0000 Subject: [PATCH] Carry a room loading state in the page bar instead of the transcript (#569) --- src/app/components/RoomChat.svelte | 27 +++++++++++---------------- src/app/components/SpaceBar.svelte | 8 ++++++++ src/app/loading.ts | 5 +++++ 3 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 src/app/loading.ts diff --git a/src/app/components/RoomChat.svelte b/src/app/components/RoomChat.svelte index 541507c0..6ccba8de 100644 --- a/src/app/components/RoomChat.svelte +++ b/src/app/components/RoomChat.svelte @@ -44,7 +44,8 @@ prependParent, } from "@app/rooms" import {userSettingsValues} from "@app/settings" - import {makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds" + import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds" + import {pageLoading} from "@app/loading" import {checked, deferredRoomPath, setChecked} from "@app/notifications" import {makeRoomPath} from "@app/routes" import {pendingShare, type Share} from "@app/share" @@ -396,11 +397,16 @@ // held back for that frame rather than showing the wrong end of the conversation and jumping. const awaitingJump = $derived(!isNaN(at) && !jumpSettled) - // There is always more history until the feed says otherwise, so this stays up rather than - // blinking between spans while it walks a quiet room. - const loadingBackward = $derived($older?.status !== "exhausted") const reachedStartOfHistory = $derived($older?.status === "exhausted") + // A room paged from an anchor walks in both directions at once, and neither walk is worth a + // loader of its own in the transcript — the page bar carries one for the pair of them. + $effect(() => { + pageLoading.set(isFeedLoading($older) || isFeedLoading($newer)) + + return () => pageLoading.set(false) + }) + // Claim the share once we're on screen. Sharing into the room you're already looking at // doesn't re-create this component, so this can't be read once on mount. $effect(() => { @@ -440,10 +446,6 @@ // from here on arrives through the repository rather than through a forward walk. const windowStopsShort = $derived(!isNaN(at) && $newer?.status !== "exhausted") - // With no messages between them the two loaders would sit against each other, so this one yields - // while the other is still running. - const loadingForward = $derived(windowStopsShort && !(elements.length === 0 && loadingBackward)) - // While the window stops short, the bottom of the container is not the bottom of the // conversation, so the button is the way back to the live end rather than a scroll — which is // why it clears `at` instead of scrolling. Once the two are the same place, scroll position is @@ -636,11 +638,6 @@ {:else} - {#if loadingForward} -

- Looking for messages... -

- {/if}

- {#if loadingBackward} - Looking for messages... - {:else if reachedStartOfHistory} + {#if reachedStartOfHistory} End of message history {/if}

diff --git a/src/app/components/SpaceBar.svelte b/src/app/components/SpaceBar.svelte index 1aba4fd3..6c80333c 100644 --- a/src/app/components/SpaceBar.svelte +++ b/src/app/components/SpaceBar.svelte @@ -3,9 +3,12 @@ import {page} from "$app/stores" import {displayRelayUrl} from "@welshman/util" import ArrowLeft from "@assets/icons/arrow-left.svg?dataurl" + import {fade} from "@lib/transition" import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" import PageBar from "@lib/components/PageBar.svelte" + import Spinner from "@lib/components/Spinner.svelte" + import {pageLoading} from "@app/loading" import {decodeRelay} from "@app/relays" interface Props { @@ -37,6 +40,11 @@
{@render title?.()}
+ {#if $pageLoading} +
+ +
+ {/if}
{displayRelayUrl(url)} diff --git a/src/app/loading.ts b/src/app/loading.ts new file mode 100644 index 00000000..9bd000d2 --- /dev/null +++ b/src/app/loading.ts @@ -0,0 +1,5 @@ +import {writable} from "svelte/store" + +// Whether the page is still waiting on content. The page bar is where that shows, so a view +// says so here rather than putting a loader among its own rows. +export const pageLoading = writable(false)