Add a scroll to top button, reorder the home dashboard on narrow screens, and paint cards further ahead

This commit is contained in:
Coracle-Bot 2026-09-09 01:14:19 +00:00 committed by hodlbod
parent ff3461a020
commit 0d1e60bf1d
8 changed files with 64 additions and 13 deletions

View file

@ -8,10 +8,12 @@
import {displayContentCount} from "@app/content" import {displayContentCount} from "@app/content"
import {inboxSpaceContent} from "@app/inbox" import {inboxSpaceContent} from "@app/inbox"
import {makeSpacePath} from "@app/routes" import {makeSpacePath} from "@app/routes"
const {class: className = ""}: {class?: string} = $props()
</script> </script>
{#if $inboxSpaceContent.length > 0} {#if $inboxSpaceContent.length > 0}
<HomeSection title="Activity" icon={Feed}> <HomeSection title="Activity" icon={Feed} class={className}>
<div class="scroll-container flex items-stretch gap-3 overflow-x-auto px-4 pb-4"> <div class="scroll-container flex items-stretch gap-3 overflow-x-auto px-4 pb-4">
{#each $inboxSpaceContent as { url, timestamp, countsByKind } (url)} {#each $inboxSpaceContent as { url, timestamp, countsByKind } (url)}
<Link <Link

View file

@ -13,6 +13,8 @@
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {HostingError, listTenantRelays, type HostedRelay} from "@app/hosting" import {HostingError, listTenantRelays, type HostedRelay} from "@app/hosting"
const {class: className = ""}: {class?: string} = $props()
let relays = $state<HostedRelay[]>([]) let relays = $state<HostedRelay[]>([])
let loading = $state(true) let loading = $state(true)
@ -33,7 +35,7 @@
}) })
</script> </script>
<HomeSection title="Hosting" icon={Server}> <HomeSection title="Hosting" icon={Server} class={className}>
{#snippet action()} {#snippet action()}
{#if relays.length > 0} {#if relays.length > 0}
<Link href="/settings/hosting" class="button button-neutral button-xs">Manage</Link> <Link href="/settings/hosting" class="button button-neutral button-xs">Manage</Link>

View file

@ -16,6 +16,8 @@
import {followLists, relayLists, router, user} from "@app/core" import {followLists, relayLists, router, user} from "@app/core"
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds" import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
const {class: className = ""}: {class?: string} = $props()
// The hubs most of the user's follows publish to, which is the same set and the same limit // The hubs most of the user's follows publish to, which is the same set and the same limit
// `syncFollowNetwork` reads their lists from. // `syncFollowNetwork` reads their lists from.
const RELAY_LIMIT = 8 const RELAY_LIMIT = 8
@ -77,7 +79,7 @@
}) })
</script> </script>
<HomeSection title="Network" icon={Planet}> <HomeSection title="Network" icon={Planet} class={className}>
<div class="flex flex-col gap-3 px-4 pb-4" bind:this={element}> <div class="flex flex-col gap-3 px-4 pb-4" bind:this={element}>
{#if isEmpty} {#if isEmpty}
<div class="flex flex-col items-center gap-3 pb-4 text-center"> <div class="flex flex-col items-center gap-3 pb-4 text-center">

View file

@ -2,6 +2,7 @@
import {sortBy} from "@welshman/lib" import {sortBy} from "@welshman/lib"
import {COMMENT, NOTE} from "@welshman/util" import {COMMENT, NOTE} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import Cv from "@lib/components/Cv.svelte"
import NoteItem from "@app/components/NoteItem.svelte" import NoteItem from "@app/components/NoteItem.svelte"
import CommentTree from "@app/components/CommentTree.svelte" import CommentTree from "@app/components/CommentTree.svelte"
import type {FeedContext} from "@app/feeds" import type {FeedContext} from "@app/feeds"
@ -29,7 +30,7 @@
) )
</script> </script>
<div class="cv card card-interactive flex flex-col gap-3"> <Cv class="card card-interactive flex flex-col gap-3">
<NoteItem {event} {context} class="" /> <NoteItem {event} {context} class="" />
{#if nodes.length > 0} {#if nodes.length > 0}
<div class="border-line-less flex flex-col border-t"> <div class="border-line-less flex flex-col border-t">
@ -38,4 +39,4 @@
{/each} {/each}
</div> </div>
{/if} {/if}
</div> </Cv>

View file

@ -7,12 +7,13 @@
icon: string icon: string
action?: Snippet action?: Snippet
children: Snippet children: Snippet
class?: string
} }
const {title, icon, action, children}: Props = $props() const {title, icon, action, children, class: className = ""}: Props = $props()
</script> </script>
<section class="flex min-w-0 flex-col border-b border-line last:border-b-0"> <section class="flex min-w-0 flex-col border-b border-line {className}">
<div class="flex items-center justify-between gap-3 px-4 pt-4 pb-3"> <div class="flex items-center justify-between gap-3 px-4 pt-4 pb-3">
<h2 class="flex items-center gap-2 text-base font-bold tracking-wide uppercase"> <h2 class="flex items-center gap-2 text-base font-bold tracking-wide uppercase">
<Icon {icon} size={5} /> <Icon {icon} size={5} />

View file

@ -0,0 +1,36 @@
<script lang="ts">
import {fade} from "svelte/transition"
import AltArrowUp from "@assets/icons/alt-arrow-up.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
const {element}: {element?: Element} = $props()
let scrolled = $state(false)
const scrollToTop = () => element?.scrollTo({top: 0, behavior: "smooth"})
$effect(() => {
if (element) {
const onScroll = () => {
scrolled = element.scrollTop > 800
}
element.addEventListener("scroll", onScroll, {passive: true})
onScroll()
return () => element.removeEventListener("scroll", onScroll)
}
})
</script>
{#if scrolled}
<div transition:fade class="absolute right-4 bottom-20 z-nav mb-sai md:bottom-4 md:mb-0">
<Button
aria-label="Scroll to top"
class="button button-neutral button-circle shadow-xl"
onclick={scrollToTop}>
<Icon icon={AltArrowUp} />
</Button>
</div>
{/if}

View file

@ -103,6 +103,7 @@
Nothing else should reach for it. */ Nothing else should reach for it. */
@utility cv { @utility cv {
content-visibility: auto; content-visibility: auto;
contain-intrinsic-size: auto 16rem;
} }
/* Marks the event a link pointed at, so the reader can pick it out of the conversation once the /* Marks the event a link pointed at, so the reader can pick it out of the conversation once the
jump lands. Brightness rather than a background so it works over any row's own surface. */ jump lands. Brightness rather than a background so it works over any row's own surface. */

View file

@ -5,6 +5,7 @@
import Page from "@lib/components/Page.svelte" import Page from "@lib/components/Page.svelte"
import PageBar from "@lib/components/PageBar.svelte" import PageBar from "@lib/components/PageBar.svelte"
import PageContent from "@lib/components/PageContent.svelte" import PageContent from "@lib/components/PageContent.svelte"
import ScrollToTop from "@lib/components/ScrollToTop.svelte"
import HomeInbox from "@app/components/HomeInbox.svelte" import HomeInbox from "@app/components/HomeInbox.svelte"
import HomeActivity from "@app/components/HomeActivity.svelte" import HomeActivity from "@app/components/HomeActivity.svelte"
import HomeNetwork from "@app/components/HomeNetwork.svelte" import HomeNetwork from "@app/components/HomeNetwork.svelte"
@ -14,6 +15,8 @@
import {HOSTING_ENABLED} from "@app/hosting" import {HOSTING_ENABLED} from "@app/hosting"
import {PLATFORM_RELAYS} from "@app/env" import {PLATFORM_RELAYS} from "@app/env"
let element: Element | undefined = $state()
const today = new Date().toLocaleDateString(undefined, { const today = new Date().toLocaleDateString(undefined, {
weekday: "long", weekday: "long",
month: "short", month: "short",
@ -38,19 +41,22 @@
<span class="hidden text-sm opacity-75 sm:inline">{today}</span> <span class="hidden text-sm opacity-75 sm:inline">{today}</span>
</div> </div>
</PageBar> </PageBar>
<PageContent noPad class="flex flex-col bg-surface"> <PageContent noPad bind:element class="flex flex-col bg-surface">
<div class="flex min-w-0 flex-1 flex-col lg:flex-row lg:items-stretch"> <div class="flex min-w-0 flex-1 flex-col lg:flex-row lg:items-stretch">
<div class="flex min-w-0 flex-col lg:flex-[2] lg:border-r lg:border-line"> <!-- The columns are `contents` below lg, which puts every section in one flow and lets the
rail's health checks sit between the inbox and the rest of the main column. -->
<div class="contents lg:flex lg:min-w-0 lg:flex-[2] lg:flex-col lg:border-r lg:border-line">
<HomeInbox /> <HomeInbox />
<HomeActivity /> <HomeActivity class="order-last" />
<HomeNetwork /> <HomeNetwork class="order-last border-b-0" />
</div> </div>
<div class="flex min-w-0 flex-col border-t border-line lg:w-96 lg:shrink-0 lg:border-t-0"> <div class="contents lg:flex lg:w-96 lg:min-w-0 lg:shrink-0 lg:flex-col">
<HomeHealthChecks /> <HomeHealthChecks />
{#if HOSTING_ENABLED} {#if HOSTING_ENABLED}
<HomeHosting /> <HomeHosting class="border-b-0 max-lg:hidden" />
{/if} {/if}
</div> </div>
</div> </div>
</PageContent> </PageContent>
<ScrollToTop {element} />
</Page> </Page>