From 4c9b7da58656f13d84c81a6d842a31174b17154b Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Mon, 23 Sep 2024 10:28:33 -0700 Subject: [PATCH] Move from topic to room using tilde --- README.md | 7 +++ src/app/commands.ts | 11 ++-- src/app/components/ChatCompose.svelte | 7 ++- src/app/components/ChatMessage.svelte | 5 +- src/app/components/EventCreate.svelte | 1 - src/app/components/PrimaryNav.svelte | 2 +- src/app/components/RoomCreate.svelte | 10 ++-- src/app/routes.ts | 2 +- src/app/state.ts | 53 ++++++++++--------- src/routes/discover/+page.svelte | 2 +- src/routes/spaces/[nrelay]/+layout.svelte | 22 ++++---- .../{[[topic]] => [[room]]}/+page.svelte | 20 +++---- 12 files changed, 75 insertions(+), 67 deletions(-) rename src/routes/spaces/[nrelay]/{[[topic]] => [[room]]}/+page.svelte (86%) diff --git a/README.md b/README.md index c90081b9..73a3eed8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ # Flotilla A discord-like nostr client. WIP. + +# Notes + +- Privacy, migrations, and content replication + - Allow relays to strip signatures based on auth'd user + - Federated relays/admins can get signatures + - Other users have to opt-in to using the relay in trusted mode diff --git a/src/app/commands.ts b/src/app/commands.ts index ddfe9e0b..4ec27628 100644 --- a/src/app/commands.ts +++ b/src/app/commands.ts @@ -14,7 +14,7 @@ import { loadMutes, followsByPubkey, } from "@welshman/app" -import {MEMBERSHIPS, INDEXER_RELAYS} from "@app/state" +import {ROOM, MEMBERSHIPS, INDEXER_RELAYS} from "@app/state" // Utils @@ -95,9 +95,9 @@ export const updateList = async (kind: number, modifyTags: ModifyTags) => { export const addSpaceMembership = (url: string) => updateList(MEMBERSHIPS, (tags: string[][]) => uniqBy(t => t.join(""), [...tags, ["r", url]])) -export const addRoomMembership = (url: string, topic: string) => +export const addRoomMembership = (url: string, room: string) => updateList(MEMBERSHIPS, (tags: string[][]) => - uniqBy(t => t.join(""), [...tags, ["t", topic, url]]), + uniqBy(t => t.join(""), [...tags, [ROOM, room, url]]), ) export const removeSpaceMembership = (url: string) => @@ -105,5 +105,6 @@ export const removeSpaceMembership = (url: string) => tags.filter(t => !equals(["r", url], t) && t[2] !== url), ) -export const removeRoomMembership = (url: string, topic: string) => - updateList(MEMBERSHIPS, (tags: string[][]) => tags.filter(t => !equals(["t", topic, url], t))) +export const removeRoomMembership = (url: string, room: string) => + updateList(MEMBERSHIPS, (tags: string[][]) => tags.filter(t => !equals([ROOM, room, url], t))) + diff --git a/src/app/components/ChatCompose.svelte b/src/app/components/ChatCompose.svelte index be554ee4..d3de38ce 100644 --- a/src/app/components/ChatCompose.svelte +++ b/src/app/components/ChatCompose.svelte @@ -11,10 +11,10 @@ import Button from "@lib/components/Button.svelte" import {makeMention, makeIMeta} from "@app/commands" import {getChatEditorOptions, addFile} from "@app/editor" - import {MESSAGE} from "@app/state" + import {ROOM, MESSAGE, GENERAL} from "@app/state" export let url - export let topic = "" + export let room = GENERAL const uploading = writable(false) @@ -22,7 +22,6 @@ const sendMessage = () => { const json = $editor.getJSON() - const topicTags = topic ? [["t", topic]] : [] const mentionTags = findNodes(NProfileExtension.name, json).map(m => makeMention(m.attrs!.pubkey, m.attrs!.relays), ) @@ -32,7 +31,7 @@ const event = createEvent(MESSAGE, { content: $editor.getText(), - tags: [["-"], ...topicTags, ...mentionTags, ...imetaTags], + tags: [[ROOM, room], ...mentionTags, ...imetaTags], }) publishThunk(makeThunk({event, relays: [url]})) diff --git a/src/app/components/ChatMessage.svelte b/src/app/components/ChatMessage.svelte index beed9543..61a12ee3 100644 --- a/src/app/components/ChatMessage.svelte +++ b/src/app/components/ChatMessage.svelte @@ -16,7 +16,6 @@ } from "@welshman/app" import type {PublishStatusData} from "@welshman/app" import { - GROUP_REPLY, REACTION, ZAP_RESPONSE, displayRelayUrl, @@ -27,7 +26,7 @@ import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" import Avatar from "@lib/components/Avatar.svelte" - import {deriveEvent, displayReaction} from "@app/state" + import {REPLY, deriveEvent, displayReaction} from "@app/state" import {getChatViewOptions} from "@app/editor" export let event: TrustedEvent @@ -85,7 +84,7 @@
- {#if event.kind === GROUP_REPLY} + {#if event.kind === REPLY}
diff --git a/src/app/components/EventCreate.svelte b/src/app/components/EventCreate.svelte index f1ca0335..36e40d83 100644 --- a/src/app/components/EventCreate.svelte +++ b/src/app/components/EventCreate.svelte @@ -52,7 +52,6 @@ const event = createEvent(kind, { content: $editor.getText(), tags: [ - ["-"], ["d", randomId()], ["title", title], ["location", location], diff --git a/src/app/components/PrimaryNav.svelte b/src/app/components/PrimaryNav.svelte index ef007025..289790fb 100644 --- a/src/app/components/PrimaryNav.svelte +++ b/src/app/components/PrimaryNav.svelte @@ -51,7 +51,7 @@ class="!h-10 !w-10 border border-solid border-base-300" size={7} /> - {#each $userMembership?.topicsByUrl.keys() || [] as url (url)} + {#each $userMembership?.roomsByUrl.keys() || [] as url (url)} diff --git a/src/app/components/RoomCreate.svelte b/src/app/components/RoomCreate.svelte index 79bc5f51..b3c21f5e 100644 --- a/src/app/components/RoomCreate.svelte +++ b/src/app/components/RoomCreate.svelte @@ -13,9 +13,9 @@ const back = () => history.back() const tryCreate = async () => { - await addRoomMembership(url, topic) + await addRoomMembership(url, room) - goto(makeSpacePath(url, topic)) + goto(makeSpacePath(url, room)) } const create = async () => { @@ -28,7 +28,7 @@ } } - let topic = "" + let room = "" let loading = false @@ -41,7 +41,7 @@

Room Name

@@ -49,7 +49,7 @@ Go back - diff --git a/src/app/routes.ts b/src/app/routes.ts index f1480d3e..e6d70bd5 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -15,7 +15,7 @@ export const makeSpacePath = (url: string, extra = "") => { export const getPrimaryNavItem = ($page: Page) => $page.route?.id?.split("/")[1] export const getPrimaryNavItemIndex = ($page: Page) => { - const urls = Array.from(userMembership.get()?.topicsByUrl.keys() || []) + const urls = Array.from(userMembership.get()?.roomsByUrl.keys() || []) switch (getPrimaryNavItem($page)) { case "discover": diff --git a/src/app/state.ts b/src/app/state.ts index 0904de42..cd6646f6 100644 --- a/src/app/state.ts +++ b/src/app/state.ts @@ -1,7 +1,7 @@ import {nip19} from "nostr-tools" import {get, derived} from "svelte/store" import type {Maybe} from "@welshman/lib" -import {setContext, max, pushToMapKey, nthEq} from "@welshman/lib" +import {setContext, nth, max, pushToMapKey, nthEq} from "@welshman/lib" import { getIdFilters, RELAYS, @@ -10,7 +10,6 @@ import { EVENT_DATE, EVENT_TIME, getRelayTagValues, - getTopicTagValues, isShareableRelayUrl, } from "@welshman/util" import type {TrustedEvent} from "@welshman/util" @@ -31,9 +30,13 @@ import { import type {SubscribeRequestWithHandlers} from "@welshman/net" import {deriveEvents, deriveEventsMapped, withGetter} from "@welshman/store" +export const ROOM = "~" + +export const GENERAL = "general" + export const MESSAGE = 209 -export const REPLY = 210 +export const REPLY = 1111 export const MEMBERSHIPS = 10209 @@ -75,7 +78,7 @@ export const deriveEvent = (idOrAddress: string, hints: string[] = []) => { // Membership export type Membership = { - topicsByUrl: Map + roomsByUrl: Map event?: TrustedEvent } @@ -84,17 +87,17 @@ export type PublishedMembership = Omit & { } export const readMembership = (event: TrustedEvent): PublishedMembership => { - const topicsByUrl = new Map() + const roomsByUrl = new Map() for (const tag of event.tags.filter(nthEq(0, "r"))) { - topicsByUrl.set(tag[1], []) + roomsByUrl.set(tag[1], []) } for (const tag of event.tags.filter(nthEq(0, "t"))) { - pushToMapKey(topicsByUrl, tag[2], tag[1]) + pushToMapKey(roomsByUrl, tag[2], tag[1]) } - return {event, topicsByUrl} + return {event, roomsByUrl} } export const memberships = deriveEventsMapped(repository, { @@ -121,16 +124,16 @@ export const { // Messages export type Message = { - topic: string + room: string event: TrustedEvent } export const readMessage = (event: TrustedEvent): Maybe => { - const topics = getTopicTagValues(event.tags) + const rooms = event.tags.filter(nthEq(0, ROOM)).map(nth(1)) - if (topics.length > 1) return undefined + if (rooms.length > 1) return undefined - return {topic: topics[0] || "", event} + return {room: rooms[0] || "", event} } export const messages = deriveEventsMapped(repository, { @@ -144,11 +147,11 @@ export const messages = deriveEventsMapped(repository, { export type Chat = { id: string url: string - topic: string + room: string messages: Message[] } -export const makeChatId = (url: string, topic: string) => `${url}'${topic}` +export const makeChatId = (url: string, room: string) => `${url}'${room}` export const splitChatId = (id: string) => id.split("'") @@ -157,16 +160,16 @@ export const chats = derived([trackerStore, messages], ([$tracker, $messages]) = for (const message of $messages) { for (const url of $tracker.getRelays(message.event.id)) { - const chatId = makeChatId(url, message.topic) + const chatId = makeChatId(url, message.room) pushToMapKey(messagesByChatId, chatId, message) } } return Array.from(messagesByChatId.entries()).map(([id, messages]) => { - const [url, topic] = splitChatId(id) + const [url, room] = splitChatId(id) - return {id, url, topic, messages} + return {id, url, room, messages} }) }) @@ -179,12 +182,12 @@ export const { store: chats, getKey: chat => chat.id, load: (id: string, request: Partial = {}) => { - const [url, topic] = splitChatId(id) + const [url, room] = splitChatId(id) const chat = get(chatsById).get(id) const timestamps = chat?.messages.map(m => m.event.created_at) || [] const since = Math.max(0, max(timestamps) - 3600) - return load({...request, relays: [url], filters: [{"#t": [topic], since}]}) + return load({...request, relays: [url], filters: [{'#~': [room], since}]}) }, }) @@ -204,18 +207,18 @@ export const eventsByUrl = derived([trackerStore, events], ([$tracker, $events]) return eventsByUrl }) -// Topics +// Rooms -export const topicsByUrl = derived(chats, $chats => { - const $topicsByUrl = new Map() +export const roomsByUrl = derived(chats, $chats => { + const $roomsByUrl = new Map() for (const chat of $chats) { - if (chat.topic) { - pushToMapKey($topicsByUrl, chat.url, chat.topic) + if (chat.room) { + pushToMapKey($roomsByUrl, chat.url, chat.room) } } - return $topicsByUrl + return $roomsByUrl }) // User stuff diff --git a/src/routes/discover/+page.svelte b/src/routes/discover/+page.svelte index 92157a6d..bb16ec1f 100644 --- a/src/routes/discover/+page.svelte +++ b/src/routes/discover/+page.svelte @@ -56,7 +56,7 @@ {/if}
- {#if $userMembership?.topicsByUrl.has(relay.url)} + {#if $userMembership?.roomsByUrl.has(relay.url)}
!rooms.includes(t)) + $: rooms = sort($userMembership?.roomsByUrl?.get(url) || []) + $: otherRooms = ($roomsByUrl.get(url) || []).filter(room => !rooms.concat(GENERAL).includes(room)) onMount(() => { const kinds = [MESSAGE, REPLY, EVENT_DATE, EVENT_TIME, CLASSIFIED] @@ -72,7 +72,7 @@
{/if} - {#each rooms as topic, i (topic)} + {#each rooms as room, i (room)}
- + - {topic} + {room}
{/each} @@ -133,11 +133,11 @@
{/if} - {#each otherRooms as topic, i (topic)} + {#each otherRooms as room, i (room)}
- + - {topic} + {room}
{/each} @@ -150,7 +150,7 @@ - {#key $page.params.topic} + {#key $page.params.room} {/key} diff --git a/src/routes/spaces/[nrelay]/[[topic]]/+page.svelte b/src/routes/spaces/[nrelay]/[[room]]/+page.svelte similarity index 86% rename from src/routes/spaces/[nrelay]/[[topic]]/+page.svelte rename to src/routes/spaces/[nrelay]/[[room]]/+page.svelte index 63dc849e..a6b728d5 100644 --- a/src/routes/spaces/[nrelay]/[[topic]]/+page.svelte +++ b/src/routes/spaces/[nrelay]/[[room]]/+page.svelte @@ -18,19 +18,19 @@ import Divider from "@lib/components/Divider.svelte" import ChatMessage from "@app/components/ChatMessage.svelte" import ChatCompose from "@app/components/ChatCompose.svelte" - import {userMembership, decodeNRelay, makeChatId, deriveChat} from "@app/state" + import {userMembership, decodeNRelay, makeChatId, deriveChat, GENERAL} from "@app/state" import {addRoomMembership, removeRoomMembership} from "@app/commands" - const {nrelay, topic = ""} = $page.params + const {nrelay, room = GENERAL} = $page.params const url = decodeNRelay(nrelay) - const chat = deriveChat(makeChatId(url, topic)) + const chat = deriveChat(makeChatId(url, room)) const assertEvent = (e: any) => e as TrustedEvent let loading = true let elements: Element[] = [] - $: membership = $userMembership?.topicsByUrl.get(url) || [] + $: membership = $userMembership?.roomsByUrl.get(url) || [] $: { elements = [] @@ -71,16 +71,16 @@ class="flex min-h-12 items-center justify-between gap-4 rounded-xl bg-base-100 px-4 shadow-xl">
- {topic || "General"} + {room || "General"}
- {#if topic} - {#if membership.includes(topic)} - {:else} - @@ -106,5 +106,5 @@

- +