Detect nip29 properly before choosing smart path, more robust auth error checking

This commit is contained in:
Jon Staab
2025-11-04 16:14:32 -08:00
parent 6429f82829
commit 501ce8067d
12 changed files with 56 additions and 45 deletions
+8 -2
View File
@@ -4,9 +4,15 @@
import Dialog from "@lib/components/Dialog.svelte"
import {modal, clearModals} from "@app/util/modal"
const closeModals = () => {
if ($modal && !$modal.options.noEscape) {
clearModals()
}
}
const onKeyDown = (e: any) => {
if (e.code === "Escape" && e.target === document.body) {
clearModals()
closeModals()
}
}
@@ -27,7 +33,7 @@
instance = mount(wrapper as any, {
target: element,
props: {
onClose: clearModals,
onClose: closeModals,
children: createRawSnippet(() => ({
render: () => "<div></div>",
setup: (target: Element) => {
@@ -1,24 +1,19 @@
<script lang="ts">
import {goto} from "$app/navigation"
import {displayRelayUrl} from "@welshman/util"
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
import SpaceAvatar from "@app/components/SpaceAvatar.svelte"
import {encodeRelay} from "@app/core/state"
import {makeSpacePath} from "@app/util/routes"
import {lastPageBySpaceUrl} from "@app/util/history"
import {makeSpacePath, goToSpace} from "@app/util/routes"
import {notifications} from "@app/util/notifications"
const {url} = $props()
const path = makeSpacePath(url)
const onClick = () => goto(lastPageBySpaceUrl.get(encodeRelay(url)) || path)
const onClick = () => goToSpace(url)
</script>
<PrimaryNavItem
onclick={onClick}
title={displayRelayUrl(url)}
class="tooltip-right"
notification={$notifications.has(path)}>
notification={$notifications.has(makeSpacePath(url))}>
<SpaceAvatar {url} />
</PrimaryNavItem>
+3 -2
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import {goto} from "$app/navigation"
import {displayRelayUrl} from "@welshman/util"
import {parse, renderAsHtml} from "@welshman/content"
import Button from "@lib/components/Button.svelte"
@@ -14,7 +15,7 @@
const {url, error} = $props()
const back = () => history.back()
const back = () => goto("/home")
const requestAccess = () => pushModal(SpaceAccessRequest, {url})
</script>
@@ -37,7 +38,7 @@
<ModalFooter>
<Button class="btn btn-link" onclick={back}>
<Icon icon={AltArrowLeft} />
Go back
Go Home
</Button>
<Button type="submit" class="btn btn-primary">
Request Access
+9 -4
View File
@@ -1,8 +1,8 @@
<script lang="ts">
import {onMount} from "svelte"
import {sleep, nthEq} from "@welshman/lib"
import {sleep} from "@welshman/lib"
import {request} from "@welshman/net"
import {displayRelayUrl, RELAY_INVITE} from "@welshman/util"
import {displayRelayUrl, getTagValue, RELAY_INVITE} from "@welshman/util"
import LinkRound from "@assets/icons/link-round.svg?dataurl"
import Copy from "@assets/icons/copy.svg?dataurl"
import Spinner from "@lib/components/Spinner.svelte"
@@ -13,10 +13,12 @@
import ModalFooter from "@lib/components/ModalFooter.svelte"
import QRCode from "@app/components/QRCode.svelte"
import {clip} from "@app/util/toast"
import {PLATFORM_URL} from "@app/core/state"
import {PLATFORM_URL, deriveRelayAuthError} from "@app/core/state"
const {url} = $props()
const authError = deriveRelayAuthError(url)
const back = () => history.back()
const copyInvite = () => clip(invite)
@@ -38,12 +40,13 @@
request({
relays: [url],
autoClose: true,
signal: AbortSignal.timeout(3000),
filters: [{kinds: [RELAY_INVITE]}],
}),
sleep(2000),
])
claim = event?.tags.find(nthEq(0, "claim"))?.[1] || ""
claim = getTagValue("claim", event?.tags || []) || ""
loading = false
})
</script>
@@ -65,6 +68,8 @@
<p class="center">
<Spinner {loading}>Requesting an invite link...</Spinner>
</p>
{:else if $authError}
<p class="center">Oops! It looks like you're not a member of this relay.</p>
{:else}
<div class="flex flex-col items-center gap-6">
<QRCode code={invite} />
+1 -1
View File
@@ -28,7 +28,7 @@
try {
await removeSpaceMembership(url)
await removeTrustedRelay(url)
goto("/")
goto("/home")
} finally {
loading = false
}
+3 -3
View File
@@ -982,8 +982,8 @@ export const deriveRelayAuthError = (url: string, claim = "") => {
})
return derived(
[relaysMostlyRestricted, deriveSocket(url)],
([$relaysMostlyRestricted, $socket]) => {
[thunk, relaysMostlyRestricted, deriveSocket(url)],
([$thunk, $relaysMostlyRestricted, $socket]) => {
if ($socket.auth.status === AuthStatus.Forbidden && $socket.auth.details) {
return stripPrefix($socket.auth.details)
}
@@ -992,7 +992,7 @@ export const deriveRelayAuthError = (url: string, claim = "") => {
return stripPrefix($relaysMostlyRestricted[url])
}
const error = getThunkError(thunk)
const error = getThunkError($thunk)
if (error) {
const isEmptyInvite = !claim && error.includes("invite code")
+1
View File
@@ -6,6 +6,7 @@ import {page} from "$app/stores"
export type ModalOptions = {
drawer?: boolean
noEscape?: boolean
fullscreen?: boolean
replaceState?: boolean
path?: string
+1 -2
View File
@@ -62,11 +62,10 @@ export const mostlyRestrictedPolicy = (socket: Socket) => {
const pending = new Set<string>()
const updateStatus = () => {
const updateStatus = () =>
relaysMostlyRestricted.update(
restricted > total / 2 ? assoc(socket.url, error) : dissoc(socket.url),
)
}
const unsubscribers = [
on(socket, SocketEvent.Receive, (message: RelayMessage) => {
+14 -9
View File
@@ -4,7 +4,7 @@ import * as nip19 from "nostr-tools/nip19"
import {goto} from "$app/navigation"
import {nthEq, sleep} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {tracker, relaysByUrl} from "@welshman/app"
import {tracker, loadRelay} from "@welshman/app"
import {scrollToEvent} from "@lib/html"
import {identity} from "@welshman/lib"
import {
@@ -26,6 +26,7 @@ import {
hasNip29,
ROOM,
} from "@app/core/state"
import {lastPageBySpaceUrl} from "@app/util/history"
export const makeSpacePath = (url: string, ...extra: (string | undefined)[]) => {
let path = `/spaces/${encodeRelay(url)}`
@@ -37,19 +38,23 @@ export const makeSpacePath = (url: string, ...extra: (string | undefined)[]) =>
.filter(identity)
.map(s => encodeURIComponent(s as string))
.join("/")
} else {
const relay = relaysByUrl.get().get(url)
if (hasNip29(relay)) {
path += "/recent"
} else {
path += "/chat"
}
}
return path
}
export const goToSpace = async (url: string) => {
const prevPath = lastPageBySpaceUrl.get(encodeRelay(url))
if (prevPath) {
goto(prevPath)
} else if (hasNip29(await loadRelay(url))) {
goto(makeSpacePath(url, "recent"))
} else {
goto(makeSpacePath("chat"))
}
}
export const makeChatPath = (pubkeys: string[]) => `/chat/${makeChatId(pubkeys)}`
export const makeRoomPath = (url: string, h: string) => `/spaces/${encodeRelay(url)}/${h}`
+3 -3
View File
@@ -1,12 +1,12 @@
<script lang="ts">
import {onMount} from "svelte"
import {goto} from "$app/navigation"
import {makeSpacePath} from "@app/util/routes"
import {goToSpace} from "@app/util/routes"
import {PLATFORM_RELAYS} from "@app/core/state"
onMount(() => {
onMount(async () => {
if (PLATFORM_RELAYS.length > 0) {
goto(makeSpacePath(PLATFORM_RELAYS[0]))
goToSpace(PLATFORM_RELAYS[0])
} else {
goto("/home")
}
+3 -3
View File
@@ -12,16 +12,16 @@
import SpaceAdd from "@app/components/SpaceAdd.svelte"
import ChatEnable from "@app/components/ChatEnable.svelte"
import {pushModal} from "@app/util/modal"
import {makeSpacePath} from "@app/util/routes"
import {goToSpace} from "@app/util/routes"
import {PLATFORM_NAME, PLATFORM_RELAYS} from "@app/core/state"
const addSpace = () => pushModal(SpaceAdd)
const openChat = () => ($shouldUnwrap ? goto("/chat") : pushModal(ChatEnable, {next: "/chat"}))
onMount(() => {
onMount(async () => {
if (PLATFORM_RELAYS.length > 0) {
goto(makeSpacePath(PLATFORM_RELAYS[0]))
goToSpace(PLATFORM_RELAYS[0])
}
})
</script>
+7 -8
View File
@@ -3,7 +3,6 @@
import {page} from "$app/stores"
import {once} from "@welshman/lib"
import Page from "@lib/components/Page.svelte"
import Dialog from "@lib/components/Dialog.svelte"
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
import MenuSpace from "@app/components/MenuSpace.svelte"
import SpaceAuthError from "@app/components/SpaceAuthError.svelte"
@@ -23,7 +22,11 @@
const authError = deriveRelayAuthError(url)
const showAuthError = once(() => pushModal(SpaceAuthError, {url, error: $authError}))
const showAuthError = once(() =>
pushModal(SpaceAuthError, {url, error: $authError}, {noEscape: true}),
)
const showPendingTrust = once(() => pushModal(SpaceTrustRelay, {url}, {noEscape: true}))
// We have to watch this one, since on mobile the badge will be visible when active
$effect(() => {
@@ -36,6 +39,8 @@
$effect(() => {
if ($authError) {
showAuthError()
} else if ($relaysPendingTrust.includes(url)) {
showPendingTrust()
}
})
</script>
@@ -48,9 +53,3 @@
{@render children?.()}
{/key}
</Page>
{#if $relaysPendingTrust.includes(url)}
<Dialog>
<SpaceTrustRelay {url} />
</Dialog>
{/if}