From d442166846ff6cd8057f50138c2e777f97bf0cc6 Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 6 May 2025 11:35:08 +0100 Subject: [PATCH] chore: remove nip28 support --- packages/app/config/iris.json | 3 +- packages/app/custom.d.ts | 2 +- packages/app/src/Hooks/useEmptyChatSystem.tsx | 29 ---- packages/app/src/Pages/Donate/DonatePage.tsx | 13 -- .../app/src/Pages/Messages/NewChatWindow.tsx | 36 +---- .../src/Pages/Messages/Nip28ChatProfile.tsx | 24 --- packages/app/src/chat/index.ts | 19 +-- packages/app/src/chat/nip28.ts | 144 ------------------ packages/app/src/chat/nip29.ts | 109 ------------- packages/system/src/links.ts | 1 - 10 files changed, 6 insertions(+), 374 deletions(-) delete mode 100644 packages/app/src/Hooks/useEmptyChatSystem.tsx delete mode 100644 packages/app/src/Pages/Messages/Nip28ChatProfile.tsx delete mode 100644 packages/app/src/chat/nip28.ts delete mode 100644 packages/app/src/chat/nip29.ts diff --git a/packages/app/config/iris.json b/packages/app/config/iris.json index 7f364cb6..fb8029de 100644 --- a/packages/app/config/iris.json +++ b/packages/app/config/iris.json @@ -45,8 +45,7 @@ "wss://relay.damus.io/": { "read": true, "write": true } }, "chatChannels": [ - { "type": "telegram", "value": "https://t.me/irismessenger" }, - { "type": "nip28", "value": "23286a4602ada10cc10200553bff62a110e8dc0eacddf73277395a89ddf26a09" } + { "type": "telegram", "value": "https://t.me/irismessenger" } ], "alby": { "clientId": "5rYcHDrlDb", diff --git a/packages/app/custom.d.ts b/packages/app/custom.d.ts index 80de3eac..4c95121c 100644 --- a/packages/app/custom.d.ts +++ b/packages/app/custom.d.ts @@ -101,7 +101,7 @@ declare const CONFIG: { // public chat channels for site chatChannels?: Array<{ - type: "nip28" | "telegram"; + type: "telegram"; value: string; }>; }; diff --git a/packages/app/src/Hooks/useEmptyChatSystem.tsx b/packages/app/src/Hooks/useEmptyChatSystem.tsx deleted file mode 100644 index dd7294da..00000000 --- a/packages/app/src/Hooks/useEmptyChatSystem.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { unwrap } from "@snort/shared"; -import { decodeTLV, EventKind, NostrPrefix, RequestBuilder, TLVEntryType } from "@snort/system"; -import { useRequestBuilder } from "@snort/system-react"; -import { useMemo } from "react"; - -import { createEmptyChatObject } from "@/chat"; - -export function useEmptyChatSystem(id?: string) { - const sub = useMemo(() => { - if (id?.startsWith(NostrPrefix.Chat28)) { - const cx = unwrap(decodeTLV(id).find(a => a.type === TLVEntryType.Special)).value as string; - const rb = new RequestBuilder(`nip28:${id}`); - rb.withFilter().ids([cx]).kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]); - rb.withFilter() - .tag("e", [cx]) - .kinds([EventKind.PublicChatChannel, EventKind.PublicChatMessage, EventKind.PublicChatMetadata]); - - return rb; - } else { - return new RequestBuilder(id ?? ""); - } - }, [id]); - - const data = useRequestBuilder(sub); - return useMemo(() => { - if (!id) return; - return createEmptyChatObject(id, data); - }, [id, data.length]); -} diff --git a/packages/app/src/Pages/Donate/DonatePage.tsx b/packages/app/src/Pages/Donate/DonatePage.tsx index a903c18d..e242ab76 100644 --- a/packages/app/src/Pages/Donate/DonatePage.tsx +++ b/packages/app/src/Pages/Donate/DonatePage.tsx @@ -4,7 +4,6 @@ import { FormattedMessage } from "react-intl"; import { Link, useNavigate } from "react-router-dom"; import Telegram from "@/assets/img/telegram.svg"; -import { Nip28ChatSystem } from "@/chat/nip28"; import AsyncButton from "@/Components/Button/AsyncButton"; import Copy from "@/Components/Copy/Copy"; import ZapButton from "@/Components/Event/ZapButton"; @@ -105,18 +104,6 @@ const DonatePage = () => { ); } - case "nip28": { - return ( - { - const id = Nip28ChatSystem.chatId(a.value); - navigate(`/messages/${id}`); - }}> - - - - ); - } } })} diff --git a/packages/app/src/Pages/Messages/NewChatWindow.tsx b/packages/app/src/Pages/Messages/NewChatWindow.tsx index c3d1e444..a4625baa 100644 --- a/packages/app/src/Pages/Messages/NewChatWindow.tsx +++ b/packages/app/src/Pages/Messages/NewChatWindow.tsx @@ -1,21 +1,15 @@ -import { decodeTLV, EventKind, NostrPrefix } from "@snort/system"; import { useUserSearch } from "@snort/system-react"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; import { ChatType, createChatLink } from "@/chat"; -import { Nip28ChatSystem } from "@/chat/nip28"; import Icon from "@/Components/Icons/Icon"; import Modal from "@/Components/Modal/Modal"; import ProfileImage from "@/Components/User/ProfileImage"; import ProfilePreview from "@/Components/User/ProfilePreview"; -import useEventPublisher from "@/Hooks/useEventPublisher"; import useFollowsControls from "@/Hooks/useFollowControls"; -import useLogin from "@/Hooks/useLogin"; -import Nip28ChatProfile from "@/Pages/Messages/Nip28ChatProfile"; import { appendDedupe, debounce } from "@/Utils"; -import { LoginSession, LoginStore } from "@/Utils/Login"; export default function NewChatWindow() { const [show, setShow] = useState(false); @@ -24,8 +18,6 @@ export default function NewChatWindow() { const [term, setSearchTerm] = useState(""); const navigate = useNavigate(); const search = useUserSearch(); - const login = useLogin(); - const { system, publisher } = useEventPublisher(); const { followList } = useFollowsControls(); useEffect(() => { @@ -115,32 +107,6 @@ export default function NewChatWindow() { /> ); })} - {results.length === 1 && ( - { - setShow(false); - const chats = appendDedupe(login.extraChats, [Nip28ChatSystem.chatId(id)]); - LoginStore.updateSession({ - ...login, - extraChats: chats, - } as LoginSession); - const evList = await publisher?.generic(eb => { - eb.kind(EventKind.PublicChatsList); - chats.forEach(c => { - if (c.startsWith(NostrPrefix.Chat28)) { - eb.tag(["e", decodeTLV(c)[0].value as string]); - } - }); - return eb; - }); - if (evList) { - await system.BroadcastEvent(evList); - } - navigate(createChatLink(ChatType.PublicGroupChat, id)); - }} - /> - )} diff --git a/packages/app/src/Pages/Messages/Nip28ChatProfile.tsx b/packages/app/src/Pages/Messages/Nip28ChatProfile.tsx deleted file mode 100644 index c0a2cda2..00000000 --- a/packages/app/src/Pages/Messages/Nip28ChatProfile.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { NostrLink, UserMetadata } from "@snort/system"; -import { useEventFeed } from "@snort/system-react"; -import React from "react"; - -import ProfilePreview from "@/Components/User/ProfilePreview"; - -export default function Nip28ChatProfile({ id, onClick }: { id: string; onClick: (id: string) => void }) { - const channel = useEventFeed(new NostrLink(CONFIG.eventLinkPrefix, id, 40)); - if (channel) { - const meta = JSON.parse(channel.content) as UserMetadata; - return ( - } - onClick={() => onClick(id)} - /> - ); - } -} diff --git a/packages/app/src/chat/index.ts b/packages/app/src/chat/index.ts index 3700a6f4..8e8932d0 100644 --- a/packages/app/src/chat/index.ts +++ b/packages/app/src/chat/index.ts @@ -15,7 +15,6 @@ import { import { useRequestBuilder } from "@snort/system-react"; import { useEffect, useMemo } from "react"; -import { useEmptyChatSystem } from "@/Hooks/useEmptyChatSystem"; import useEventPublisher from "@/Hooks/useEventPublisher"; import useLogin from "@/Hooks/useLogin"; import useModeration from "@/Hooks/useModeration"; @@ -23,7 +22,6 @@ import { findTag } from "@/Utils"; import { LoginSession } from "@/Utils/Login"; import { Nip17Chats, Nip17ChatSystem } from "./nip17"; -import { Nip28Chats, Nip28ChatSystem } from "./nip28"; export enum ChatType { PublicGroupChat = 2, @@ -135,20 +133,14 @@ export function createChatLink(type: ChatType, ...params: Array) { ), )}`; } - case ChatType.PublicGroupChat: { - return `/messages/${Nip28ChatSystem.chatId(params[0])}`; - } } throw new Error("Unknown chat type"); } -export function createEmptyChatObject(id: string, messages?: Array) { +export function createEmptyChatObject(id: string) { if (id.startsWith(NostrPrefix.Chat17)) { return Nip17ChatSystem.createChatObj(id, []); } - if (id.startsWith(NostrPrefix.Chat28)) { - return Nip28ChatSystem.createChatObj(id, messages ?? []); - } throw new Error("Cant create new empty chat, unknown id"); } @@ -179,10 +171,9 @@ export function useChatSystem(chat: ChatSystem) { } export function useChatSystems() { - const nip28 = useChatSystem(Nip28Chats); const nip17 = useChatSystem(Nip17Chats); - return [...nip28, ...nip17]; + return nip17; } export function useChat(id: string) { @@ -190,12 +181,8 @@ export function useChat(id: string) { if (id.startsWith(NostrPrefix.Chat17)) { return Nip17Chats; } - if (id.startsWith(NostrPrefix.Chat28)) { - return Nip28Chats; - } throw new Error("Unsupported chat system"); }; const ret = useChatSystem(getStore()).find(a => a.id === id); - const emptyChat = useEmptyChatSystem(ret === undefined ? id : undefined); - return ret ?? emptyChat; + return ret; } diff --git a/packages/app/src/chat/nip28.ts b/packages/app/src/chat/nip28.ts deleted file mode 100644 index 88f19137..00000000 --- a/packages/app/src/chat/nip28.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { unwrap } from "@snort/shared"; -import { - decodeTLV, - encodeTLVEntries, - EventKind, - NostrEvent, - NostrPrefix, - RequestBuilder, - SystemInterface, - TaggedNostrEvent, - TLVEntryType, - UserMetadata, -} from "@snort/system"; - -import { Chat, ChatParticipant, ChatSystem, ChatType, lastReadInChat } from "@/chat"; -import { findTag } from "@/Utils"; -import { LoginSession } from "@/Utils/Login"; - -export class Nip28ChatSystem implements ChatSystem { - readonly ChannelKinds = [ - EventKind.PublicChatChannel, - EventKind.PublicChatMessage, - EventKind.PublicChatMetadata, - EventKind.PublicChatMuteMessage, - EventKind.PublicChatMuteUser, - ]; - - subscription(session: LoginSession): RequestBuilder { - const chats = (session.extraChats ?? []).filter(a => a.startsWith(NostrPrefix.Chat28)); - const chatId = (v: string) => unwrap(decodeTLV(v).find(a => a.type === TLVEntryType.Special)).value as string; - - const rb = new RequestBuilder(`nip28:${session.id}`); - if (chats.length > 0) { - rb.withFilter() - .ids(chats.map(v => chatId(v))) - .kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]); - for (const c of chats) { - const id = chatId(c); - rb.withFilter().tag("e", [id]).kinds(this.ChannelKinds); - } - } - return rb; - } - - processEvents(): Promise { - // nothing to do - return Promise.resolve(); - } - - listChats(pk: string, evs: Array): Chat[] { - const chats = this.#chatChannels(evs); - const ret = Object.entries(chats).map(([k, v]) => { - return Nip28ChatSystem.createChatObj(Nip28ChatSystem.chatId(k), v); - }); - return ret; - } - - static chatId(id: string) { - return encodeTLVEntries(NostrPrefix.Chat28, { - type: TLVEntryType.Special, - value: id, - length: id.length, - }); - } - - static createChatObj(id: string, messages: Array) { - const last = lastReadInChat(id); - const participants = decodeTLV(id) - .filter(v => v.type === TLVEntryType.Special) - .map( - v => - ({ - type: "generic", - id: v.value as string, - profile: this.#chatProfileFromMessages(messages), - }) as ChatParticipant, - ); - return { - type: ChatType.PublicGroupChat, - id, - unread: messages.reduce((acc, v) => (v.created_at > last ? acc++ : acc), 0), - lastMessage: messages.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0), - participants, - messages: messages - .filter(a => a.kind === EventKind.PublicChatMessage) - .map(m => ({ - id: m.id, - created_at: m.created_at, - from: m.pubkey, - tags: m.tags, - content: m.content, - needsDecryption: false, - })), - createMessage: async (msg, pub) => { - return [ - await pub.generic(eb => { - return eb.kind(EventKind.PublicChatMessage).content(msg).tag(["e", participants[0].id, "", "root"]); - }), - ]; - }, - sendMessage: (ev, system: SystemInterface) => { - ev.forEach(a => system.BroadcastEvent(a)); - }, - } as Chat; - } - - static #chatProfileFromMessages(messages: Array) { - const chatDefs = messages.filter( - a => a.kind === EventKind.PublicChatChannel || a.kind === EventKind.PublicChatMetadata, - ); - const chatDef = - chatDefs.length > 0 - ? chatDefs.reduce((acc, v) => (acc.created_at > v.created_at ? acc : v), chatDefs[0]) - : undefined; - return chatDef ? (JSON.parse(chatDef.content) as UserMetadata) : undefined; - } - - #chatChannels(evs: Array) { - const chats = evs.reduce( - (acc, v) => { - const k = this.#chatId(v); - if (k) { - acc[k] ??= []; - acc[k].push(v); - } - return acc; - }, - {} as Record>, - ); - return chats; - } - - #chatId(ev: NostrEvent) { - if (ev.kind === EventKind.PublicChatChannel) { - return ev.id; - } else if (ev.kind === EventKind.PublicChatMetadata) { - return findTag(ev, "e"); - } else if (this.ChannelKinds.includes(ev.kind)) { - return ev.tags.find(a => a[0] === "e" && a[3] === "root")?.[1]; - } - } -} - -export const Nip28Chats = new Nip28ChatSystem(); diff --git a/packages/app/src/chat/nip29.ts b/packages/app/src/chat/nip29.ts deleted file mode 100644 index 6435601c..00000000 --- a/packages/app/src/chat/nip29.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { dedupe, ExternalStore, FeedCache, removeUndefined } from "@snort/shared"; -import { EventKind, NostrEvent, RequestBuilder, SystemInterface, TaggedNostrEvent } from "@snort/system"; - -import { Chat, ChatSystem, ChatType, lastReadInChat } from "@/chat"; -import { LoginSession } from "@/Utils/Login"; - -export class Nip29ChatSystem extends ExternalStore> implements ChatSystem { - readonly #cache: FeedCache; - - constructor(cache: FeedCache) { - super(); - this.#cache = cache; - } - - processEvents(): Promise { - return Promise.resolve(); - } - - takeSnapshot(): Chat[] { - return this.listChats(); - } - - subscription(session: LoginSession) { - const id = session.publicKey; - if (!id) return; - const gs = id.split("/", 2); - const rb = new RequestBuilder(`nip29:${id}`); - const last = this.listChats().find(a => a.id === id)?.lastMessage; - rb.withFilter() - .relay(`wss://${gs[0]}`) - .kinds([EventKind.SimpleChatMessage]) - .tag("g", [`/${gs[1]}`]) - .since(last); - rb.withFilter() - .relay(`wss://${gs[0]}`) - .kinds([EventKind.SimpleChatMetadata]) - .tag("d", [`/${gs[1]}`]); - return rb; - } - - async onEvent(evs: readonly TaggedNostrEvent[]) { - const msg = evs.filter(a => a.kind === EventKind.SimpleChatMessage && a.tags.some(b => b[0] === "g")); - if (msg.length > 0) { - await this.#cache.bulkSet(msg); - this.notifyChange(); - } - } - - listChats(): Chat[] { - const allMessages = this.#nip29Chats(); - const groups = dedupe( - removeUndefined(allMessages.map(a => a.tags.find(b => b[0] === "g"))).map(a => `${a[2]}${a[1]}`), - ); - return groups.map(g => { - const [relay, channel] = g.split("/", 2); - const messages = allMessages.filter( - a => `${a.tags.find(b => b[0] === "g")?.[2]}${a.tags.find(b => b[0] === "g")?.[1]}` === g, - ); - const lastRead = lastReadInChat(g); - return { - type: ChatType.PublicGroupChat, - id: g, - title: `${relay}/${channel}`, - unread: messages.reduce((acc, v) => (v.created_at > lastRead ? acc++ : acc), 0), - lastMessage: messages.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0), - messages: messages.map(m => ({ - id: m.id, - created_at: m.created_at, - from: m.pubkey, - tags: m.tags, - needsDecryption: false, - content: m.content, - decrypt: async () => { - return m.content; - }, - })), - participants: [ - { - type: "generic", - id: "", - profile: { - name: `${relay}/${channel}`, - }, - }, - ], - createMessage: async (msg, pub) => { - return [ - await pub.generic(eb => { - return eb - .kind(EventKind.SimpleChatMessage) - .tag(["g", `/${channel}`, relay]) - .content(msg); - }), - ]; - }, - sendMessage: async (ev, system: SystemInterface) => { - ev.forEach(async a => { - system.HandleEvent("*", { ...a, relays: [] }); - await system.WriteOnceToRelay(`wss://${relay}`, a); - }); - }, - } as Chat; - }); - } - - #nip29Chats() { - return this.#cache.snapshot().filter(a => a.kind === EventKind.SimpleChatMessage); - } -} diff --git a/packages/system/src/links.ts b/packages/system/src/links.ts index 0c8a71d4..2afa922e 100644 --- a/packages/system/src/links.ts +++ b/packages/system/src/links.ts @@ -14,7 +14,6 @@ export enum NostrPrefix { Address = "naddr", Req = "nreq", Chat17 = "nchat17", - Chat28 = "nchat28", } export enum TLVEntryType {