flotilla/src/routes/spaces/[relay]/+layout.svelte

119 lines
3.8 KiB
Svelte
Raw Normal View History

<script module lang="ts">
const joinPrompted = new Set<string>()
2026-07-15 16:30:17 +00:00
const redirectPrompted = new Set<string>()
</script>
2024-08-16 19:42:00 +00:00
<script lang="ts">
2024-08-16 22:17:30 +00:00
import {page} from "$app/stores"
import {navigating} from "$app/state"
2026-07-15 16:30:17 +00:00
import type {Maybe} from "@welshman/lib"
import {once} from "@welshman/lib"
2026-07-28 16:16:26 +00:00
import {normalizeRelayUrl} from "@welshman/util"
import {isMobile} from "@lib/html"
2024-08-16 19:42:00 +00:00
import Page from "@lib/components/Page.svelte"
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
2025-11-13 18:36:28 +00:00
import SpaceMenu from "@app/components/SpaceMenu.svelte"
import SocketStatusToast from "@app/components/SocketStatusToast.svelte"
2024-11-19 04:31:17 +00:00
import SpaceAuthError from "@app/components/SpaceAuthError.svelte"
import SpaceTrustRelay from "@app/components/SpaceTrustRelay.svelte"
import SpaceJoin from "@app/components/SpaceJoin.svelte"
2026-07-15 16:30:17 +00:00
import SpaceRedirect from "@app/components/SpaceRedirect.svelte"
import {deriveRelayAuthError} from "@app/access"
2026-07-28 16:16:26 +00:00
import {relays, roomLists, user} from "@app/core"
import {userSpaceUrls} from "@app/rooms"
import {getModal, pushModal} from "@app/modal"
import {relaysPendingTrust} from "@app/policies"
2026-07-28 16:16:26 +00:00
import {decodeRelay} from "@app/relays"
import {makeSpacePath} from "@app/routes"
import type {LayoutProps} from "./$types"
const {children, params}: LayoutProps = $props()
2025-02-04 00:37:14 +00:00
const url = decodeRelay(params.relay)
2024-11-14 20:16:44 +00:00
const authError = deriveRelayAuthError(url)
2024-11-19 04:31:17 +00:00
const showAuthError = once(() =>
pushModal(SpaceAuthError, {url, error: $authError}, {noEscape: true}),
)
const showPendingTrust = once(() => pushModal(SpaceTrustRelay, {url}, {noEscape: true}))
2024-11-07 23:19:19 +00:00
2026-07-28 16:16:26 +00:00
// Detect a moved custom domain — the relay's nip-11 document answered with a redirect
2026-07-15 16:30:17 +00:00
const checkRedirect = once(() => {
2026-07-28 16:16:26 +00:00
$relays.load(url).then($relay => {
const next = $relay?.redirect_to
if (next) {
redirectUrl = normalizeRelayUrl(next.replace(/^http/, "ws"))
}
2026-07-15 16:30:17 +00:00
})
})
2026-07-28 16:16:26 +00:00
const loadSpaces = async () => {
const currentPubkey = user.get().pubkey
if (currentPubkey) {
// forceLoad skips the hour-stale cache and, unlike load, rethrows fetch errors.
try {
await $roomLists.forceLoad(currentPubkey, [url])
} catch (error) {
console.warn(`Failed to load room list for ${currentPubkey}`, error)
}
2026-07-28 16:16:26 +00:00
}
spacesLoaded = true
}
// Track this manually since we want to avoid race conditions in which we show this prompt before we load
2026-07-03 22:57:45 +00:00
let spacesLoaded = $state(false)
2026-07-15 16:30:17 +00:00
let redirectUrl = $state<Maybe<string>>()
$effect(checkRedirect)
// A modal owns a history entry, so the join prompt waits for the page to settle.
2026-07-01 17:23:23 +00:00
$effect(() => {
if (getModal() || navigating.to) {
2026-09-21 16:48:35 +00:00
return
}
2026-07-28 16:16:26 +00:00
if (redirectUrl && redirectUrl !== url && !redirectPrompted.has(url)) {
2026-07-15 16:30:17 +00:00
redirectPrompted.add(url)
pushModal(SpaceRedirect, {url, newUrl: redirectUrl})
} else if (!$userSpaceUrls.includes(url) && !joinPrompted.has(url)) {
if (spacesLoaded) {
joinPrompted.add(url)
pushModal(SpaceJoin, {url})
} else {
2026-07-28 16:16:26 +00:00
loadSpaces()
}
} else if ($authError) {
showAuthError()
} else if ($relaysPendingTrust.includes(url)) {
showPendingTrust()
}
})
2024-08-16 19:42:00 +00:00
</script>
<!-- Desktop shows the same status pinned to the space menu; on mobile that's behind the drawer -->
{#if isMobile}
<SocketStatusToast {url} />
{/if}
{#if $page.url.pathname === makeSpacePath(url)}
{@render children?.()}
{:else}
<SecondaryNav>
<SpaceMenu {url} />
</SecondaryNav>
<Page>
2026-09-16 16:19:43 +00:00
<!-- SvelteKit builds a new page when the route changes and keeps the one it has when only the
params change, so this rebuilds it for the second case. The url the page store reports
arrives a tick after the page is built, so keying on that throws the new page away. -->
{#key JSON.stringify(params)}
{@render children?.()}
{/key}
</Page>
{/if}