parent
a7fa996e84
commit
fdf3d855df
@ -246,8 +246,8 @@ export default function Note(props: NoteProps) {
|
||||
}
|
||||
|
||||
const maxMentions = 2;
|
||||
const replyId = thread?.replyTo?.Event ?? thread?.root?.Event;
|
||||
const replyRelayHints = thread?.replyTo?.Relay ?? thread.root?.Relay;
|
||||
const replyId = thread?.replyTo?.value ?? thread?.root?.value;
|
||||
const replyRelayHints = thread?.replyTo?.relay ?? thread.root?.relay;
|
||||
const mentions: { pk: string; name: string; link: ReactNode }[] = [];
|
||||
for (const pk of thread?.pubKeys ?? []) {
|
||||
const u = UserCache.getFromCache(pk);
|
||||
|
@ -239,9 +239,9 @@ export default function Thread() {
|
||||
.sort((a, b) => b.created_at - a.created_at)
|
||||
.forEach(v => {
|
||||
const t = EventExt.extractThread(v);
|
||||
let replyTo = t?.replyTo?.Event ?? t?.root?.Event;
|
||||
if (t?.root?.ATag) {
|
||||
const parsed = t.root.ATag.split(":");
|
||||
let replyTo = t?.replyTo?.value ?? t?.root?.value;
|
||||
if (t?.root?.key === "a" && t?.root?.value) {
|
||||
const parsed = t.root.value.split(":");
|
||||
replyTo = thread.data?.find(
|
||||
a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2]
|
||||
)?.id;
|
||||
@ -274,14 +274,14 @@ export default function Thread() {
|
||||
|
||||
// sometimes the root event ID is missing, and we can only take the happy path if the root event ID exists
|
||||
if (replyTo) {
|
||||
if (replyTo.ATag) {
|
||||
const parsed = replyTo.ATag.split(":");
|
||||
if (replyTo.key === "a" && replyTo.value) {
|
||||
const parsed = replyTo.value.split(":");
|
||||
return thread.data?.find(
|
||||
a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2]
|
||||
);
|
||||
}
|
||||
if (replyTo.Event) {
|
||||
return thread.data?.find(a => a.id === replyTo.Event);
|
||||
if (replyTo.value) {
|
||||
return thread.data?.find(a => a.id === replyTo.value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,7 +305,11 @@ export default function Thread() {
|
||||
const parent = useMemo(() => {
|
||||
if (root) {
|
||||
const currentThread = EventExt.extractThread(root);
|
||||
return currentThread?.replyTo?.Event ?? currentThread?.root?.Event ?? currentThread?.root?.ATag;
|
||||
return (
|
||||
currentThread?.replyTo?.value ??
|
||||
currentThread?.root?.value ??
|
||||
(currentThread?.root?.key === "a" && currentThread.root?.value)
|
||||
);
|
||||
}
|
||||
}, [root]);
|
||||
|
||||
|
@ -1,14 +1,21 @@
|
||||
import * as secp from "@noble/curves/secp256k1";
|
||||
import * as utils from "@noble/curves/abstract/utils";
|
||||
import { EventKind, HexKey, NostrEvent, Tag } from ".";
|
||||
import { EventKind, HexKey, NostrEvent } from ".";
|
||||
import base64 from "@protobufjs/base64";
|
||||
import { sha256, unixNow } from "./Utils";
|
||||
|
||||
export interface Tag {
|
||||
key: string
|
||||
value?: string
|
||||
relay?: string
|
||||
marker?: string // NIP-10
|
||||
}
|
||||
|
||||
export interface Thread {
|
||||
root?: Tag;
|
||||
replyTo?: Tag;
|
||||
mentions: Array<Tag>;
|
||||
pubKeys: Array<HexKey>;
|
||||
root?: Tag
|
||||
replyTo?: Tag
|
||||
mentions: Array<Tag>
|
||||
pubKeys: Array<HexKey>
|
||||
}
|
||||
|
||||
export abstract class EventExt {
|
||||
@ -73,6 +80,24 @@ export abstract class EventExt {
|
||||
} as NostrEvent;
|
||||
}
|
||||
|
||||
static parseTag(tag: Array<string>) {
|
||||
if (tag.length < 1) {
|
||||
throw new Error("Invalid tag, must have more than 2 items")
|
||||
}
|
||||
|
||||
const ret = {
|
||||
key: tag[0],
|
||||
value: tag[1]
|
||||
} as Tag;
|
||||
switch (ret.key) {
|
||||
case "e": {
|
||||
ret.relay = tag.length > 2 ? tag[2] : undefined;
|
||||
ret.marker = tag.length > 3 ? tag[3] : undefined;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static extractThread(ev: NostrEvent) {
|
||||
const isThread = ev.tags.some(a => (a[0] === "e" && a[3] !== "mention") || a[0] == "a");
|
||||
if (!isThread) {
|
||||
@ -84,27 +109,27 @@ export abstract class EventExt {
|
||||
mentions: [],
|
||||
pubKeys: [],
|
||||
} as Thread;
|
||||
const eTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map((v, i) => new Tag(v, i));
|
||||
const marked = eTags.some(a => a.Marker !== undefined);
|
||||
const eTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map(a => EventExt.parseTag(a));
|
||||
const marked = eTags.some(a => a.marker);
|
||||
if (!marked) {
|
||||
ret.root = eTags[0];
|
||||
ret.root.Marker = shouldWriteMarkers ? "root" : undefined;
|
||||
ret.root.marker = shouldWriteMarkers ? "root" : undefined;
|
||||
if (eTags.length > 1) {
|
||||
ret.replyTo = eTags[1];
|
||||
ret.replyTo.Marker = shouldWriteMarkers ? "reply" : undefined;
|
||||
ret.replyTo = eTags[eTags.length - 1];
|
||||
ret.replyTo.marker = shouldWriteMarkers ? "reply" : undefined;
|
||||
}
|
||||
if (eTags.length > 2) {
|
||||
ret.mentions = eTags.slice(2);
|
||||
ret.mentions = eTags.slice(1, -1);
|
||||
if (shouldWriteMarkers) {
|
||||
ret.mentions.forEach(a => (a.Marker = "mention"));
|
||||
ret.mentions.forEach(a => (a.marker = "mention"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const root = eTags.find(a => a.Marker === "root");
|
||||
const reply = eTags.find(a => a.Marker === "reply");
|
||||
const root = eTags.find(a => a.marker === "root");
|
||||
const reply = eTags.find(a => a.marker === "reply");
|
||||
ret.root = root;
|
||||
ret.replyTo = reply;
|
||||
ret.mentions = eTags.filter(a => a.Marker === "mention");
|
||||
ret.mentions = eTags.filter(a => a.marker === "mention");
|
||||
}
|
||||
ret.pubKeys = Array.from(new Set(ev.tags.filter(a => a[0] === "p").map(a => a[1])));
|
||||
return ret;
|
||||
|
@ -214,7 +214,7 @@ export class EventPublisher {
|
||||
const thread = EventExt.extractThread(replyTo);
|
||||
if (thread) {
|
||||
if (thread.root || thread.replyTo) {
|
||||
eb.tag(["e", thread.root?.Event ?? thread.replyTo?.Event ?? "", "", "root"]);
|
||||
eb.tag(["e", thread.root?.value ?? thread.replyTo?.value ?? "", "", "root"]);
|
||||
}
|
||||
eb.tag(["e", replyTo.id, replyTo.relays?.[0] ?? "", "reply"]);
|
||||
|
||||
|
@ -1,88 +0,0 @@
|
||||
import { HexKey, u256 } from "./Nostr";
|
||||
import { unwrap } from "./Utils";
|
||||
|
||||
export default class Tag {
|
||||
Original: string[];
|
||||
Key: string;
|
||||
Event?: u256;
|
||||
PubKey?: HexKey;
|
||||
Relay?: string;
|
||||
Marker?: string;
|
||||
Hashtag?: string;
|
||||
DTag?: string;
|
||||
ATag?: string;
|
||||
Index: number;
|
||||
Invalid: boolean;
|
||||
LNURL?: string;
|
||||
|
||||
constructor(tag: string[], index: number) {
|
||||
this.Original = tag;
|
||||
this.Key = tag[0];
|
||||
this.Index = index;
|
||||
this.Invalid = false;
|
||||
|
||||
switch (this.Key) {
|
||||
case "e": {
|
||||
// ["e", <event-id>, <relay-url>, <marker>]
|
||||
this.Event = tag[1];
|
||||
this.Relay = tag.length > 2 ? tag[2] : undefined;
|
||||
this.Marker = tag.length > 3 ? tag[3] : undefined;
|
||||
if (!this.Event) {
|
||||
this.Invalid = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "p": {
|
||||
// ["p", <pubkey>]
|
||||
this.PubKey = tag[1];
|
||||
if (!this.PubKey) {
|
||||
this.Invalid = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "d": {
|
||||
this.DTag = tag[1];
|
||||
break;
|
||||
}
|
||||
case "a": {
|
||||
this.ATag = tag[1];
|
||||
break;
|
||||
}
|
||||
case "t": {
|
||||
this.Hashtag = tag[1];
|
||||
break;
|
||||
}
|
||||
case "delegation": {
|
||||
this.PubKey = tag[1];
|
||||
break;
|
||||
}
|
||||
case "zap": {
|
||||
this.LNURL = tag[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToObject(): string[] | null {
|
||||
switch (this.Key) {
|
||||
case "e": {
|
||||
let ret = ["e", this.Event, this.Relay, this.Marker];
|
||||
const trimEnd = ret.reverse().findIndex(a => a !== undefined);
|
||||
ret = ret.reverse().slice(0, ret.length - trimEnd);
|
||||
return <string[]>ret;
|
||||
}
|
||||
case "p": {
|
||||
return this.PubKey ? ["p", this.PubKey] : null;
|
||||
}
|
||||
case "t": {
|
||||
return ["t", unwrap(this.Hashtag)];
|
||||
}
|
||||
case "d": {
|
||||
return ["d", unwrap(this.DTag)];
|
||||
}
|
||||
default: {
|
||||
return this.Original;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ export * from "./NostrSystem";
|
||||
export { default as EventKind } from "./EventKind";
|
||||
export * from "./Nostr";
|
||||
export * from "./Links";
|
||||
export { default as Tag } from "./Tag";
|
||||
export * from "./Nips";
|
||||
export * from "./RelayInfo";
|
||||
export * from "./EventExt";
|
||||
|
62
packages/system/tests/EventExt.test.ts
Normal file
62
packages/system/tests/EventExt.test.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { EventExt } from "../src/EventExt";
|
||||
|
||||
describe("NIP-10", () => {
|
||||
it("should extract thread", () => {
|
||||
const a = {
|
||||
content: "This is the problem with Lightning....",
|
||||
id: "868187063f...",
|
||||
kind: 1,
|
||||
created_at: 1,
|
||||
pubkey: "test",
|
||||
sig: "test",
|
||||
"tags": [
|
||||
["e", "cbf2375078..."],
|
||||
["e", "977ac5d3b6..."],
|
||||
["e", "8f99ca1363..."],
|
||||
]
|
||||
}
|
||||
|
||||
const b = {
|
||||
"content": "This is a good point, but your ...",
|
||||
"id": "434ad4a646...",
|
||||
kind: 1,
|
||||
created_at: 1,
|
||||
pubkey: "test",
|
||||
sig: "test",
|
||||
"tags": [
|
||||
["e", "cbf2375078..."],
|
||||
["e", "868187063f..."],
|
||||
["e", "6834ffc491..."],
|
||||
]
|
||||
}
|
||||
|
||||
const c = {
|
||||
"content": "There is some middle ground ...",
|
||||
"id": "6834ffc491...",
|
||||
kind: 1,
|
||||
created_at: 1,
|
||||
pubkey: "test",
|
||||
sig: "test",
|
||||
"tags": [
|
||||
["e", "cbf2375078...", "", "root"],
|
||||
["e", "868187063f...", "", "reply"],
|
||||
]
|
||||
}
|
||||
|
||||
expect(EventExt.extractThread(a)).toMatchObject({
|
||||
root: { key: "e", value: "cbf2375078...", marker: "root" },
|
||||
replyTo: { key: "e", value: "8f99ca1363...", marker: "reply" },
|
||||
mentions: [{ key: "e", value: "977ac5d3b6...", marker: "mention" }]
|
||||
})
|
||||
expect(EventExt.extractThread(b)).toMatchObject({
|
||||
root: { key: "e", value: "cbf2375078...", marker: "root" },
|
||||
replyTo: { key: "e", value: "6834ffc491...", marker: "reply" },
|
||||
mentions: [{ key: "e", value: "868187063f...", marker: "mention" }]
|
||||
})
|
||||
expect(EventExt.extractThread(c)).toMatchObject({
|
||||
root: { key: "e", value: "cbf2375078...", relay: "", marker: "root" },
|
||||
replyTo: { key: "e", value: "868187063f...", relay: "", marker: "reply" },
|
||||
mentions: []
|
||||
})
|
||||
})
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user