refactor: thread loading improvements

This commit is contained in:
2024-04-04 16:30:30 +01:00
parent ad2b6dbcf7
commit ee31726961
29 changed files with 272 additions and 972 deletions

View File

@ -56,7 +56,7 @@ const LinkPreview = ({ url }: { url: string }) => {
const urlTags = ["og:video:secure_url", "og:video:url", "og:video"];
const link = preview?.og_tags?.find(a => urlTags.includes(a[0].toLowerCase()))?.[1];
const videoType = preview?.og_tags?.find(a => a[0].toLowerCase() === "og:video:type")?.[1] ?? "video/mp4";
if (link) {
if (link && videoType.startsWith("video/")) {
return <MediaElement url={link} mime={videoType} />;
}
}

View File

@ -149,11 +149,12 @@ function useGoToEvent(props, options) {
}
function Reaction({ ev }: { ev: TaggedNostrEvent }) {
const reactedToTag = ev.tags.find((tag: string[]) => tag[0] === "e");
const reactedToTag = ev.tags.findLast(tag => tag[0] === "e");
const pTag = ev.tags.findLast(tag => tag[0] === "p");
if (!reactedToTag?.length) {
return null;
}
const link = NostrLink.fromTag(reactedToTag);
const link = NostrLink.fromTag(reactedToTag, pTag?.[1]);
return (
<div className="note card">
<div className="text-gray-medium font-bold">

View File

@ -1,10 +1,14 @@
import { EventExt, EventKind, NostrLink, RequestBuilder } from "@snort/system";
import { useReactions, useRequestBuilder } from "@snort/system-react";
import { useEffect, useMemo, useState } from "react";
import { SnortContext, useRequestBuilder } from "@snort/system-react";
import { useContext, useEffect, useMemo, useState } from "react";
import { randomSample } from "@/Utils";
export default function useThreadFeed(link: NostrLink) {
const [root, setRoot] = useState<NostrLink>();
const [rootRelays, setRootRelays] = useState<Array<string>>();
const [allEvents, setAllEvents] = useState<Array<NostrLink>>([]);
const system = useContext(SnortContext);
const sub = useMemo(() => {
const sub = new RequestBuilder(`thread:${link.id.slice(0, 12)}`);
@ -13,7 +17,7 @@ export default function useThreadFeed(link: NostrLink) {
});
sub.withFilter().link(link);
if (root) {
sub.withFilter().link(root);
sub.withFilter().link(root).relay(rootRelays ?? []);
}
const grouped = [link, ...allEvents].reduce(
(acc, v) => {
@ -24,11 +28,14 @@ export default function useThreadFeed(link: NostrLink) {
{} as Record<string, Array<NostrLink>>,
);
for (const [, v] of Object.entries(grouped)) {
sub.withFilter().kinds([EventKind.TextNote]).replyToLink(v);
for (const v of Object.values(grouped)) {
sub.withFilter()
.kinds([EventKind.TextNote])
.replyToLink(v)
.relay(rootRelays ?? []);
}
return sub;
}, [allEvents.length]);
}, [allEvents.length, rootRelays]);
const store = useRequestBuilder(sub);
@ -57,15 +64,28 @@ export default function useThreadFeed(link: NostrLink) {
]),
);
}
} else {
setRoot(link);
}
}
}
}, [store?.length]);
const reactions = useReactions(`thread:${link.id.slice(0, 12)}:reactions`, [link, ...allEvents]);
useEffect(() => {
if (root) {
const rootEvent = store?.find(a => root.matchesEvent(a));
if (rootEvent) {
system.relayCache.buffer([rootEvent.pubkey]).then(() => {
const relays = system.relayCache.getFromCache(rootEvent.pubkey);
return {
thread: store ?? [],
reactions: reactions ?? [],
};
if (relays) {
const readRelays = randomSample(relays.relays.filter(a => a.settings.read).map(a => a.url), 3);
setRootRelays(readRelays);
}
})
}
}
}, [link, root, store?.length]);
return store ?? [];
}

View File

@ -2,13 +2,12 @@
import { TaggedNostrEvent } from "@snort/system";
import { createContext } from "react";
interface ThreadContext {
export interface ThreadContextState {
current: string;
root?: TaggedNostrEvent;
chains: Map<string, Array<TaggedNostrEvent>>;
data: Array<TaggedNostrEvent>;
reactions: Array<TaggedNostrEvent>;
setCurrent: (i: string) => void;
}
export const ThreadContext = createContext({} as ThreadContext);
export const ThreadContext = createContext({} as ThreadContextState);

View File

@ -6,7 +6,7 @@ import { useLocation } from "react-router-dom";
import useThreadFeed from "@/Feed/ThreadFeed";
import useModeration from "@/Hooks/useModeration";
import { chainKey, replyChainKey } from "@/Utils/Thread/ChainKey";
import { ThreadContext } from "@/Utils/Thread/ThreadContext";
import { ThreadContext, ThreadContextState } from "@/Utils/Thread/ThreadContext";
export function ThreadContextWrapper({ link, children }: { link: NostrLink; children?: ReactNode }) {
const location = useLocation();
@ -16,8 +16,8 @@ export function ThreadContextWrapper({ link, children }: { link: NostrLink; chil
const chains = useMemo(() => {
const chains = new Map<u256, Array<TaggedNostrEvent>>();
if (feed.thread) {
feed.thread
if (feed) {
feed
?.filter(a => !isBlocked(a.pubkey))
.forEach(v => {
const replyTo = replyChainKey(v);
@ -31,30 +31,29 @@ export function ThreadContextWrapper({ link, children }: { link: NostrLink; chil
});
}
return chains;
}, [feed.thread]);
}, [feed]);
// 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 =
feed.thread?.find(a => chainKey(a) === currentId) ??
feed?.find(a => chainKey(a) === currentId) ??
(location.state && "sig" in location.state ? (location.state as TaggedNostrEvent) : undefined);
if (currentNote) {
const key = replyChainKey(currentNote);
if (key) {
return feed.thread?.find(a => chainKey(a) === key);
return feed?.find(a => chainKey(a) === key);
} else {
return currentNote;
}
}
}, [feed.thread.length, currentId, location]);
}, [feed.length, currentId, location]);
const ctxValue = useMemo<ThreadContext>(() => {
const ctxValue = useMemo<ThreadContextState>(() => {
return {
current: currentId,
root,
chains,
reactions: feed.reactions,
data: feed.thread,
data: feed,
setCurrent: v => setCurrentId(v),
};
}, [root, chains]);