diff --git a/src/app/commands.ts b/src/app/commands.ts index 83445667..792a8bb4 100644 --- a/src/app/commands.ts +++ b/src/app/commands.ts @@ -8,6 +8,8 @@ import { FOLLOWS, REACTION, AUTH_JOIN, + GROUP_JOIN, + GROUP_LEAVE, isSignedEvent, createEvent, displayProfile, @@ -49,8 +51,10 @@ import { clearStorage, dropSession, } from "@welshman/app" +import type {Relay} from "@welshman/app" import { COMMENT, + tagRoom, userMembership, MEMBERSHIPS, INDEXER_RELAYS, @@ -216,6 +220,22 @@ export const broadcastUserData = async (relays: string[]) => { } } +// NIP 29 stuff + +export const nip29 = { + isSupported: (relay?: Relay) => relay?.profile?.supported_nips?.map(String)?.includes("29"), + joinRoom: (url: string, room: string) => { + const event = createEvent(GROUP_JOIN, {tags: [tagRoom(room, url)]}) + + return publishThunk({event, relays: [url]}) + }, + leaveRoom: (url: string, room: string) => { + const event = createEvent(GROUP_LEAVE, {tags: [tagRoom(room, url)]}) + + return publishThunk({event, relays: [url]}) + }, +} + // List updates export const addSpaceMembership = async (url: string) => { @@ -327,7 +347,11 @@ export const checkRelayAccess = async (url: string, claim = "") => { result[url].message?.replace(/^.*: /, "") || "join request rejected" - return `Failed to join relay (${message})` + // If it's a strict NIP 29 relay don't worry about requesting access + // TODO: remove this if relay29 ever gets less strict + if (message !== "missing group (`h`) tag") { + return `Failed to join relay (${message})` + } } } diff --git a/src/routes/spaces/[relay]/[room]/+page.svelte b/src/routes/spaces/[relay]/[room]/+page.svelte index 44fce464..6fcf082d 100644 --- a/src/routes/spaces/[relay]/[room]/+page.svelte +++ b/src/routes/spaces/[relay]/[room]/+page.svelte @@ -9,7 +9,8 @@ import type {TrustedEvent, EventContent} from "@welshman/util" import {throttled} from "@welshman/store" import {createEvent, DELETE} from "@welshman/util" - import {formatTimestampAsDate, publishThunk} from "@welshman/app" + import {PublishStatus} from "@welshman/net" + import {formatTimestampAsDate, publishThunk, deriveRelay} from "@welshman/app" import {slide} from "@lib/transition" import {createScroller, type Scroller} from "@lib/html" import Icon from "@lib/components/Icon.svelte" @@ -33,17 +34,41 @@ getMembershipRoomsByUrl, } from "@app/state" import {setChecked} from "@app/notifications" - import {addRoomMembership, removeRoomMembership, subscribePersistent} from "@app/commands" + import {nip29, addRoomMembership, removeRoomMembership, subscribePersistent} from "@app/commands" import {PROTECTED} from "@app/state" import {popKey} from "@app/implicit" + import {pushToast} from "@app/toast" const {room = GENERAL} = $page.params const content = popKey("content") || "" const url = decodeRelay($page.params.relay) + const relay = deriveRelay(url) const events = throttled(300, deriveChannelMessages(url, room)) const assertEvent = (e: any) => e as TrustedEvent + const joinRoom = async () => { + if (nip29.isSupported($relay)) { + const thunk = nip29.joinRoom(url, room) + const result = await thunk.result + const {status, message} = result[url]! + + if (status !== PublishStatus.Success) { + return pushToast({theme: "error", message}) + } + } + + addRoomMembership(url, room) + } + + const leaveRoom = () => { + if (nip29.isSupported($relay)) { + nip29.leaveRoom(url, room) + } + + removeRoomMembership(url, room) + } + const replyTo = (event: TrustedEvent) => { const relays = ctx.app.router.Event(event).getUrls() const nevent = nip19.neventEncode({...event, relays}) @@ -141,12 +166,12 @@
{#if room !== GENERAL} {#if getMembershipRoomsByUrl(url, $userMembership).includes(room)} - {:else} -