Read route params from the params prop (#559)

This commit is contained in:
Coracle-Bot 2026-09-16 17:14:43 +00:00 committed by hodlbod
parent ed7641117a
commit 17302a75ee
28 changed files with 106 additions and 83 deletions

View file

@ -172,6 +172,9 @@ callbacks and hot paths.
2. Use `+page.svelte` for page component
3. Use `+layout.svelte` for shared layouts
4. Top-level sync logic goes in root `+layout.svelte`
5. Read params from the `params` prop, typed with `PageProps`/`LayoutProps` from the route's own
`./$types`. SvelteKit passes them down in the same update as the component swap, while the
`page` store is set a tick later. Only code outside `src/routes/` reads `$page.params`.
### Loading Data from Network

View file

@ -1,18 +1,19 @@
<script lang="ts">
import {onMount} from "svelte"
import * as nip19 from "nostr-tools/nip19"
import type {MakeNonOptional} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {Address, getIdFilters} from "@welshman/util"
import {LOCAL_RELAY_URL} from "@welshman/net"
import {page} from "$app/stores"
import {goto} from "$app/navigation"
import {decodePubkey} from "@lib/util"
import Spinner from "@lib/components/Spinner.svelte"
import {network} from "@app/core"
import {goToEvent, makeProfilePath} from "@app/routes"
import type {PageProps} from "./$types"
const {bech32} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {bech32} = params
const attemptToNavigate = async () => {
const profilePubkey = decodePubkey(bech32)

View file

@ -1,5 +1,4 @@
<script lang="ts">
import type {Snippet} from "svelte"
import {onMount} from "svelte"
import {page} from "$app/stores"
import {sleep} from "@welshman/lib"
@ -21,12 +20,9 @@
import {chatSearch} from "@app/chats"
import {pushModal} from "@app/modal"
import {shouldUnwrap} from "@app/sync"
import type {LayoutProps} from "./$types"
type Props = {
children?: Snippet
}
const {children}: Props = $props()
const {children, params}: LayoutProps = $props()
const startChat = () => pushModal(ChatStart)
@ -78,7 +74,7 @@
{/key}
</Page>
{#if !$page.params.chat}
{#if !params.chat}
<FAB onclick={startChat}>
<Icon icon={ChatSquarePlus} size={7} />
</FAB>

View file

@ -11,8 +11,6 @@
<script lang="ts">
import {onMount} from "svelte"
import {page} from "$app/stores"
import type {MakeNonOptional} from "@welshman/lib"
import {append, uniq} from "@welshman/lib"
import Chat from "@app/components/Chat.svelte"
import {splitChatId} from "@app/chats"
@ -20,8 +18,11 @@
import {pushToast} from "@app/toast"
import {Push} from "@app/push"
import {user} from "@app/core"
import type {PageProps} from "./$types"
const {chat} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {chat} = params
const pubkeys = uniq(append($user.pubkey, splitChatId(chat)))
onMount(async () => {

View file

@ -1,14 +1,9 @@
<script lang="ts">
import type {Snippet} from "svelte"
import {page} from "$app/stores"
import type {LayoutProps} from "./$types"
type Props = {
children?: Snippet
}
const {children}: Props = $props()
const {children, params}: LayoutProps = $props()
</script>
{#key $page.params.npub}
{#key params.npub}
{@render children?.()}
{/key}

View file

@ -1,8 +1,6 @@
<script lang="ts">
import {onMount} from "svelte"
import {page} from "$app/stores"
import {goto} from "$app/navigation"
import type {MakeNonOptional} from "@welshman/lib"
import type {Filter} from "@welshman/util"
import {ROOMS, NOTE, FOLLOWS} from "@welshman/util"
import {outbox} from "@welshman/util"
@ -23,8 +21,11 @@
router,
user,
} from "@app/core"
import type {PageProps} from "./$types"
const {npub} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {npub} = params
const pubkey = decodePubkey(npub)

View file

@ -1,12 +1,7 @@
<script lang="ts">
import type {Snippet} from "svelte"
import type {LayoutProps} from "./$types"
type Props = {
children?: Snippet
params: {relay?: string}
}
const {children, params}: Props = $props()
const {children, params}: LayoutProps = $props()
</script>
{#key params.relay}

View file

@ -4,7 +4,6 @@
</script>
<script lang="ts">
import type {Snippet} from "svelte"
import {page} from "$app/stores"
import type {Maybe} from "@welshman/lib"
import {once} from "@welshman/lib"
@ -25,15 +24,11 @@
import {relaysPendingTrust} from "@app/policies"
import {decodeRelay} from "@app/relays"
import {makeSpacePath} from "@app/routes"
import type {LayoutProps} from "./$types"
type Props = {
children?: Snippet
params: Record<string, string>
}
const {children, params}: LayoutProps = $props()
const {children, params}: Props = $props()
const url = decodeRelay($page.params.relay!)
const url = decodeRelay(params.relay)
const authError = deriveRelayAuthError(url)

View file

@ -1,12 +1,14 @@
<script lang="ts">
import {page} from "$app/stores"
import theme from "tailwindcss/defaultTheme"
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
import {decodeRelay} from "@app/relays"
import {goToSpace} from "@app/routes"
import SpaceMenu from "@app/components/SpaceMenu.svelte"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const md = parseFloat(theme.screens.md) * 16
let width = $state(window.innerWidth)

View file

@ -1,6 +1,4 @@
<script lang="ts">
import {page} from "$app/stores"
import type {MakeNonOptional} from "@welshman/lib"
import InfoCircle from "@assets/icons/info-circle.svg?dataurl"
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
import Button from "@lib/components/Button.svelte"
@ -13,8 +11,11 @@
import RoomSearch from "@app/components/RoomSearch.svelte"
import {decodeRelay} from "@app/relays"
import {pushModal} from "@app/modal"
import type {PageProps} from "./$types"
const {h, relay} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {h, relay} = params
const url = decodeRelay(relay)
const showRoomSearch = () => pushModal(RoomSearch, {url, h})

View file

@ -1,5 +1,4 @@
<script lang="ts">
import {page} from "$app/stores"
import {displayRelayUrl} from "@welshman/util"
import Home from "@assets/icons/home.svg?dataurl"
import ShieldUser from "@assets/icons/shield-user.svg?dataurl"
@ -16,8 +15,11 @@
import SpaceFeaturedContent from "@app/components/SpaceFeaturedContent.svelte"
import {relays} from "@app/core"
import {decodeRelay} from "@app/relays"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const relay = $relays.one(url)
</script>

View file

@ -1,6 +1,5 @@
<script lang="ts">
import {derived} from "svelte/store"
import {page} from "$app/stores"
import Server from "@assets/icons/server.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte"
@ -11,8 +10,11 @@
import ActivityFeed from "@app/components/hosting/ActivityFeed.svelte"
import {deriveHostedRelay, deriveRelayActivity} from "@app/hosting"
import {decodeRelay} from "@app/relays"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const hostedRelay = deriveHostedRelay(url)
const relayActivity = deriveRelayActivity(derived(hostedRelay, $h => $h.relay?.id))
</script>

View file

@ -2,7 +2,6 @@
import {onDestroy, onMount} from "svelte"
import {readable} from "svelte/store"
import type {Readable} from "svelte/store"
import {page} from "$app/stores"
import {goto} from "$app/navigation"
import {sortBy, partition, spec, max, pushToMapKey} from "@welshman/lib"
import type {Maybe} from "@welshman/lib"
@ -24,8 +23,11 @@
import {makeCommentFilter} from "@app/content"
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
import {makeArticleCreatePath} from "@app/routes"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const context = makeFeedContext({relays: [url]})
onDestroy(context.cleanup)

View file

@ -1,9 +1,7 @@
<script lang="ts">
import {onDestroy} from "svelte"
import {derived} from "svelte/store"
import {page} from "$app/stores"
import {formatTimestamp, sleep, uniq} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
import {Article} from "@welshman/domain"
import Link from "@lib/components/Link.svelte"
import PageContent from "@lib/components/PageContent.svelte"
@ -24,8 +22,11 @@
import {decodeRelay} from "@app/relays"
import {displayReadingTime} from "@app/articles"
import {makeSpacePath} from "@app/routes"
import type {PageProps} from "./$types"
const {relay, address} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {relay, address} = params
const url = decodeRelay(relay)
const context = makeFeedContext({relays: [url]})
const event = deriveEvent(address, [url])

View file

@ -28,6 +28,7 @@
import {makeArticlePath} from "@app/routes"
import {compressFileForUpload, uploadFile} from "@app/uploads"
import {pushToast} from "@app/toast"
import type {PageProps} from "./$types"
type Values = {
d?: string
@ -38,7 +39,9 @@
topics?: string[]
}
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const h = $page.url.searchParams.get("h") ?? undefined
const shareToChat = $page.url.searchParams.has("shareToChat")

View file

@ -3,7 +3,6 @@
import cx from "classnames"
import type {Readable} from "svelte/store"
import {readable} from "svelte/store"
import {page} from "$app/stores"
import type {Maybe} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {EVENT_TIME} from "@welshman/util"
@ -37,8 +36,11 @@
import {decodeRelay} from "@app/relays"
import {makeCommentFilter} from "@app/content"
import {makeCalendarFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const context = makeFeedContext({relays: [url]})
const makeEvent = () => pushModal(CalendarEventCreate, {url})

View file

@ -6,9 +6,7 @@
import {onDestroy} from "svelte"
import cx from "classnames"
import {derived, readable} from "svelte/store"
import {page} from "$app/stores"
import {sleep} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {getCommentFiltersForRoot} from "@welshman/util"
import {TimeEvent} from "@welshman/domain"
@ -29,8 +27,11 @@
import {deriveEvent, deriveEvents} from "@app/repository"
import {makeFeedContext} from "@app/feeds"
import {decodeRelay} from "@app/relays"
import type {PageProps} from "./$types"
const {relay, address} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {relay, address} = params
const url = decodeRelay(relay)
const context = makeFeedContext({relays: [url]})
const event = deriveEvent(address, [url])

View file

@ -1,5 +1,4 @@
<script lang="ts">
import {page} from "$app/stores"
import ChatRound from "@assets/icons/chat-round.svg?dataurl"
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
import Button from "@lib/components/Button.svelte"
@ -9,8 +8,11 @@
import RoomChat from "@app/components/RoomChat.svelte"
import {decodeRelay} from "@app/relays"
import {pushModal} from "@app/modal"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const showSpaceSearch = () => pushModal(SpaceSearch, {url})
</script>

View file

@ -35,8 +35,11 @@
} from "@app/classifieds"
import {reader} from "@app/core"
import {pushModal} from "@app/modal"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const context = makeFeedContext({relays: [url]})
onDestroy(context.cleanup)

View file

@ -1,9 +1,7 @@
<script lang="ts">
import {onDestroy} from "svelte"
import {derived} from "svelte/store"
import {page} from "$app/stores"
import {sleep} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
import {Classified} from "@welshman/domain"
import PageContent from "@lib/components/PageContent.svelte"
import Spinner from "@lib/components/Spinner.svelte"
@ -16,8 +14,11 @@
import {deriveEvent} from "@app/repository"
import {makeFeedContext} from "@app/feeds"
import {decodeRelay} from "@app/relays"
import type {PageProps} from "./$types"
const {relay, address} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {relay, address} = params
const url = decodeRelay(relay)
const context = makeFeedContext({relays: [url]})
const event = deriveEvent(address, [url])

View file

@ -1,6 +1,5 @@
<script lang="ts">
import {derived} from "svelte/store"
import {page} from "$app/stores"
import {removeUndefined, sortBy} from "@welshman/lib"
import UsersGroup from "@assets/icons/users-group-rounded.svg?dataurl"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
@ -23,8 +22,11 @@
import {deriveDisplaysByPubkey} from "@app/social"
import {decodeRelay} from "@app/relays"
import {pushModal} from "@app/modal"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const roles = $relayRoles.forUrl(url).$
const members = $relayMemberLists.forUrl(url)
const memberRoles = deriveSpaceMemberRoles(url)

View file

@ -2,7 +2,6 @@
import {onDestroy, onMount} from "svelte"
import {readable} from "svelte/store"
import type {Readable} from "svelte/store"
import {page} from "$app/stores"
import {sortBy, partition, spec, pushToMapKey, max} from "@welshman/lib"
import type {Maybe} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
@ -21,8 +20,11 @@
import {makeCommentFilter} from "@app/content"
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
import {pushModal} from "@app/modal"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const context = makeFeedContext({relays: [url]})
onDestroy(context.cleanup)

View file

@ -1,9 +1,7 @@
<script lang="ts">
import {onDestroy} from "svelte"
import {derived} from "svelte/store"
import {page} from "$app/stores"
import {sleep} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
import {getCommentFiltersForRoot} from "@welshman/util"
import {deriveEventsAsc} from "@welshman/store"
import {ZapGoal} from "@welshman/domain"
@ -23,8 +21,11 @@
import {deriveEvent, deriveEventsById} from "@app/repository"
import {makeFeedContext} from "@app/feeds"
import {decodeRelay} from "@app/relays"
import type {PageProps} from "./$types"
const {relay, id} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {relay, id} = params
const url = decodeRelay(relay)
const context = makeFeedContext({relays: [url]})
const event = deriveEvent(id, [url])

View file

@ -24,8 +24,11 @@
import {deriveUserIsSpaceAdmin} from "@app/management"
import {makeLibraryPath} from "@app/routes"
import {pushModal} from "@app/modal"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const relay = $relays.one(url)

View file

@ -2,7 +2,6 @@
import {onDestroy, onMount} from "svelte"
import {readable} from "svelte/store"
import type {Readable} from "svelte/store"
import {page} from "$app/stores"
import {sortBy, partition, spec, pushToMapKey, max} from "@welshman/lib"
import type {Maybe} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
@ -21,8 +20,11 @@
import {makeCommentFilter} from "@app/content"
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
import {pushModal} from "@app/modal"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const context = makeFeedContext({relays: [url]})
onDestroy(context.cleanup)

View file

@ -1,8 +1,6 @@
<script lang="ts">
import {onDestroy, onMount} from "svelte"
import {page} from "$app/stores"
import {sleep} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
import {POLL, POLL_RESPONSE, getCommentFiltersForRoot} from "@welshman/util"
import {deriveEventsAsc} from "@welshman/store"
import SortVertical from "@assets/icons/sort-vertical.svg?dataurl"
@ -19,8 +17,11 @@
import {deriveEvent, deriveEventsById} from "@app/repository"
import {makeFeedContext} from "@app/feeds"
import {decodeRelay} from "@app/relays"
import type {PageProps} from "./$types"
const {relay, id} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {relay, id} = params
const url = decodeRelay(relay)
const context = makeFeedContext({relays: [url]})
const event = deriveEvent(id, [url])

View file

@ -2,7 +2,6 @@
import {onDestroy, onMount} from "svelte"
import {readable} from "svelte/store"
import type {Readable} from "svelte/store"
import {page} from "$app/stores"
import {sortBy, partition, spec, max, pushToMapKey, groupBy} from "@welshman/lib"
import type {Maybe} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
@ -17,8 +16,11 @@
import {displayRoom} from "@app/rooms"
import {makeCommentFilter} from "@app/content"
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
import type {PageProps} from "./$types"
const url = decodeRelay($page.params.relay!)
const {params}: PageProps = $props()
const url = decodeRelay(params.relay)
const context = makeFeedContext({relays: [url]})
onDestroy(context.cleanup)

View file

@ -1,9 +1,7 @@
<script lang="ts">
import {onDestroy} from "svelte"
import * as nip19 from "nostr-tools/nip19"
import {page} from "$app/stores"
import {call, sleep, spec, tryCatch} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {deriveEventsAsc} from "@welshman/store"
import {getCommentFiltersForRoot, tagValue, tagSpec} from "@welshman/util"
@ -22,10 +20,13 @@
import {makeFeedContext} from "@app/feeds"
import {decodeRelay} from "@app/relays"
import {makeSpacePath, scrollToEvent} from "@app/routes"
import type {PageProps} from "./$types"
const REPLY_BATCH_SIZE = 20
const {relay, id} = $page.params as MakeNonOptional<typeof $page.params>
const {params}: PageProps = $props()
const {relay, id} = params
const url = decodeRelay(relay)
const event = deriveEvent(id, [url])
// Rows register themselves with `related`, so the whole page's reactions load in one batch