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
+2 -2
View File
@@ -13,7 +13,7 @@
import Spinner from "@lib/components/Spinner.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import {alerts, getMembershipUrls, userMembership} from "@app/core/state"
import {alerts, userSpaceUrls} from "@app/core/state"
import {requestRelayClaim} from "@app/core/requests"
import {createAlert} from "@app/core/commands"
import {canSendPushNotifications} from "@app/util/push"
@@ -174,7 +174,7 @@
{#snippet input()}
<select bind:value={url} class="select select-bordered">
<option value="" disabled selected>Choose a space URL</option>
{#each getMembershipUrls($userMembership) as url (url)}
{#each $userSpaceUrls as url (url)}
<option value={url}>{displayRelayUrl(url)}</option>
{/each}
</select>
@@ -0,0 +1,18 @@
<script lang="ts">
import type {TrustedEvent} from "@welshman/util"
import {getPubkeyTagValues} from "@welshman/util"
import ProfileLink from "@app/components/ProfileLink.svelte"
type Props = {
url: string
event: TrustedEvent
}
const {url, event}: Props = $props()
</script>
{#each getPubkeyTagValues(event.tags) as pubkey}
<div class="py-1 text-center text-xs opacity-75">
<ProfileLink unstyled class="text-primary" {url} {pubkey} /> joined the room
</div>
{/each}
@@ -0,0 +1,18 @@
<script lang="ts">
import type {TrustedEvent} from "@welshman/util"
import {getPubkeyTagValues} from "@welshman/util"
import ProfileLink from "@app/components/ProfileLink.svelte"
type Props = {
url: string
event: TrustedEvent
}
const {url, event}: Props = $props()
</script>
{#each getPubkeyTagValues(event.tags) as pubkey}
<div class="py-1 text-center text-xs opacity-75">
<ProfileLink unstyled class="text-primary" {url} {pubkey} /> left the room
</div>
{/each}
+5 -10
View File
@@ -39,9 +39,7 @@
import {
ENABLE_ZAPS,
MESSAGE_FILTER,
userRoomsByUrl,
hasMembershipUrl,
memberships,
deriveSpaceMembers,
deriveEventsForUrl,
deriveUserRooms,
deriveOtherRooms,
@@ -62,6 +60,7 @@
const calendarPath = makeSpacePath(url, "calendar")
const userRooms = deriveUserRooms(url)
const otherRooms = deriveOtherRooms(url)
const members = deriveSpaceMembers(url)
const owner = $derived($relay?.profile?.pubkey)
const hasAlerts = $derived($alerts.some(a => getTagValue("feed", a.tags)?.includes(url)))
@@ -83,7 +82,7 @@
const showMembers = () =>
pushModal(
ProfileList,
{url, pubkeys: members, title: `Members of`, subtitle: displayRelayUrl(url)},
{url, pubkeys: $members, title: `Members of`, subtitle: displayRelayUrl(url)},
{replaceState},
)
@@ -108,10 +107,6 @@
let replaceState = $state(false)
let element: Element | undefined = $state()
const members = $derived(
$memberships.filter(l => hasMembershipUrl(l, url)).map(l => l.event.pubkey),
)
onMount(() => {
replaceState = Boolean(element?.closest(".drawer"))
})
@@ -151,7 +146,7 @@
<li>
<Button onclick={showMembers}>
<Icon icon={UserRounded} />
View Members ({members.length})
View Members ({$members.length})
</Button>
</li>
{#if owner}
@@ -163,7 +158,7 @@
</li>
{/if}
<li>
{#if $userRoomsByUrl.has(url)}
{#if $userRooms.includes(url)}
<Button onclick={leaveSpace} class="text-error">
<Icon icon={Exit} />
Leave Space
+3 -3
View File
@@ -5,15 +5,15 @@
import Divider from "@lib/components/Divider.svelte"
import CardButton from "@lib/components/CardButton.svelte"
import MenuSpacesItem from "@app/components/MenuSpacesItem.svelte"
import {userRoomsByUrl, PLATFORM_RELAYS} from "@app/core/state"
import {userSpaceUrls, PLATFORM_RELAYS} from "@app/core/state"
</script>
<div class="column menu gap-2">
{#each PLATFORM_RELAYS as url (url)}
<MenuSpacesItem {url} />
{:else}
{#if $userRoomsByUrl.size > 0}
{#each $userRoomsByUrl.keys() as url (url)}
{#if $userSpaceUrls.length > 0}
{#each $userSpaceUrls as url (url)}
<MenuSpacesItem {url} />
{/each}
<Divider />
+5 -5
View File
@@ -13,7 +13,7 @@
import MenuOtherSpaces from "@app/components/MenuOtherSpaces.svelte"
import MenuSettings from "@app/components/MenuSettings.svelte"
import PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
import {userRoomsByUrl, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
import {userSpaceUrls, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
import {pushModal} from "@app/util/modal"
import {makeSpacePath} from "@app/util/routes"
import {notifications} from "@app/util/notifications"
@@ -31,7 +31,8 @@
const {children}: Props = $props()
const showSpacesMenu = () => (spaceUrls.length > 0 ? pushModal(MenuSpaces) : pushModal(SpaceAdd))
const showSpacesMenu = () =>
$userSpaceUrls.length > 0 ? pushModal(MenuSpaces) : pushModal(SpaceAdd)
const showOtherSpacesMenu = () => pushModal(MenuOtherSpaces, {urls: secondarySpaceUrls})
@@ -50,9 +51,8 @@
const itemHeight = 56
const navPadding = 6 * itemHeight
const itemLimit = $derived((windowHeight - navPadding) / itemHeight)
const spaceUrls = $derived(Array.from($userRoomsByUrl.keys()))
const [primarySpaceUrls, secondarySpaceUrls] = $derived(splitAt(itemLimit, spaceUrls))
const anySpaceNotifications = $derived(spaceUrls.some(hasNotification))
const [primarySpaceUrls, secondarySpaceUrls] = $derived(splitAt(itemLimit, $userSpaceUrls))
const anySpaceNotifications = $derived($userSpaceUrls.some(hasNotification))
const otherSpaceNotifications = $derived(secondarySpaceUrls.some(hasNotification))
</script>
+11 -7
View File
@@ -5,11 +5,15 @@
import type {Filter} from "@welshman/util"
import {deriveEvents} from "@welshman/store"
import {formatTimestampRelative} from "@welshman/lib"
import {NOTE, ROOMS, COMMENT, getRelayTags, getListTags} from "@welshman/util"
import {NOTE, ROOMS, COMMENT} from "@welshman/util"
import {repository, loadRelaySelections} from "@welshman/app"
import Button from "@lib/components/Button.svelte"
import ProfileSpaces from "@app/components/ProfileSpaces.svelte"
import {membershipsByPubkey, MESSAGE_KINDS} from "@app/core/state"
import {
deriveGroupSelections,
getSpaceUrlsFromGroupSelections,
MESSAGE_KINDS,
} from "@app/core/state"
import {goToEvent} from "@app/util/routes"
import {pushModal} from "@app/util/modal"
@@ -21,8 +25,8 @@
const {pubkey, url}: Props = $props()
const filters: Filter[] = [{authors: [pubkey], limit: 1}]
const events = deriveEvents(repository, {filters})
const membership = $derived($membershipsByPubkey.get(pubkey))
const relays = $derived(getRelayTags(getListTags(membership)))
const selections = deriveGroupSelections(pubkey)
const spaceUrls = $derived(getSpaceUrlsFromGroupSelections($selections))
const viewEvent = () => goToEvent($events[0]!)
@@ -49,10 +53,10 @@
Last active {formatTimestampRelative($events[0].created_at)}
</Button>
{/if}
{#if relays.length > 0}
{#if spaceUrls.length > 0}
<Button onclick={openSpaces} class="badge badge-neutral">
{relays.length}
{relays.length === 1 ? "space" : "spaces"}
{spaceUrls.length}
{spaceUrls.length === 1 ? "space" : "spaces"}
</Button>
{/if}
</div>
+2 -12
View File
@@ -19,13 +19,7 @@
import ModalFooter from "@lib/components/ModalFooter.svelte"
import {pushToast} from "@app/util/toast"
import {logout} from "@app/core/commands"
import {
INDEXER_RELAYS,
PLATFORM_NAME,
userMembership,
getMembershipUrls,
userWriteRelays,
} from "@app/core/state"
import {INDEXER_RELAYS, PLATFORM_NAME, userSpaceUrls, userWriteRelays} from "@app/core/state"
let progress: number | undefined = $state(undefined)
let confirmText = $state("")
@@ -46,11 +40,7 @@
const profileEvent = makeEvent(PROFILE, createProfile({name: "[deleted]"}))
const vanishEvent = makeEvent(62, {tags: [["relay", "ALL_RELAYS"]]})
const denominator = chunks.length + 2
const relays = uniq([
...INDEXER_RELAYS,
...$userWriteRelays,
...getMembershipUrls($userMembership),
])
const relays = uniq([...INDEXER_RELAYS, ...$userWriteRelays, ...$userSpaceUrls])
let step = 0
+3 -2
View File
@@ -8,7 +8,7 @@
import SpaceAvatar from "@app/components/SpaceAvatar.svelte"
import RelayName from "@app/components/RelayName.svelte"
import {makeSpacePath} from "@app/util/routes"
import {getMembershipUrls, membershipsByPubkey} from "@app/core/state"
import {deriveGroupSelections, getSpaceUrlsFromGroupSelections} from "@app/core/state"
type Props = {
pubkey: string
@@ -16,7 +16,8 @@
const {pubkey}: Props = $props()
const spaceUrls = $derived(getMembershipUrls($membershipsByPubkey.get(pubkey)))
const selections = deriveGroupSelections(pubkey)
const spaceUrls = $derived(getSpaceUrlsFromGroupSelections($selections))
const back = () => history.back()
</script>
+6 -5
View File
@@ -1,5 +1,4 @@
<script lang="ts">
import {gt} from "@welshman/lib"
import {deriveRelay} from "@welshman/app"
import Ghost from "@assets/icons/ghost-smile.svg?dataurl"
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
@@ -7,7 +6,7 @@
import RelayName from "@app/components/RelayName.svelte"
import RelayDescription from "@app/components/RelayDescription.svelte"
import ProfileCircles from "@app/components/ProfileCircles.svelte"
import {membersByUrl, userRoomsByUrl} from "@app/core/state"
import {deriveSpaceMembers, deriveUserRooms} from "@app/core/state"
type Props = {
url: string
@@ -15,6 +14,8 @@
const {url}: Props = $props()
const relay = deriveRelay(url)
const rooms = deriveUserRooms(url)
const members = deriveSpaceMembers(url)
</script>
<div class="col-4 text-left">
@@ -31,7 +32,7 @@
{/if}
</div>
</div>
{#if $userRoomsByUrl.has(url)}
{#if $rooms.includes(url)}
<div
class="tooltip absolute -right-1 -top-1 h-5 w-5 rounded-full bg-primary"
data-tip="You are already a member of this space.">
@@ -48,10 +49,10 @@
</div>
<RelayDescription {url} />
</div>
{#if gt($membersByUrl.get(url)?.size, 0)}
{#if $members.length > 0}
<div class="row-2 card2 card2-sm bg-alt">
Members:
<ProfileCircles pubkeys={Array.from($membersByUrl.get(url) || [])} />
<ProfileCircles pubkeys={$members} />
</div>
{/if}
</div>
+3 -3
View File
@@ -2,7 +2,7 @@
import {onMount} from "svelte"
import {sleep, nthEq} from "@welshman/lib"
import {request} from "@welshman/net"
import {displayRelayUrl, AUTH_INVITE} from "@welshman/util"
import {displayRelayUrl, 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"
@@ -38,7 +38,7 @@
request({
relays: [url],
autoClose: true,
filters: [{kinds: [AUTH_INVITE]}],
filters: [{kinds: [RELAY_INVITE]}],
}),
sleep(2000),
])
@@ -83,7 +83,7 @@
This invite link can be used by clicking "Add Space" and pasting it there.
{#if !claim}
This space did not issue a claim for this link, so additional steps might be
required for people using this invite link.
required.
{/if}
</p>
{/snippet}