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