Init SnortDeck
This commit is contained in:
110
packages/app/src/Hooks/useThreadContext.tsx
Normal file
110
packages/app/src/Hooks/useThreadContext.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { EventExt, EventKind, NostrLink, NostrPrefix, TaggedNostrEvent, u256, Thread as ThreadInfo, } from "@snort/system";
|
||||
import useThreadFeed from "Feed/ThreadFeed";
|
||||
import { findTag } from "SnortUtils";
|
||||
import { ReactNode, createContext, useMemo, useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
export interface ThreadContext {
|
||||
current: string,
|
||||
root?: TaggedNostrEvent,
|
||||
chains: Map<string, Array<TaggedNostrEvent>>,
|
||||
data: Array<TaggedNostrEvent>,
|
||||
setCurrent: (i: string) => void;
|
||||
}
|
||||
|
||||
export const ThreadContext = createContext({} as ThreadContext)
|
||||
|
||||
export function ThreadContextWrapper({ link, children }: { link: NostrLink, children?: ReactNode }) {
|
||||
const location = useLocation();
|
||||
const [currentId, setCurrentId] = useState(link.id);
|
||||
const thread = useThreadFeed(link);
|
||||
|
||||
const chains = useMemo(() => {
|
||||
const chains = new Map<u256, Array<TaggedNostrEvent>>();
|
||||
if (thread.data) {
|
||||
thread.data
|
||||
?.filter(a => a.kind === EventKind.TextNote)
|
||||
.sort((a, b) => b.created_at - a.created_at)
|
||||
.forEach(v => {
|
||||
const t = EventExt.extractThread(v);
|
||||
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;
|
||||
}
|
||||
if (replyTo) {
|
||||
if (!chains.has(replyTo)) {
|
||||
chains.set(replyTo, [v]);
|
||||
} else {
|
||||
unwrap(chains.get(replyTo)).push(v);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return chains;
|
||||
}, [thread.data]);
|
||||
|
||||
// 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 currentNote =
|
||||
thread.data?.find(
|
||||
ne =>
|
||||
ne.id === currentId ||
|
||||
(link.type === NostrPrefix.Address && findTag(ne, "d") === currentId && ne.pubkey === link.author)
|
||||
) ?? (location.state && "sig" in location.state ? (location.state as TaggedNostrEvent) : undefined);
|
||||
if (currentNote) {
|
||||
const currentThread = EventExt.extractThread(currentNote);
|
||||
const isRoot = (ne?: ThreadInfo) => ne === undefined;
|
||||
|
||||
if (isRoot(currentThread)) {
|
||||
return currentNote;
|
||||
}
|
||||
const replyTo = currentThread?.replyTo ?? currentThread?.root;
|
||||
|
||||
// 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.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.value) {
|
||||
return thread.data?.find(a => a.id === replyTo.value);
|
||||
}
|
||||
}
|
||||
|
||||
const possibleRoots = thread.data?.filter(a => {
|
||||
const thread = EventExt.extractThread(a);
|
||||
return isRoot(thread);
|
||||
});
|
||||
if (possibleRoots) {
|
||||
// worst case we need to check every possible root to see which one contains the current note as a child
|
||||
for (const ne of possibleRoots) {
|
||||
const children = chains.get(ne.id) ?? [];
|
||||
|
||||
if (children.find(ne => ne.id === currentId)) {
|
||||
return ne;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [thread.data, currentId, location]);
|
||||
|
||||
const ctxValue = useMemo(() => {
|
||||
return {
|
||||
current: currentId,
|
||||
root,
|
||||
chains,
|
||||
data: thread.data,
|
||||
setCurrent: v => setCurrentId(v)
|
||||
} as ThreadContext
|
||||
}, [root, chains]);
|
||||
|
||||
return <ThreadContext.Provider value={ctxValue}>
|
||||
{children}
|
||||
</ThreadContext.Provider>
|
||||
}
|
Reference in New Issue
Block a user