diff --git a/packages/app/src/Element/Thread.tsx b/packages/app/src/Element/Thread.tsx index 29698173..628ddcf2 100644 --- a/packages/app/src/Element/Thread.tsx +++ b/packages/app/src/Element/Thread.tsx @@ -2,9 +2,9 @@ import "./Thread.css"; import { useMemo, useState, ReactNode, useContext } from "react"; import { useIntl } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; -import { TaggedNostrEvent, u256, NostrPrefix, EventExt, parseNostrLink } from "@snort/system"; +import { TaggedNostrEvent, u256, NostrPrefix, EventExt, parseNostrLink, NostrLink } from "@snort/system"; -import { getReactions, getAllReactions } from "SnortUtils"; +import { getReactions, getAllReactions, unwrap } from "SnortUtils"; import BackButton from "Element/BackButton"; import Note from "Element/Note"; import NoteGhost from "Element/NoteGhost"; @@ -154,9 +154,8 @@ const TierThree = ({ active, isLastSubthread, notes, related, chains, onNavigate return ( <>
+ className={`subthread-container ${hasMultipleNotes ? "subthread-multi" : ""} ${isLast ? "subthread-last" : "subthread-mid" + }`}> + className={`subthread-container ${lastReply ? "" : "subthread-multi"} ${lastReply ? "subthread-last" : "subthread-mid" + }`}> void }) { description: "Navigate back button on threads view", }); + const rootChainId = (ev: TaggedNostrEvent) => { + const link = NostrLink.fromEvent(ev); + return unwrap(link.toEventTag())[1]; + } + return ( <>
@@ -304,7 +307,7 @@ export function Thread(props: { onBack?: () => void }) {
{thread.root && renderRoot(thread.root)} - {thread.root && renderChain(thread.root.id)} + {thread.root && renderChain(rootChainId(thread.root))}
); diff --git a/packages/app/src/Feed/Reactions.ts b/packages/app/src/Feed/Reactions.ts index 6b2c0745..664268c3 100644 --- a/packages/app/src/Feed/Reactions.ts +++ b/packages/app/src/Feed/Reactions.ts @@ -10,15 +10,13 @@ export function useReactions(subId: string, ids: Array, others?: (rb: const rb = new RequestBuilder(subId); if (ids.length > 0) { - const f = rb + rb .withFilter() .kinds( pref.enableReactions ? [EventKind.Reaction, EventKind.Repost, EventKind.ZapReceipt] : [EventKind.ZapReceipt, EventKind.Repost], - ); - - ids.forEach(v => f.replyToLink(v)); + ).replyToLink(ids); } others?.(rb); return rb.numFilters > 0 ? rb : null; diff --git a/packages/app/src/Feed/ThreadFeed.ts b/packages/app/src/Feed/ThreadFeed.ts index 6a8d10fe..4ebd1e10 100644 --- a/packages/app/src/Feed/ThreadFeed.ts +++ b/packages/app/src/Feed/ThreadFeed.ts @@ -13,10 +13,7 @@ export default function useThreadFeed(link: NostrLink) { leaveOpen: true, }); sub.withFilter().link(link); - sub.withFilter().kinds([EventKind.TextNote]).replyToLink(link); - allEvents.forEach(x => { - sub.withFilter().kinds([EventKind.TextNote]).replyToLink(x); - }); + sub.withFilter().kinds([EventKind.TextNote]).replyToLink([link, ...allEvents]); return sub; }, [allEvents.length]); diff --git a/packages/app/src/Feed/ZapsFeed.ts b/packages/app/src/Feed/ZapsFeed.ts index 3026e385..87e57881 100644 --- a/packages/app/src/Feed/ZapsFeed.ts +++ b/packages/app/src/Feed/ZapsFeed.ts @@ -7,7 +7,7 @@ export default function useZapsFeed(link?: NostrLink) { const sub = useMemo(() => { if (!link) return null; const b = new RequestBuilder(`zaps:${link.encode()}`); - b.withFilter().kinds([EventKind.ZapReceipt]).replyToLink(link); + b.withFilter().kinds([EventKind.ZapReceipt]).replyToLink([link]); return b; }, [link]); diff --git a/packages/app/src/Hooks/useThreadContext.tsx b/packages/app/src/Hooks/useThreadContext.tsx index 15649b23..ee5ee31d 100644 --- a/packages/app/src/Hooks/useThreadContext.tsx +++ b/packages/app/src/Hooks/useThreadContext.tsx @@ -15,6 +15,13 @@ export interface ThreadContext { export const ThreadContext = createContext({} as ThreadContext); +export function threadChainKey(ev: TaggedNostrEvent) { + const t = EventExt.extractThread(ev); + if (t) { + return unwrap(t.replyTo?.value ?? t.root?.value); + } +} + export function ThreadContextWrapper({ link, children }: { link: NostrLink; children?: ReactNode }) { const location = useLocation(); const [currentId, setCurrentId] = useState(link.id); @@ -26,21 +33,12 @@ export function ThreadContextWrapper({ link, children }: { link: NostrLink; chil feed.thread ?.sort((a, b) => b.created_at - a.created_at) .forEach(v => { - const t = EventExt.extractThread(v); - if (t) { - let replyTo = t.replyTo?.value ?? t.root?.value; - if (t.root?.key === "a" && t.root?.value) { - const parsed = t.root.value.split(":"); - replyTo = feed.thread?.find( - a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2], - )?.id; - } - if (replyTo) { - if (!chains.has(replyTo)) { - chains.set(replyTo, [v]); - } else { - unwrap(chains.get(replyTo)).push(v); - } + const replyTo = threadChainKey(v); + if (replyTo) { + if (!chains.has(replyTo)) { + chains.set(replyTo, [v]); + } else { + unwrap(chains.get(replyTo)).push(v); } } }); diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index bb5c6c5a..96f95d41 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -38,6 +38,7 @@ import { preload, RelayMetrics, UserCache, UserRelays } from "Cache"; import { LoginStore } from "Login"; import { SnortDeckLayout } from "Pages/DeckLayout"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars const WasmQueryOptimizer = { expandFilter: (f: ReqFilter) => { return expand_filter(f) as Array; @@ -60,7 +61,7 @@ export const System = new NostrSystem({ relayCache: UserRelays, profileCache: UserCache, relayMetrics: RelayMetrics, - queryOptimizer: WasmQueryOptimizer, + //queryOptimizer: WasmQueryOptimizer, authHandler: async (c, r) => { const { id } = LoginStore.snapshot(); const pub = LoginStore.getPublisher(id); diff --git a/packages/system/src/event-publisher.ts b/packages/system/src/event-publisher.ts index 1ebf8391..87201330 100644 --- a/packages/system/src/event-publisher.ts +++ b/packages/system/src/event-publisher.ts @@ -9,6 +9,7 @@ import { HexKey, Lists, NostrEvent, + NostrLink, NotSignedNostrEvent, PowMiner, PrivateKeySigner, @@ -187,9 +188,9 @@ export class EventPublisher { if (thread) { const rootOrReplyAsRoot = thread.root || thread.replyTo; if (rootOrReplyAsRoot) { - eb.tag(["e", rootOrReplyAsRoot?.value ?? "", rootOrReplyAsRoot?.relay ?? "", "root"]); + eb.tag([rootOrReplyAsRoot.key, rootOrReplyAsRoot.value ?? "", rootOrReplyAsRoot.relay ?? "", "root"]); } - eb.tag(["e", replyTo.id, replyTo.relays?.[0] ?? "", "reply"]); + eb.tag([...(NostrLink.fromEvent(replyTo).toEventTag() ?? []), "reply"]); eb.tag(["p", replyTo.pubkey]); for (const pk of thread.pubKeys) { @@ -199,7 +200,7 @@ export class EventPublisher { eb.tag(["p", pk]); } } else { - eb.tag(["e", replyTo.id, "", "reply"]); + eb.tag([...(NostrLink.fromEvent(replyTo).toEventTag() ?? []), "reply"]); // dont tag self in replies if (replyTo.pubkey !== this.#pubKey) { eb.tag(["p", replyTo.pubkey]); diff --git a/packages/system/src/request-builder.ts b/packages/system/src/request-builder.ts index a1aad89c..7d57271f 100644 --- a/packages/system/src/request-builder.ts +++ b/packages/system/src/request-builder.ts @@ -250,16 +250,27 @@ export class RequestFilterBuilder { /** * Get replies to link with e/a tags */ - replyToLink(link: NostrLink) { - if (link.type === NostrPrefix.Address) { - this.tag("a", [`${link.kind}:${link.author}:${link.id}`]); - link.relays?.forEach(v => this.relay(v)); - } else if (link.type === NostrPrefix.PublicKey || link.type === NostrPrefix.Profile) { - this.tag("p", [link.id]); - link.relays?.forEach(v => this.relay(v)); - } else { - this.tag("e", [link.id]); - link.relays?.forEach(v => this.relay(v)); + replyToLink(links: Array) { + const grouped = links.reduce((acc, v) => { + acc[v.type] ??= []; + if (v.type === NostrPrefix.Address) { + acc[v.type].push(`${v.kind}:${v.author}:${v.id}`); + } else if (v.type === NostrPrefix.PublicKey || v.type === NostrPrefix.Profile) { + acc[v.type].push(v.id); + } else { + acc[v.type].push(v.id); + } + return acc; + }, {} as Record>); + + for(const [k,v] of Object.entries(grouped)) { + if (k === NostrPrefix.Address) { + this.tag("a", v); + } else if (k === NostrPrefix.PublicKey || k === NostrPrefix.Profile) { + this.tag("p", v); + } else { + this.tag("e", v); + } } return this; } @@ -293,7 +304,7 @@ export class RequestFilterBuilder { return [ { - filters: [this.filter], + filters: [this.#filter], relay: "", strategy: RequestStrategy.DefaultRelays, },