Simplify thread loading

This commit is contained in:
Kieran 2023-09-19 09:58:15 +01:00
parent 6a9314d513
commit 04239123bb
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
2 changed files with 35 additions and 88 deletions

View File

@ -1,101 +1,48 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { u256, EventKind, NostrLink, FlatNoteStore, RequestBuilder, NostrPrefix } from "@snort/system"; import { EventKind, NostrLink, RequestBuilder, NoteCollection } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react"; import { useRequestBuilder } from "@snort/system-react";
import { appendDedupe } from "SnortUtils";
import useLogin from "Hooks/useLogin"; import useLogin from "Hooks/useLogin";
import { useReactions } from "./FeedReactions";
interface RelayTaggedEventId {
id: u256;
relay?: string;
}
export default function useThreadFeed(link: NostrLink) { export default function useThreadFeed(link: NostrLink) {
const [trackingEvents, setTrackingEvent] = useState<Array<RelayTaggedEventId>>([]); const [allEvents, setAllEvents] = useState<Array<NostrLink>>([]);
const [trackingATags, setTrackingATags] = useState<string[]>([]);
const [allEvents, setAllEvents] = useState<Array<RelayTaggedEventId>>([]);
const pref = useLogin().preferences; const pref = useLogin().preferences;
const sub = useMemo(() => { const sub = useMemo(() => {
const sub = new RequestBuilder(`thread:${link.id.substring(0, 8)}`); const sub = new RequestBuilder(`thread:${link.id}`);
sub.withOptions({ sub.withOptions({
leaveOpen: true, leaveOpen: true,
}); });
if (trackingEvents.length > 0) { sub.withFilter()
for (const te of trackingEvents) { .kinds([EventKind.TextNote])
const fTracking = sub.withFilter(); .link(link);
fTracking.ids([te.id]);
if (te.relay) {
fTracking.relay(te.relay);
}
}
}
if (allEvents.length > 0) { if (allEvents.length > 0) {
sub const f = sub
.withFilter() .withFilter()
.kinds( .kinds([EventKind.TextNote]);
pref.enableReactions allEvents.forEach(x => f.replyToLink(x));
? [EventKind.Reaction, EventKind.TextNote, EventKind.Repost, EventKind.ZapReceipt]
: [EventKind.TextNote, EventKind.ZapReceipt, EventKind.Repost],
)
.tag(
"e",
allEvents.map(a => a.id),
);
}
if (trackingATags.length > 0) {
const parsed = trackingATags.map(a => a.split(":"));
sub
.withFilter()
.kinds(parsed.map(a => Number(a[0])))
.authors(parsed.map(a => a[1]))
.tag(
"d",
parsed.map(a => a[2]),
);
sub.withFilter().tag("a", trackingATags);
} }
return sub; return sub;
}, [trackingEvents, trackingATags, allEvents, pref]); }, [allEvents.length, pref]);
const store = useRequestBuilder(FlatNoteStore, sub); const store = useRequestBuilder(NoteCollection, sub);
useEffect(() => {
if (link.type === NostrPrefix.Address) {
setTrackingATags([`${link.kind}:${link.author}:${link.id}`]);
} else {
const lnk = {
id: link.id,
relay: link.relays?.[0],
};
setTrackingEvent([lnk]);
setAllEvents([lnk]);
}
}, [link.id]);
useEffect(() => { useEffect(() => {
if (store.data) { if (store.data) {
const mainNotes = store.data?.filter(a => a.kind === EventKind.TextNote || a.kind === EventKind.Polls) ?? []; const mainNotes = store.data?.filter(a => a.kind === EventKind.TextNote || a.kind === EventKind.Polls) ?? [];
const links = mainNotes.map(a => [
const eTags = mainNotes NostrLink.fromEvent(a),
.map(a => ...a.tags.filter(a => a[0] === "e" || a[0] === "a").map(v => NostrLink.fromTag(v))
a.tags ]).flat();
.filter(b => b[0] === "e") setAllEvents(links);
.map(b => {
return {
id: b[1],
relay: b[2],
};
}),
)
.flat();
const eTagsMissing = eTags.filter(a => !mainNotes.some(b => b.id === a.id));
setTrackingEvent(s => appendDedupe(s, eTagsMissing));
setAllEvents(s => appendDedupe(s, eTags));
const aTags = mainNotes.map(a => a.tags.filter(b => b[0] === "a").map(b => b[1])).flat();
setTrackingATags(s => appendDedupe(s, aTags));
} }
}, [store]); }, [store.data?.length]);
return store; const reactions = useReactions(`thread:${link.id}:reactions`, allEvents);
return {
thread: store.data ?? [],
reactions: reactions.data ?? [],
};
} }

View File

@ -25,19 +25,19 @@ export const ThreadContext = createContext({} as ThreadContext);
export function ThreadContextWrapper({ link, children }: { link: NostrLink; children?: ReactNode }) { export function ThreadContextWrapper({ link, children }: { link: NostrLink; children?: ReactNode }) {
const location = useLocation(); const location = useLocation();
const [currentId, setCurrentId] = useState(link.id); const [currentId, setCurrentId] = useState(link.id);
const thread = useThreadFeed(link); const feed = useThreadFeed(link);
const chains = useMemo(() => { const chains = useMemo(() => {
const chains = new Map<u256, Array<TaggedNostrEvent>>(); const chains = new Map<u256, Array<TaggedNostrEvent>>();
if (thread.data) { if (feed.thread) {
thread.data feed.thread
?.sort((a, b) => b.created_at - a.created_at) ?.sort((a, b) => b.created_at - a.created_at)
.forEach(v => { .forEach(v => {
const t = EventExt.extractThread(v); const t = EventExt.extractThread(v);
let replyTo = t?.replyTo?.value ?? t?.root?.value; let replyTo = t?.replyTo?.value ?? t?.root?.value;
if (t?.root?.key === "a" && t?.root?.value) { if (t?.root?.key === "a" && t?.root?.value) {
const parsed = t.root.value.split(":"); const parsed = t.root.value.split(":");
replyTo = thread.data?.find( replyTo = feed.thread?.find(
a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2], a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2],
)?.id; )?.id;
} }
@ -51,12 +51,12 @@ export function ThreadContextWrapper({ link, children }: { link: NostrLink; chil
}); });
} }
return chains; return chains;
}, [thread.data]); }, [feed.thread]);
// Root is the parent of the current note or the current note if its a root note or the root of the thread // Root is the parent of the current note or the current note if its a root note or the root of the thread
const root = useMemo(() => { const root = useMemo(() => {
const currentNote = const currentNote =
thread.data?.find( feed.thread?.find(
ne => ne =>
ne.id === currentId || ne.id === currentId ||
(link.type === NostrPrefix.Address && findTag(ne, "d") === currentId && ne.pubkey === link.author), (link.type === NostrPrefix.Address && findTag(ne, "d") === currentId && ne.pubkey === link.author),
@ -74,16 +74,16 @@ export function ThreadContextWrapper({ link, children }: { link: NostrLink; chil
if (replyTo) { if (replyTo) {
if (replyTo.key === "a" && replyTo.value) { if (replyTo.key === "a" && replyTo.value) {
const parsed = replyTo.value.split(":"); const parsed = replyTo.value.split(":");
return thread.data?.find( return feed.thread?.find(
a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2], a => a.kind === Number(parsed[0]) && a.pubkey === parsed[1] && findTag(a, "d") === parsed[2],
); );
} }
if (replyTo.value) { if (replyTo.value) {
return thread.data?.find(a => a.id === replyTo.value); return feed.thread?.find(a => a.id === replyTo.value);
} }
} }
const possibleRoots = thread.data?.filter(a => { const possibleRoots = feed.thread?.filter(a => {
const thread = EventExt.extractThread(a); const thread = EventExt.extractThread(a);
return isRoot(thread); return isRoot(thread);
}); });
@ -98,14 +98,14 @@ export function ThreadContextWrapper({ link, children }: { link: NostrLink; chil
} }
} }
} }
}, [thread.data, currentId, location]); }, [feed.thread, currentId, location]);
const ctxValue = useMemo(() => { const ctxValue = useMemo(() => {
return { return {
current: currentId, current: currentId,
root, root,
chains, chains,
data: thread.data, data: feed.reactions,
setCurrent: v => setCurrentId(v), setCurrent: v => setCurrentId(v),
} as ThreadContext; } as ThreadContext;
}, [root, chains]); }, [root, chains]);