Detect nip29 support for create room button

This commit is contained in:
Jon Staab
2025-05-27 16:22:20 -07:00
parent 4ba6c72459
commit e57b5721f6
5 changed files with 67 additions and 40 deletions
+9 -4
View File
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import {displayRelayUrl} from "@welshman/util" import {displayRelayUrl} from "@welshman/util"
import {deriveRelay} from "@welshman/app"
import {fly} from "@lib/transition" import {fly} from "@lib/transition"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
@@ -20,6 +21,7 @@
memberships, memberships,
deriveUserRooms, deriveUserRooms,
deriveOtherRooms, deriveOtherRooms,
hasNip29,
} from "@app/state" } from "@app/state"
import {notifications} from "@app/notifications" import {notifications} from "@app/notifications"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
@@ -27,6 +29,7 @@
const {url} = $props() const {url} = $props()
const relay = deriveRelay(url)
const threadsPath = makeSpacePath(url, "threads") const threadsPath = makeSpacePath(url, "threads")
const calendarPath = makeSpacePath(url, "calendar") const calendarPath = makeSpacePath(url, "calendar")
const userRooms = deriveUserRooms(url) const userRooms = deriveUserRooms(url)
@@ -143,10 +146,12 @@
{#each $otherRooms as room, i (room)} {#each $otherRooms as room, i (room)}
<MenuSpaceRoomItem {replaceState} {url} {room} /> <MenuSpaceRoomItem {replaceState} {url} {room} />
{/each} {/each}
<SecondaryNavItem {replaceState} onclick={addRoom}> {#if hasNip29($relay)}
<Icon icon="add-circle" /> <SecondaryNavItem {replaceState} onclick={addRoom}>
Create room <Icon icon="add-circle" />
</SecondaryNavItem> Create room
</SecondaryNavItem>
{/if}
</div> </div>
</SecondaryNavSection> </SecondaryNavSection>
</div> </div>
+30 -25
View File
@@ -23,24 +23,22 @@
const back = () => history.back() const back = () => history.back()
const tryCreate = async () => { const tryCreate = async () => {
if (hasNip29($relay)) { const createMessage = await getThunkError(nip29.createRoom(url, room))
const createMessage = await getThunkError(nip29.createRoom(url, room))
if (createMessage && !createMessage.match(/^duplicate:|already a member/)) { if (createMessage && !createMessage.match(/^duplicate:|already a member/)) {
return pushToast({theme: "error", message: createMessage}) return pushToast({theme: "error", message: createMessage})
} }
const editMessage = await getThunkError(nip29.editMeta(url, room, {name})) const editMessage = await getThunkError(nip29.editMeta(url, room, {name}))
if (editMessage) { if (editMessage) {
return pushToast({theme: "error", message: editMessage}) return pushToast({theme: "error", message: editMessage})
} }
const joinMessage = await getThunkError(nip29.joinRoom(url, room)) const joinMessage = await getThunkError(nip29.joinRoom(url, room))
if (joinMessage && !joinMessage.includes("already")) { if (joinMessage && !joinMessage.includes("already")) {
return pushToast({theme: "error", message: joinMessage}) return pushToast({theme: "error", message: joinMessage})
}
} }
addRoomMembership(url, room, name) addRoomMembership(url, room, name)
@@ -72,23 +70,30 @@
</div> </div>
{/snippet} {/snippet}
</ModalHeader> </ModalHeader>
<Field> {#if hasNip29($relay)}
{#snippet label()} <Field>
<p>Room Name</p> {#snippet label()}
{/snippet} <p>Room Name</p>
{#snippet input()} {/snippet}
<label class="input input-bordered flex w-full items-center gap-2"> {#snippet input()}
<Icon icon="hashtag" /> <label class="input input-bordered flex w-full items-center gap-2">
<input bind:value={name} class="grow" type="text" /> <Icon icon="hashtag" />
</label> <input bind:value={name} class="grow" type="text" />
{/snippet} </label>
</Field> {/snippet}
</Field>
{:else}
<p class="bg-alt card2 row-2">
<Icon icon="danger" />
This relay does not support creating rooms.
</p>
{/if}
<ModalFooter> <ModalFooter>
<Button class="btn btn-link" onclick={back}> <Button class="btn btn-link" onclick={back}>
<Icon icon="alt-arrow-left" /> <Icon icon="alt-arrow-left" />
Go back Go back
</Button> </Button>
<Button type="submit" class="btn btn-primary" disabled={!name || loading}> <Button type="submit" class="btn btn-primary" disabled={!name || loading || !hasNip29($relay)}>
<Spinner {loading}>Create Room</Spinner> <Spinner {loading}>Create Room</Spinner>
<Icon icon="alt-arrow-right" /> <Icon icon="alt-arrow-right" />
</Button> </Button>
+10 -3
View File
@@ -3,6 +3,7 @@ import {get, derived} from "svelte/store"
import * as nip19 from "nostr-tools/nip19" import * as nip19 from "nostr-tools/nip19"
import { import {
remove, remove,
uniqBy,
sortBy, sortBy,
sort, sort,
uniq, uniq,
@@ -528,7 +529,7 @@ export const channels = derived(
} }
} }
return $channels return uniqBy(c => c.id, $channels)
}, },
) )
@@ -594,12 +595,18 @@ export const userMembership = withGetter(
) )
export const userRoomsByUrl = withGetter( export const userRoomsByUrl = withGetter(
derived(userMembership, $userMembership => { derived([userMembership, channelsById], ([$userMembership, $channelsById]) => {
const tags = getListTags($userMembership) const tags = getListTags($userMembership)
const $userRoomsByUrl = new Map<string, Set<string>>() const $userRoomsByUrl = new Map<string, Set<string>>()
for (const url of getRelayTagValues(tags)) {
$userRoomsByUrl.set(normalizeRelayUrl(url), new Set())
}
for (const [_, room, url] of getGroupTags(tags)) { for (const [_, room, url] of getGroupTags(tags)) {
addToMapKey($userRoomsByUrl, normalizeRelayUrl(url), room) if ($channelsById.has(makeChannelId(url, room))) {
addToMapKey($userRoomsByUrl, normalizeRelayUrl(url), room)
}
} }
return $userRoomsByUrl return $userRoomsByUrl
+11 -4
View File
@@ -1,6 +1,9 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import {addToMapKey, dec, gt} from "@welshman/lib" import {addToMapKey, dec, gt} from "@welshman/lib"
import {GROUPS} from "@welshman/util"
import {Router} from "@welshman/router"
import {load} from "@welshman/net"
import type {Relay} from "@welshman/app" import type {Relay} from "@welshman/app"
import {relays, createSearch, loadRelay, loadRelaySelections} from "@welshman/app" import {relays, createSearch, loadRelay, loadRelaySelections} from "@welshman/app"
import {createScroller} from "@lib/html" import {createScroller} from "@lib/html"
@@ -24,8 +27,12 @@
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
const discoverRelays = () => const discoverRelays = () =>
Promise.all( Promise.all([
getDefaultPubkeys().map(async pubkey => { load({
filters: [{kinds: [GROUPS]}],
relays: Router.get().Index().getUrls(),
}),
...getDefaultPubkeys().map(async pubkey => {
await loadRelaySelections(pubkey) await loadRelaySelections(pubkey)
const membership = await loadMembership(pubkey) const membership = await loadMembership(pubkey)
@@ -33,7 +40,7 @@
await Promise.all(urls.map(url => loadRelay(url))) await Promise.all(urls.map(url => loadRelay(url)))
}), }),
) ])
const wotGraph = $derived.by(() => { const wotGraph = $derived.by(() => {
const scores = new Map<string, Set<string>>() const scores = new Map<string, Set<string>>()
@@ -87,7 +94,7 @@
}) })
</script> </script>
<Page> <Page class="cw-full">
<div class="content column gap-4" bind:this={element}> <div class="content column gap-4" bind:this={element}>
<PageHeader> <PageHeader>
{#snippet title()} {#snippet title()}
+7 -4
View File
@@ -18,6 +18,7 @@
import RoomCreate from "@app/components/RoomCreate.svelte" import RoomCreate from "@app/components/RoomCreate.svelte"
import RelayDescription from "@app/components/RelayDescription.svelte" import RelayDescription from "@app/components/RelayDescription.svelte"
import { import {
hasNip29,
decodeRelay, decodeRelay,
channelIsLocked, channelIsLocked,
makeChannelId, makeChannelId,
@@ -183,10 +184,12 @@
</div> </div>
</Link> </Link>
{/each} {/each}
<Button onclick={addRoom} class="btn btn-neutral whitespace-nowrap"> {#if hasNip29($relay)}
<Icon icon="add-circle" /> <Button onclick={addRoom} class="btn btn-neutral whitespace-nowrap">
Create <Icon icon="add-circle" />
</Button> Create
</Button>
{/if}
</div> </div>
{#if pubkey} {#if pubkey}
<Divider>Recent posts from the relay admin</Divider> <Divider>Recent posts from the relay admin</Divider>