Add relay members list and room join/leave events

This commit is contained in:
Matthew Remmel
2025-10-15 12:04:17 -04:00
committed by hodlbod
parent 43cf91e877
commit a730384baf
22 changed files with 499 additions and 323 deletions
+12 -9
View File
@@ -6,7 +6,7 @@
import {Router} from "@welshman/router"
import {load} from "@welshman/net"
import type {Relay} from "@welshman/app"
import {relays, createSearch, loadRelay, loadRelaySelections} from "@welshman/app"
import {relays, createSearch, loadRelay} from "@welshman/app"
import {createScroller} from "@lib/html"
import {fly} from "@lib/transition"
import QrCode from "@assets/icons/qr-code.svg?dataurl"
@@ -23,7 +23,12 @@
import SpaceInviteAccept from "@app/components/SpaceInviteAccept.svelte"
import RelaySummary from "@app/components/RelaySummary.svelte"
import SpaceCheck from "@app/components/SpaceCheck.svelte"
import {getMembershipUrls, loadMembership, defaultPubkeys, membersByUrl} from "@app/core/state"
import {
bootstrapPubkeys,
loadGroupSelections,
getSpaceUrlsFromGroupSelections,
groupSelectionsPubkeysByUrl,
} from "@app/core/state"
import {pushModal} from "@app/util/modal"
const openMenu = () => pushModal(SpaceAdd)
@@ -45,11 +50,9 @@
filters: [{kinds: [ROOMS]}],
relays: Router.get().Index().getUrls(),
}),
...$defaultPubkeys.map(async pubkey => {
await loadRelaySelections(pubkey)
const membership = await loadMembership(pubkey)
const urls = getMembershipUrls(membership)
...$bootstrapPubkeys.map(async pubkey => {
const list = await loadGroupSelections(pubkey)
const urls = getSpaceUrlsFromGroupSelections(list)
await Promise.all(urls.map(url => loadRelay(url)))
}),
@@ -57,13 +60,13 @@
const relaySearch = $derived(
createSearch(
$relays.filter(r => $membersByUrl.has(r.url) && r.url !== termUrl),
$relays.filter(r => $groupSelectionsPubkeysByUrl.has(r.url) && r.url !== termUrl),
{
getValue: (relay: Relay) => relay.url,
sortFn: ({score, item}) => {
if (score && score > 0.1) return -score!
const wotScore = $membersByUrl.get(item.url)?.size || 0
const wotScore = $groupSelectionsPubkeysByUrl.get(item.url)!.size
return score ? dec(score) * wotScore : -wotScore
},
+2 -2
View File
@@ -7,13 +7,13 @@
import Page from "@lib/components/Page.svelte"
import ContentSearch from "@lib/components/ContentSearch.svelte"
import PeopleItem from "@app/components/PeopleItem.svelte"
import {defaultPubkeys} from "@app/core/state"
import {bootstrapPubkeys} from "@app/core/state"
let term = $state("")
let limit = $state(10)
let element: Element | undefined = $state()
const pubkeys = $derived(term ? $profileSearch.searchValues(term) : $defaultPubkeys)
const pubkeys = $derived(term ? $profileSearch.searchValues(term) : $bootstrapPubkeys)
onMount(() => {
const scroller = createScroller({
+32 -16
View File
@@ -7,7 +7,13 @@
import type {MakeNonOptional} from "@welshman/lib"
import {now, formatTimestampAsDate, ago, MINUTE} from "@welshman/lib"
import type {TrustedEvent, EventContent} from "@welshman/util"
import {makeEvent, makeRoomMeta, MESSAGE} from "@welshman/util"
import {
makeEvent,
makeRoomMeta,
MESSAGE,
ROOM_ADD_MEMBER,
ROOM_REMOVE_MEMBER,
} from "@welshman/util"
import {pubkey, publishThunk, waitForThunkError, joinRoom, leaveRoom} from "@welshman/app"
import {slide, fade, fly} from "@lib/transition"
import Hashtag from "@assets/icons/hashtag.svg?dataurl"
@@ -26,13 +32,16 @@
import MenuSpaceButton from "@app/components/MenuSpaceButton.svelte"
import ChannelName from "@app/components/ChannelName.svelte"
import ChannelItem from "@app/components/ChannelItem.svelte"
import ChannelItemAddMember from "@src/app/components/ChannelItemAddMember.svelte"
import ChannelItemRemoveMember from "@src/app/components/ChannelItemRemoveMember.svelte"
import ChannelCompose from "@app/components/ChannelCompose.svelte"
import ChannelComposeEdit from "@src/app/components/ChannelComposeEdit.svelte"
import ChannelComposeParent from "@app/components/ChannelComposeParent.svelte"
import {
userRoomsByUrl,
deriveUserRooms,
userSettingsValues,
decodeRelay,
deriveUserMembershipStatus,
deriveUserRoomMembershipStatus,
deriveChannel,
MembershipStatus,
PROTECTED,
@@ -49,16 +58,16 @@
import {makeFeed} from "@app/core/requests"
import {popKey} from "@lib/implicit"
import {pushToast} from "@app/util/toast"
import ChannelComposeEdit from "@src/app/components/ChannelComposeEdit.svelte"
const {room, relay} = $page.params as MakeNonOptional<typeof $page.params>
const mounted = now()
const lastChecked = $checked[$page.url.pathname]
const url = decodeRelay(relay)
const channel = deriveChannel(url, room)
const isFavorite = $derived($userRoomsByUrl.get(url)?.has(room))
const shouldProtect = canEnforceNip70(url)
const membershipStatus = deriveUserMembershipStatus(url, room)
const userRooms = deriveUserRooms(url)
const isFavorite = $derived($userRooms.includes(room))
const membershipStatus = deriveUserRoomMembershipStatus(url, room)
const addFavorite = () => addRoomMembership(url, room)
@@ -256,7 +265,7 @@
const feed = makeFeed({
url,
element: element!,
filters: [{kinds: MESSAGE_KINDS, "#h": [room]}],
filters: [{kinds: [...MESSAGE_KINDS, ROOM_ADD_MEMBER, ROOM_REMOVE_MEMBER], "#h": [room]}],
onExhausted: () => {
loadingEvents = false
},
@@ -398,15 +407,22 @@
{:else if type === "date"}
<Divider>{value}</Divider>
{:else}
<div in:slide class:-mt-1={!showPubkey}>
<ChannelItem
{url}
{replyTo}
event={$state.snapshot(value as TrustedEvent)}
{showPubkey}
canEdit={canEditEvent}
onEdit={onEditEvent} />
</div>
{@const event = $state.snapshot(value as TrustedEvent)}
{#if event.kind === ROOM_ADD_MEMBER}
<ChannelItemAddMember {url} {event} />
{:else if event.kind === ROOM_REMOVE_MEMBER}
<ChannelItemRemoveMember {url} {event} />
{:else}
<div in:slide class:-mt-1={!showPubkey}>
<ChannelItem
{url}
{event}
{replyTo}
{showPubkey}
canEdit={canEditEvent}
onEdit={onEditEvent} />
</div>
{/if}
{/if}
{/each}
<p class="flex h-10 items-center justify-center py-20">
+22 -14
View File
@@ -5,9 +5,9 @@
import {readable} from "svelte/store"
import {now, formatTimestampAsDate, MINUTE, ago} from "@welshman/lib"
import type {TrustedEvent, EventContent} from "@welshman/util"
import {makeEvent, MESSAGE} from "@welshman/util"
import {makeEvent, MESSAGE, RELAY_ADD_MEMBER, RELAY_REMOVE_MEMBER} from "@welshman/util"
import {pubkey, publishThunk} from "@welshman/app"
import {slide, fade, fly} from "@lib/transition"
import {fade, fly} from "@lib/transition"
import ChatRound from "@assets/icons/chat-round.svg?dataurl"
import AltArrowDown from "@assets/icons/alt-arrow-down.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
@@ -19,15 +19,17 @@
import ThunkToast from "@app/components/ThunkToast.svelte"
import MenuSpaceButton from "@app/components/MenuSpaceButton.svelte"
import ChannelItem from "@app/components/ChannelItem.svelte"
import ChannelItemAddMember from "@src/app/components/ChannelItemAddMember.svelte"
import ChannelItemRemoveMember from "@src/app/components/ChannelItemRemoveMember.svelte"
import ChannelCompose from "@app/components/ChannelCompose.svelte"
import ChannelComposeEdit from "@src/app/components/ChannelComposeEdit.svelte"
import ChannelComposeParent from "@app/components/ChannelComposeParent.svelte"
import {userSettingsValues, decodeRelay, MESSAGE_FILTER, PROTECTED} from "@app/core/state"
import {userSettingsValues, decodeRelay, PROTECTED, MESSAGE_KINDS} from "@app/core/state"
import {prependParent, canEnforceNip70, publishDelete} from "@app/core/commands"
import {setChecked, checked} from "@app/util/notifications"
import {pushToast} from "@app/util/toast"
import {makeFeed} from "@app/core/requests"
import {popKey} from "@lib/implicit"
import ChannelComposeEdit from "@src/app/components/ChannelComposeEdit.svelte"
const mounted = now()
const lastChecked = $checked[$page.url.pathname]
@@ -218,7 +220,7 @@
const feed = makeFeed({
url,
element: element!,
filters: [MESSAGE_FILTER],
filters: [{kinds: [...MESSAGE_KINDS, RELAY_ADD_MEMBER, RELAY_REMOVE_MEMBER]}],
onExhausted: () => {
loadingEvents = false
},
@@ -271,15 +273,21 @@
<Divider>{value}</Divider>
{:else}
{@const event = $state.snapshot(value as TrustedEvent)}
<div in:slide class:-mt-1={!showPubkey}>
<ChannelItem
{url}
{event}
{replyTo}
{showPubkey}
canEdit={canEditEvent}
onEdit={onEditEvent} />
</div>
{#if event.kind === RELAY_ADD_MEMBER}
<ChannelItemAddMember {url} {event} />
{:else if event.kind === RELAY_REMOVE_MEMBER}
<ChannelItemRemoveMember {url} {event} />
{:else}
<div class:-mt-1={!showPubkey}>
<ChannelItem
{url}
{event}
{replyTo}
{showPubkey}
canEdit={canEditEvent}
onEdit={onEditEvent} />
</div>
{/if}
{/if}
{/each}
<p class="flex h-10 items-center justify-center py-20">