Improve group membership detection

This commit is contained in:
Jon Staab
2025-05-28 16:46:41 -07:00
parent 72d85e5740
commit f7d11cf124
11 changed files with 223 additions and 118 deletions
+3 -20
View File
@@ -53,6 +53,7 @@ import {
tagEventForComment,
tagEventForQuote,
thunkIsComplete,
getThunkError,
} from "@welshman/app"
import type {Thunk} from "@welshman/app"
import {
@@ -83,21 +84,6 @@ export const getPubkeyPetname = (pubkey: string) => {
return display
}
export const getThunkError = (thunk: Thunk) =>
new Promise<string>(resolve => {
thunk.subscribe($thunk => {
for (const [relay, status] of Object.entries($thunk.status)) {
if (status === PublishStatus.Failure) {
resolve($thunk.details[relay])
}
}
if (thunkIsComplete($thunk)) {
resolve("")
}
})
})
export const prependParent = (parent: TrustedEvent | undefined, {content, tags}: EventContent) => {
if (parent) {
const nevent = nip19.neventEncode({
@@ -189,12 +175,9 @@ export const removeSpaceMembership = async (url: string) => {
return publishThunk({event, relays})
}
export const addRoomMembership = async (url: string, room: string, name: string) => {
export const addRoomMembership = async (url: string, room: string) => {
const list = get(userMembership) || makeList({kind: GROUPS})
const newTags = [
["r", url],
["group", room, url, name],
]
const newTags = [["r", url], ["group", room, url]]
const event = await addToListPublicly(list, ...newTags).reconcile(nip44EncryptToSelf)
const relays = uniq([...Router.get().FromUser().getUrls(), ...getRelayTagValues(event.tags)])
+2 -2
View File
@@ -3,7 +3,7 @@
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
import ChannelName from "@app/components/ChannelName.svelte"
import {makeRoomPath} from "@app/routes"
import {deriveChannel, channelIsLocked} from "@app/state"
import {deriveChannel} from "@app/state"
import {notifications} from "@app/notifications"
interface Props {
@@ -23,7 +23,7 @@
href={path}
{replaceState}
notification={notify ? $notifications.has(path) : false}>
{#if channelIsLocked($channel)}
{#if $channel?.closed || $channel?.private}
<Icon icon="lock" size={4} />
{:else}
<Icon icon="hashtag" />
+2 -3
View File
@@ -2,7 +2,7 @@
import {goto} from "$app/navigation"
import {randomId} from "@welshman/lib"
import {displayRelayUrl} from "@welshman/util"
import {deriveRelay} from "@welshman/app"
import {deriveRelay, getThunkError} from "@welshman/app"
import {preventDefault} from "@lib/html"
import Field from "@lib/components/Field.svelte"
import Spinner from "@lib/components/Spinner.svelte"
@@ -11,7 +11,7 @@
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import {hasNip29, loadChannel} from "@app/state"
import {addRoomMembership, createRoom, editRoom, joinRoom, getThunkError} from "@app/commands"
import {createRoom, editRoom, joinRoom} from "@app/commands"
import {makeSpacePath} from "@app/routes"
import {pushToast} from "@app/toast"
@@ -43,7 +43,6 @@
await loadChannel(url, room)
addRoomMembership(url, room, name)
goto(makeSpacePath(url, room))
}
+39 -7
View File
@@ -34,6 +34,9 @@ import {
GROUPS,
THREAD,
COMMENT,
GROUP_JOIN,
GROUP_ADD_USER,
GROUP_REMOVE_USER,
getGroupTags,
getRelayTagValues,
getPubkeyTagValues,
@@ -43,6 +46,8 @@ import {
getListTags,
asDecryptedEvent,
normalizeRelayUrl,
getTag,
getTagValues,
} from "@welshman/util"
import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util"
import {Nip59, decrypt} from "@welshman/signer"
@@ -486,8 +491,8 @@ export type Channel = {
room: string
name: string
event: TrustedEvent
access: "public" | "private"
membership: "open" | "closed"
closed: boolean
private: boolean
picture?: string
about?: string
}
@@ -520,8 +525,8 @@ export const channels = derived(
room,
event,
name: meta.name || room,
access: meta.private ? "private" : "public",
membership: meta.closed ? "closed" : "open",
closed: Boolean(getTag("closed", event.tags)),
private: Boolean(getTag("private", event.tags)),
picture: meta.picture,
about: meta.about,
})
@@ -563,9 +568,6 @@ export const displayChannel = (url: string, room: string) =>
export const roomComparator = (url: string) => (room: string) =>
displayChannel(url, room).toLowerCase()
export const channelIsLocked = (channel?: Channel) =>
channel?.access === "private" && channel?.membership === "closed"
// User stuff
export const userSettings = withGetter(
@@ -626,6 +628,36 @@ export const deriveOtherRooms = (url: string) =>
),
)
export enum MembershipStatus {
Initial,
Pending,
Granted,
}
export const deriveUserMembershipStatus = (url: string, room: string) =>
derived(
[pubkey, deriveEventsForUrl(url, [{kinds: [GROUP_JOIN, GROUP_ADD_USER, GROUP_REMOVE_USER], '#h': [room]}])],
([$pubkey, $events]) => {
let status = MembershipStatus.Initial
for (const event of $events) {
if (event.kind === GROUP_JOIN && event.pubkey === $pubkey) {
status = MembershipStatus.Pending
}
if (event.kind === GROUP_REMOVE_USER && getTagValues("p", event.tags).includes($pubkey!)) {
break
}
if (event.kind === GROUP_ADD_USER && getTagValues("p", event.tags).includes($pubkey!)) {
return MembershipStatus.Granted
}
}
return status
}
)
// Other utils
export const encodeRelay = (url: string) =>