chore: cleanup chat links
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
import { unwrap } from "@snort/shared";
|
import { unwrap } from "@snort/shared";
|
||||||
import { decodeTLV, EventKind, RequestBuilder, TLVEntryType } from "@snort/system";
|
import { decodeTLV, EventKind, NostrPrefix, RequestBuilder, TLVEntryType } from "@snort/system";
|
||||||
import { useRequestBuilder } from "@snort/system-react";
|
import { useRequestBuilder } from "@snort/system-react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export function useEmptyChatSystem(id?: string) {
|
|||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
|
|
||||||
if (id.startsWith("chat281")) {
|
if (id.startsWith(NostrPrefix.Chat28)) {
|
||||||
const cx = unwrap(decodeTLV(id).find(a => a.type === TLVEntryType.Special)).value as string;
|
const cx = unwrap(decodeTLV(id).find(a => a.type === TLVEntryType.Special)).value as string;
|
||||||
const rb = new RequestBuilder(`nip28:${id}`);
|
const rb = new RequestBuilder(`nip28:${id}`);
|
||||||
rb.withFilter().ids([cx]).kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]);
|
rb.withFilter().ids([cx]).kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]);
|
||||||
|
@ -18,7 +18,7 @@ export default function HelpPage() {
|
|||||||
values={{
|
values={{
|
||||||
link: (
|
link: (
|
||||||
<Link
|
<Link
|
||||||
to={`/messages/${encodeTLVEntries("chat4" as NostrPrefix, {
|
to={`/messages/${encodeTLVEntries(NostrPrefix.Chat17, {
|
||||||
type: TLVEntryType.Author,
|
type: TLVEntryType.Author,
|
||||||
length: 64,
|
length: 64,
|
||||||
value: bech32ToHex(KieranPubKey),
|
value: bech32ToHex(KieranPubKey),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { decodeTLV, EventKind } from "@snort/system";
|
import { decodeTLV, EventKind, NostrPrefix } from "@snort/system";
|
||||||
import { useUserSearch } from "@snort/system-react";
|
import { useUserSearch } from "@snort/system-react";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
@ -125,7 +125,7 @@ export default function NewChatWindow() {
|
|||||||
const evList = await publisher?.generic(eb => {
|
const evList = await publisher?.generic(eb => {
|
||||||
eb.kind(EventKind.PublicChatsList);
|
eb.kind(EventKind.PublicChatsList);
|
||||||
chats.forEach(c => {
|
chats.forEach(c => {
|
||||||
if (c.startsWith("chat281")) {
|
if (c.startsWith(NostrPrefix.Chat28)) {
|
||||||
eb.tag(["e", decodeTLV(c)[0].value as string]);
|
eb.tag(["e", decodeTLV(c)[0].value as string]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -74,9 +74,9 @@ const AvatarSection = ({
|
|||||||
<IconButton
|
<IconButton
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
navigate(
|
navigate(
|
||||||
`/messages/${encodeTLVEntries("chat4" as NostrPrefix, {
|
`/messages/${encodeTLVEntries(NostrPrefix.Chat17, {
|
||||||
type: TLVEntryType.Author,
|
type: TLVEntryType.Author,
|
||||||
length: 32,
|
length: 64,
|
||||||
value: id,
|
value: id,
|
||||||
})}`,
|
})}`,
|
||||||
)
|
)
|
||||||
|
@ -22,12 +22,10 @@ import useModeration from "@/Hooks/useModeration";
|
|||||||
import { findTag } from "@/Utils";
|
import { findTag } from "@/Utils";
|
||||||
import { LoginSession } from "@/Utils/Login";
|
import { LoginSession } from "@/Utils/Login";
|
||||||
|
|
||||||
import { Nip4Chats, Nip4ChatSystem } from "./nip4";
|
|
||||||
import { Nip17Chats, Nip17ChatSystem } from "./nip17";
|
import { Nip17Chats, Nip17ChatSystem } from "./nip17";
|
||||||
import { Nip28Chats, Nip28ChatSystem } from "./nip28";
|
import { Nip28Chats, Nip28ChatSystem } from "./nip28";
|
||||||
|
|
||||||
export enum ChatType {
|
export enum ChatType {
|
||||||
DirectMessage = 1,
|
|
||||||
PublicGroupChat = 2,
|
PublicGroupChat = 2,
|
||||||
PrivateGroupChat = 3,
|
PrivateGroupChat = 3,
|
||||||
PrivateDirectMessage = 4,
|
PrivateDirectMessage = 4,
|
||||||
@ -116,31 +114,17 @@ export function setLastReadIn(id: string) {
|
|||||||
|
|
||||||
export function createChatLink(type: ChatType, ...params: Array<string>) {
|
export function createChatLink(type: ChatType, ...params: Array<string>) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ChatType.DirectMessage: {
|
|
||||||
if (params.length > 1) throw new Error("Must only contain one pubkey");
|
|
||||||
return `/messages/${encodeTLVEntries(
|
|
||||||
"chat4" as NostrPrefix,
|
|
||||||
{
|
|
||||||
type: TLVEntryType.Author,
|
|
||||||
length: params[0].length,
|
|
||||||
value: params[0],
|
|
||||||
} as TLVEntry,
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
case ChatType.PrivateDirectMessage: {
|
case ChatType.PrivateDirectMessage: {
|
||||||
if (params.length > 1) throw new Error("Must only contain one pubkey");
|
if (params.length > 1) throw new Error("Must only contain one pubkey");
|
||||||
return `/messages/${encodeTLVEntries(
|
return `/messages/${encodeTLVEntries(NostrPrefix.Chat17, {
|
||||||
"chat17" as NostrPrefix,
|
type: TLVEntryType.Author,
|
||||||
{
|
length: params[0].length,
|
||||||
type: TLVEntryType.Author,
|
value: params[0],
|
||||||
length: params[0].length,
|
} as TLVEntry)}`;
|
||||||
value: params[0],
|
|
||||||
} as TLVEntry,
|
|
||||||
)}`;
|
|
||||||
}
|
}
|
||||||
case ChatType.PrivateGroupChat: {
|
case ChatType.PrivateGroupChat: {
|
||||||
return `/messages/${encodeTLVEntries(
|
return `/messages/${encodeTLVEntries(
|
||||||
"chat17" as NostrPrefix,
|
NostrPrefix.Chat17,
|
||||||
...params.map(
|
...params.map(
|
||||||
a =>
|
a =>
|
||||||
({
|
({
|
||||||
@ -159,13 +143,10 @@ export function createChatLink(type: ChatType, ...params: Array<string>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createEmptyChatObject(id: string, messages?: Array<TaggedNostrEvent>) {
|
export function createEmptyChatObject(id: string, messages?: Array<TaggedNostrEvent>) {
|
||||||
if (id.startsWith("chat41")) {
|
if (id.startsWith(NostrPrefix.Chat17)) {
|
||||||
return Nip4ChatSystem.createChatObj(id, messages ?? []);
|
|
||||||
}
|
|
||||||
if (id.startsWith("chat171")) {
|
|
||||||
return Nip17ChatSystem.createChatObj(id, []);
|
return Nip17ChatSystem.createChatObj(id, []);
|
||||||
}
|
}
|
||||||
if (id.startsWith("chat281")) {
|
if (id.startsWith(NostrPrefix.Chat28)) {
|
||||||
return Nip28ChatSystem.createChatObj(id, messages ?? []);
|
return Nip28ChatSystem.createChatObj(id, messages ?? []);
|
||||||
}
|
}
|
||||||
throw new Error("Cant create new empty chat, unknown id");
|
throw new Error("Cant create new empty chat, unknown id");
|
||||||
@ -198,7 +179,6 @@ export function useChatSystem(chat: ChatSystem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useChatSystems() {
|
export function useChatSystems() {
|
||||||
//const nip4 = useChatSystem(Nip4Chats);
|
|
||||||
const nip28 = useChatSystem(Nip28Chats);
|
const nip28 = useChatSystem(Nip28Chats);
|
||||||
const nip17 = useChatSystem(Nip17Chats);
|
const nip17 = useChatSystem(Nip17Chats);
|
||||||
|
|
||||||
@ -207,13 +187,10 @@ export function useChatSystems() {
|
|||||||
|
|
||||||
export function useChat(id: string) {
|
export function useChat(id: string) {
|
||||||
const getStore = () => {
|
const getStore = () => {
|
||||||
if (id.startsWith("chat41")) {
|
if (id.startsWith(NostrPrefix.Chat17)) {
|
||||||
return Nip4Chats;
|
|
||||||
}
|
|
||||||
if (id.startsWith("chat171")) {
|
|
||||||
return Nip17Chats;
|
return Nip17Chats;
|
||||||
}
|
}
|
||||||
if (id.startsWith("chat281")) {
|
if (id.startsWith(NostrPrefix.Chat28)) {
|
||||||
return Nip28Chats;
|
return Nip28Chats;
|
||||||
}
|
}
|
||||||
throw new Error("Unsupported chat system");
|
throw new Error("Unsupported chat system");
|
||||||
|
@ -53,7 +53,7 @@ export class Nip17ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
|
|||||||
.filter(a => a !== pk);
|
.filter(a => a !== pk);
|
||||||
|
|
||||||
return encodeTLVEntries(
|
return encodeTLVEntries(
|
||||||
"chat17" as NostrPrefix,
|
NostrPrefix.Chat17,
|
||||||
...pTags.map(
|
...pTags.map(
|
||||||
v =>
|
v =>
|
||||||
({
|
({
|
||||||
|
@ -26,7 +26,7 @@ export class Nip28ChatSystem implements ChatSystem {
|
|||||||
];
|
];
|
||||||
|
|
||||||
subscription(session: LoginSession): RequestBuilder | undefined {
|
subscription(session: LoginSession): RequestBuilder | undefined {
|
||||||
const chats = (session.extraChats ?? []).filter(a => a.startsWith("chat281"));
|
const chats = (session.extraChats ?? []).filter(a => a.startsWith(NostrPrefix.Chat28));
|
||||||
if (chats.length === 0) return;
|
if (chats.length === 0) return;
|
||||||
|
|
||||||
const chatId = (v: string) => unwrap(decodeTLV(v).find(a => a.type === TLVEntryType.Special)).value as string;
|
const chatId = (v: string) => unwrap(decodeTLV(v).find(a => a.type === TLVEntryType.Special)).value as string;
|
||||||
@ -57,7 +57,7 @@ export class Nip28ChatSystem implements ChatSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static chatId(id: string) {
|
static chatId(id: string) {
|
||||||
return encodeTLVEntries("chat28" as NostrPrefix, {
|
return encodeTLVEntries(NostrPrefix.Chat28, {
|
||||||
type: TLVEntryType.Special,
|
type: TLVEntryType.Special,
|
||||||
value: id,
|
value: id,
|
||||||
length: id.length,
|
length: id.length,
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
import {
|
|
||||||
decodeTLV,
|
|
||||||
encodeTLVEntries,
|
|
||||||
EventKind,
|
|
||||||
NostrEvent,
|
|
||||||
NostrPrefix,
|
|
||||||
RequestBuilder,
|
|
||||||
SystemInterface,
|
|
||||||
TaggedNostrEvent,
|
|
||||||
TLVEntryType,
|
|
||||||
} from "@snort/system";
|
|
||||||
|
|
||||||
import { Chat, ChatSystem, ChatType, inChatWith, lastReadInChat } from "@/chat";
|
|
||||||
import { LoginSession } from "@/Utils/Login";
|
|
||||||
|
|
||||||
export class Nip4ChatSystem implements ChatSystem {
|
|
||||||
subscription(session: LoginSession) {
|
|
||||||
const pk = session.publicKey;
|
|
||||||
if (!pk || session.readonly) return;
|
|
||||||
|
|
||||||
const rb = new RequestBuilder(`nip4:${pk.slice(0, 12)}`);
|
|
||||||
rb.withFilter().authors([pk]).kinds([EventKind.DirectMessage]);
|
|
||||||
rb.withFilter().kinds([EventKind.DirectMessage]).tag("p", [pk]);
|
|
||||||
return rb;
|
|
||||||
}
|
|
||||||
|
|
||||||
listChats(pk: string, evs: Array<TaggedNostrEvent>): Chat[] {
|
|
||||||
const myDms = this.#nip4Events(evs);
|
|
||||||
const chats = myDms.reduce(
|
|
||||||
(acc, v) => {
|
|
||||||
const chatId = inChatWith(v, pk);
|
|
||||||
acc[chatId] ??= [];
|
|
||||||
acc[chatId].push(v);
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, Array<NostrEvent>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
return [...Object.entries(chats)].map(([k, v]) => Nip4ChatSystem.createChatObj(Nip4ChatSystem.makeChatId(k), v));
|
|
||||||
}
|
|
||||||
|
|
||||||
static makeChatId(pubkey: string) {
|
|
||||||
return encodeTLVEntries("chat4" as NostrPrefix, {
|
|
||||||
type: TLVEntryType.Author,
|
|
||||||
value: pubkey,
|
|
||||||
length: 32,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static createChatObj(id: string, messages: Array<NostrEvent>) {
|
|
||||||
const last = lastReadInChat(id);
|
|
||||||
const participants = decodeTLV(id)
|
|
||||||
.filter(v => v.type === TLVEntryType.Author)
|
|
||||||
.map(v => ({
|
|
||||||
type: "pubkey",
|
|
||||||
id: v.value as string,
|
|
||||||
}));
|
|
||||||
return {
|
|
||||||
type: ChatType.DirectMessage,
|
|
||||||
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.map(m => ({
|
|
||||||
id: m.id,
|
|
||||||
created_at: m.created_at,
|
|
||||||
from: m.pubkey,
|
|
||||||
tags: m.tags,
|
|
||||||
content: "",
|
|
||||||
needsDecryption: true,
|
|
||||||
decrypt: async pub => {
|
|
||||||
return await pub.decryptDm(m);
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
createMessage: async (msg, pub) => {
|
|
||||||
return await Promise.all(participants.map(v => pub.sendDm(msg, v.id)));
|
|
||||||
},
|
|
||||||
sendMessage: (ev, system: SystemInterface) => {
|
|
||||||
ev.forEach(a => system.BroadcastEvent(a));
|
|
||||||
},
|
|
||||||
} as Chat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nip4Events(evs: Array<TaggedNostrEvent>) {
|
|
||||||
return evs.filter(a => a.kind === EventKind.DirectMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Nip4Chats = new Nip4ChatSystem();
|
|
@ -182,7 +182,7 @@ self.addEventListener("notificationclick", event => {
|
|||||||
}
|
}
|
||||||
} else if (ev.type === PushType.DirectMessage) {
|
} else if (ev.type === PushType.DirectMessage) {
|
||||||
const reaction = ev.data as CompactReaction;
|
const reaction = ev.data as CompactReaction;
|
||||||
return `/messages/${encodeTLVEntries("chat4" as NostrPrefix, {
|
return `/messages/${encodeTLVEntries(NostrPrefix.Chat17, {
|
||||||
type: TLVEntryType.Author,
|
type: TLVEntryType.Author,
|
||||||
value: reaction.author.pubkey,
|
value: reaction.author.pubkey,
|
||||||
length: 32,
|
length: 32,
|
||||||
|
@ -13,6 +13,8 @@ export enum NostrPrefix {
|
|||||||
Relay = "nrelay",
|
Relay = "nrelay",
|
||||||
Address = "naddr",
|
Address = "naddr",
|
||||||
Req = "nreq",
|
Req = "nreq",
|
||||||
|
Chat17 = "nchat17",
|
||||||
|
Chat28 = "nchat28",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TLVEntryType {
|
export enum TLVEntryType {
|
||||||
|
Reference in New Issue
Block a user