diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e08b8b5..6fa8ddbe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4243,8 +4243,8 @@ packages: svelte: optional: true - svelte@4.2.19: - resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==} + svelte@4.2.20: + resolution: {integrity: sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==} engines: {node: '>=16'} svelte@5.25.10: @@ -6442,7 +6442,7 @@ snapshots: '@welshman/util': 0.3.0(typescript@5.8.3) fuse.js: 7.1.0 idb: 8.0.2 - svelte: 4.2.19 + svelte: 4.2.20 throttle-debounce: 5.0.2 transitivePeerDependencies: - nostr-signer-capacitor-plugin @@ -6563,7 +6563,7 @@ snapshots: '@welshman/lib': 0.3.0 '@welshman/relay': 0.3.0(typescript@5.8.3) '@welshman/util': 0.3.0(typescript@5.8.3) - svelte: 4.2.19 + svelte: 4.2.20 transitivePeerDependencies: - typescript @@ -9327,7 +9327,7 @@ snapshots: optionalDependencies: svelte: 5.25.10 - svelte@4.2.19: + svelte@4.2.20: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 diff --git a/src/app/commands.ts b/src/app/commands.ts index a6652b46..35d2b7bc 100644 --- a/src/app/commands.ts +++ b/src/app/commands.ts @@ -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(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)]) diff --git a/src/app/components/MenuSpaceRoomItem.svelte b/src/app/components/MenuSpaceRoomItem.svelte index 1a66672a..e19cc265 100644 --- a/src/app/components/MenuSpaceRoomItem.svelte +++ b/src/app/components/MenuSpaceRoomItem.svelte @@ -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} {:else} diff --git a/src/app/components/RoomCreate.svelte b/src/app/components/RoomCreate.svelte index e5c71d7f..0a5195c6 100644 --- a/src/app/components/RoomCreate.svelte +++ b/src/app/components/RoomCreate.svelte @@ -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)) } diff --git a/src/app/state.ts b/src/app/state.ts index 09d28ea0..bf48ec68 100644 --- a/src/app/state.ts +++ b/src/app/state.ts @@ -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) => diff --git a/src/assets/icons/Bookmark.svg b/src/assets/icons/Bookmark.svg new file mode 100644 index 00000000..31a523d7 --- /dev/null +++ b/src/assets/icons/Bookmark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/Star.svg b/src/assets/icons/Star.svg new file mode 100644 index 00000000..669ceb5e --- /dev/null +++ b/src/assets/icons/Star.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/lib/components/Icon.svelte b/src/lib/components/Icon.svelte index 75cc7aee..c4a95a3f 100644 --- a/src/lib/components/Icon.svelte +++ b/src/lib/components/Icon.svelte @@ -9,6 +9,7 @@ import {switcher} from "@welshman/lib" import AddSquare from "@assets/icons/Add Square.svg?dataurl" import ArrowsALogout2 from "@assets/icons/Arrows ALogout 2.svg?dataurl" + import Bookmark from "@assets/icons/Bookmark.svg?dataurl" import Code2 from "@assets/icons/Code 2.svg?dataurl" import Document from "@assets/icons/Document.svg?dataurl" import Earth from "@assets/icons/Earth.svg?dataurl" @@ -80,6 +81,7 @@ import SmileCircle from "@assets/icons/Smile Circle.svg?dataurl" import SquareShareLine from "@assets/icons/Square Share Line.svg?dataurl" import SortVertical from "@assets/icons/Sort Vertical.svg?dataurl" + import Star from "@assets/icons/Star.svg?dataurl" import TrashBin2 from "@assets/icons/Trash Bin 2.svg?dataurl" import UFO3 from "@assets/icons/UFO 3.svg?dataurl" import UserHeart from "@assets/icons/User Heart.svg?dataurl" @@ -104,6 +106,7 @@ const data = switcher(icon, { "add-square": AddSquare, "arrows-a-logout-2": ArrowsALogout2, + bookmark: Bookmark, "code-2": Code2, document: Document, earth: Earth, @@ -177,6 +180,7 @@ "ufo-3": UFO3, "square-share-line": SquareShareLine, "sort-vertical": SortVertical, + star: Star, "user-heart": UserHeart, "user-circle": UserCircle, "user-rounded": UserRounded, diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index c9b86760..1d870792 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -17,7 +17,6 @@ MESSAGE, INBOX_RELAYS, DIRECT_MESSAGE, - GROUP_META, MUTES, FOLLOWS, PROFILE, @@ -174,11 +173,7 @@ limit: 10_000, repository, rankEvent: (e: TrustedEvent) => { - if ( - [PROFILE, FOLLOWS, MUTES, RELAYS, BLOSSOM_SERVERS, INBOX_RELAYS, GROUP_META].includes( - e.kind, - ) - ) { + if ([PROFILE, FOLLOWS, MUTES, RELAYS, BLOSSOM_SERVERS, INBOX_RELAYS].includes(e.kind)) { return 1 } diff --git a/src/routes/spaces/[relay]/+page.svelte b/src/routes/spaces/[relay]/+page.svelte index 5412370a..fe546dc7 100644 --- a/src/routes/spaces/[relay]/+page.svelte +++ b/src/routes/spaces/[relay]/+page.svelte @@ -20,7 +20,6 @@ import { hasNip29, decodeRelay, - channelIsLocked, makeChannelId, channelsById, deriveUserRooms, @@ -155,9 +154,10 @@ {#each $userRooms as room (room)} {@const roomPath = makeRoomPath(url, room)} + {@const channel = $channelsById.get(makeChannelId(url, room))}
- {#if channelIsLocked($channelsById.get(makeChannelId(url, room)))} + {#if channel?.closed || channel?.private} {:else} @@ -173,9 +173,10 @@ Other Rooms
{#each $otherRooms as room (room)} + {@const channel = $channelsById.get(makeChannelId(url, room))}
- {#if channelIsLocked($channelsById.get(makeChannelId(url, room)))} + {#if channel?.closed || channel?.private} {:else} diff --git a/src/routes/spaces/[relay]/[room]/+page.svelte b/src/routes/spaces/[relay]/[room]/+page.svelte index 55ed11ad..209d5f1f 100644 --- a/src/routes/spaces/[relay]/[room]/+page.svelte +++ b/src/routes/spaces/[relay]/[room]/+page.svelte @@ -1,12 +1,14 @@