Init SnortDeck

This commit is contained in:
2023-09-07 15:54:25 +01:00
parent 5960a5bf5f
commit a31e30979d
30 changed files with 903 additions and 436 deletions

View 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>
}