mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-01 00:19:45 +02:00
remove legacy thread loader
This commit is contained in:
parent
6cb7f0d219
commit
d18e03afe2
5
.changeset/three-cats-refuse.md
Normal file
5
.changeset/three-cats-refuse.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Rebuild thread loading
|
@ -14,7 +14,7 @@ import SettingsView from "./views/settings";
|
||||
import NostrLinkView from "./views/link";
|
||||
import ProfileView from "./views/profile";
|
||||
import HashTagView from "./views/hashtag";
|
||||
import NoteView from "./views/note";
|
||||
import ThreadView from "./views/note";
|
||||
import NotificationsView from "./views/notifications";
|
||||
import DirectMessagesView from "./views/messages";
|
||||
import DirectMessageChatView from "./views/messages/chat";
|
||||
@ -216,7 +216,7 @@ const router = createHashRouter([
|
||||
},
|
||||
{
|
||||
path: "/n/:id",
|
||||
element: <NoteView />,
|
||||
element: <ThreadView />,
|
||||
},
|
||||
{ path: "settings", element: <SettingsView /> },
|
||||
{
|
||||
|
@ -1,111 +0,0 @@
|
||||
import { getReferences } from "../helpers/nostr/events";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import NostrRequest from "./nostr-request";
|
||||
import NostrMultiSubscription from "./nostr-multi-subscription";
|
||||
import { PersistentSubject } from "./subject";
|
||||
import { createSimpleQueryMap } from "../helpers/nostr/filter";
|
||||
|
||||
/** @deprecated */
|
||||
export default class ThreadLoader {
|
||||
loading = new PersistentSubject(false);
|
||||
focusId = new PersistentSubject<string>("");
|
||||
rootId = new PersistentSubject<string>("");
|
||||
events = new PersistentSubject<Record<string, NostrEvent>>({});
|
||||
|
||||
private relays: string[];
|
||||
private subscription: NostrMultiSubscription;
|
||||
|
||||
constructor(relays: string[], eventId: string) {
|
||||
this.relays = relays;
|
||||
|
||||
this.subscription = new NostrMultiSubscription();
|
||||
|
||||
this.subscription.onEvent.subscribe((event) => {
|
||||
this.events.next({ ...this.events.value, [event.id]: event });
|
||||
});
|
||||
|
||||
this.updateEventId(eventId);
|
||||
}
|
||||
|
||||
loadEvent() {
|
||||
this.loading.next(true);
|
||||
const request = new NostrRequest(this.relays);
|
||||
request.onEvent.subscribe((event) => {
|
||||
this.events.next({ ...this.events.value, [event.id]: event });
|
||||
|
||||
this.checkAndUpdateRoot();
|
||||
|
||||
request.complete();
|
||||
this.loading.next(false);
|
||||
});
|
||||
request.start({ ids: [this.focusId.value] });
|
||||
}
|
||||
|
||||
private checkAndUpdateRoot() {
|
||||
const event = this.events.value[this.focusId.value];
|
||||
|
||||
if (event) {
|
||||
const refs = getReferences(event);
|
||||
const rootId = refs.rootId || event.id;
|
||||
// only update the root if its different
|
||||
if (rootId !== this.rootId.value) {
|
||||
this.rootId.next(rootId);
|
||||
this.loadRoot();
|
||||
this.updateSubscription();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadRoot() {
|
||||
if (this.rootId.value) {
|
||||
const request = new NostrRequest(this.relays);
|
||||
request.onEvent.subscribe((event) => {
|
||||
this.events.next({ ...this.events.value, [event.id]: event });
|
||||
|
||||
request.complete();
|
||||
});
|
||||
request.start({ ids: [this.rootId.value] });
|
||||
}
|
||||
}
|
||||
|
||||
setRelays(relays: string[]) {
|
||||
this.relays = relays;
|
||||
this.subscription.setQueryMap(createSimpleQueryMap(this.relays, { "#e": [this.rootId.value], kinds: [1] }));
|
||||
this.loadEvent();
|
||||
}
|
||||
|
||||
private updateSubscription() {
|
||||
if (this.rootId.value) {
|
||||
this.subscription.setQueryMap(createSimpleQueryMap(this.relays, { "#e": [this.rootId.value], kinds: [1] }));
|
||||
if (this.subscription.state !== NostrMultiSubscription.OPEN) {
|
||||
this.subscription.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateEventId(eventId: string) {
|
||||
if (this.loading.value) {
|
||||
console.warn("trying to set eventId while loading");
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusId.next(eventId);
|
||||
|
||||
const event = this.events.value[eventId];
|
||||
if (!event) {
|
||||
this.loadEvent();
|
||||
} else {
|
||||
this.checkAndUpdateRoot();
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
if (!this.loading.value && this.focusId.value && this.events.value[this.focusId.value]) {
|
||||
this.loadEvent();
|
||||
}
|
||||
this.updateSubscription();
|
||||
}
|
||||
close() {
|
||||
this.subscription.close();
|
||||
}
|
||||
}
|
@ -197,9 +197,6 @@ export default class TimelineLoader {
|
||||
mapQueryMap(this.queryMap, (filter) => addQueryToFilter(filter, { limit: BLOCK_SIZE / 2 })),
|
||||
);
|
||||
|
||||
// TODO: maybe smartly prune the events based on the new filter
|
||||
this.forgetEvents();
|
||||
|
||||
this.triggerBlockLoads();
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { CompactNoteContent } from "../../compact-note-content";
|
||||
import { useNavigateInDrawer } from "../../../providers/drawer-sub-view-provider";
|
||||
import HoverLinkOverlay from "../../hover-link-overlay";
|
||||
import singleEventService from "../../../services/single-event";
|
||||
|
||||
export default function EmbeddedNote({ event, ...props }: Omit<CardProps, "children"> & { event: NostrEvent }) {
|
||||
const { showSignatureVerification } = useSubject(appSettings);
|
||||
@ -25,6 +26,7 @@ export default function EmbeddedNote({ event, ...props }: Omit<CardProps, "child
|
||||
const handleClick = useCallback<MouseEventHandler>(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
singleEventService.handleEvent(event);
|
||||
navigate(to);
|
||||
},
|
||||
[navigate, to],
|
||||
|
@ -76,7 +76,7 @@ export default function BookmarkButton({ event, ...props }: { event: NostrEvent
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu closeOnSelect={false}>
|
||||
<Menu isLazy closeOnSelect={false}>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
icon={inLists.length > 0 ? <BookmarkedIcon /> : <BookmarkIcon />}
|
||||
|
@ -43,13 +43,13 @@ import OpenInDrawerButton from "../open-in-drawer-button";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import { useBreakpointValue } from "../../providers/breakpoint-provider";
|
||||
import HoverLinkOverlay from "../hover-link-overlay";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import NoteCommunityMetadata from "./note-community-metadata";
|
||||
import useSingleEvent from "../../hooks/use-single-event";
|
||||
import { CompactNoteContent } from "../compact-note-content";
|
||||
import NoteProxyLink from "./components/note-proxy-link";
|
||||
import { NoteDetailsButton } from "./components/note-details-button";
|
||||
import EventInteractionDetailsModal from "../event-interactions-modal";
|
||||
import singleEventService from "../../services/single-event";
|
||||
|
||||
export type NoteProps = Omit<CardProps, "children"> & {
|
||||
event: NostrEvent;
|
||||
@ -97,7 +97,13 @@ export const Note = React.memo(
|
||||
data-event-id={event.id}
|
||||
{...props}
|
||||
>
|
||||
{clickable && <HoverLinkOverlay as={RouterLink} to={`/n/${getSharableEventAddress(event)}`} />}
|
||||
{clickable && (
|
||||
<HoverLinkOverlay
|
||||
as={RouterLink}
|
||||
to={`/n/${getSharableEventAddress(event)}`}
|
||||
onClick={() => singleEventService.handleEvent(event)}
|
||||
/>
|
||||
)}
|
||||
<CardHeader p="2">
|
||||
<Flex flex="1" gap="2" alignItems="center">
|
||||
<UserAvatarLink pubkey={event.pubkey} size={["xs", "sm"]} />
|
||||
@ -106,7 +112,12 @@ export const Note = React.memo(
|
||||
<Flex grow={1} />
|
||||
{showSignatureVerification && <EventVerificationIcon event={event} />}
|
||||
{!hideDrawerButton && (
|
||||
<OpenInDrawerButton to={`/n/${getSharableEventAddress(event)}`} size="sm" variant="ghost" />
|
||||
<OpenInDrawerButton
|
||||
to={`/n/${getSharableEventAddress(event)}`}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => singleEventService.handleEvent(event)}
|
||||
/>
|
||||
)}
|
||||
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
|
||||
<Timestamp timestamp={event.created_at} />
|
||||
|
@ -103,8 +103,13 @@ export function getReferences(event: NostrEvent | DraftNostrEvent) {
|
||||
const events = eTags.map((t) => t[1]);
|
||||
const contentTagRefs = getContentTagRefs(event.content, event.tags);
|
||||
|
||||
let replyId = eTags.find((t) => t[3] === "reply")?.[1];
|
||||
let rootId = eTags.find((t) => t[3] === "root")?.[1];
|
||||
const replyTag = eTags.find((t) => t[3] === "reply");
|
||||
const rootTag = eTags.find((t) => t[3] === "root");
|
||||
|
||||
let replyId = replyTag?.[1];
|
||||
let replyRelay = replyTag?.[2];
|
||||
let rootId = rootTag?.[1];
|
||||
let rootRelay = rootTag?.[2];
|
||||
|
||||
if (!rootId || !replyId) {
|
||||
// a direct reply dose not need a "reply" reference
|
||||
@ -136,7 +141,9 @@ export function getReferences(event: NostrEvent | DraftNostrEvent) {
|
||||
return {
|
||||
events,
|
||||
rootId,
|
||||
rootRelay,
|
||||
replyId,
|
||||
replyRelay,
|
||||
contentTagRefs,
|
||||
};
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export function getThreadMembers(item: ThreadItem, omit?: string) {
|
||||
return Array.from(pubkeys);
|
||||
}
|
||||
|
||||
export function linkEvents(events: NostrEvent[]) {
|
||||
export function buildThread(events: NostrEvent[]) {
|
||||
const idToChildren: Record<string, NostrEvent[]> = {};
|
||||
|
||||
const replies = new Map<string, ThreadItem>();
|
||||
|
@ -1,52 +0,0 @@
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { useUnmount } from "react-use";
|
||||
|
||||
import ThreadLoader from "../classes/thread-loader";
|
||||
import { linkEvents } from "../helpers/thread";
|
||||
import { useReadRelayUrls } from "./use-client-relays";
|
||||
import useSubject from "./use-subject";
|
||||
import useRelaysChanged from "./use-relays-changed";
|
||||
|
||||
type Options = {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
export function useThreadLoader(eventId: string, additionalRelays: string[] = [], opts?: Options) {
|
||||
const relays = useReadRelayUrls(additionalRelays);
|
||||
|
||||
const ref = useRef<ThreadLoader | null>(null);
|
||||
const loader = (ref.current = ref.current || new ThreadLoader(relays, eventId));
|
||||
|
||||
useEffect(() => {
|
||||
if (eventId !== loader.focusId.value) loader.updateEventId(eventId);
|
||||
}, [eventId]);
|
||||
|
||||
const enabled = opts?.enabled ?? true;
|
||||
useEffect(() => {
|
||||
if (enabled) loader.open();
|
||||
else loader.close();
|
||||
}, [enabled]);
|
||||
|
||||
useRelaysChanged(relays, () => {
|
||||
loader.setRelays(relays);
|
||||
});
|
||||
|
||||
useUnmount(() => {
|
||||
loader.close();
|
||||
});
|
||||
|
||||
const events = useSubject(loader.events) ?? {};
|
||||
const loading = useSubject(loader.loading);
|
||||
const rootId = useSubject(loader.rootId) ?? "";
|
||||
const focusId = useSubject(loader.focusId) ?? "";
|
||||
const thread = useMemo(() => linkEvents(Object.values(events)), [events]);
|
||||
|
||||
return {
|
||||
loader,
|
||||
events,
|
||||
thread,
|
||||
rootId,
|
||||
focusId,
|
||||
loading,
|
||||
};
|
||||
}
|
@ -14,11 +14,16 @@ type Options = {
|
||||
customSort?: (a: NostrEvent, b: NostrEvent) => number;
|
||||
};
|
||||
|
||||
export default function useTimelineLoader(key: string, relays: string[], query: NostrRequestFilter, opts?: Options) {
|
||||
export default function useTimelineLoader(
|
||||
key: string,
|
||||
relays: string[],
|
||||
query: NostrRequestFilter | undefined,
|
||||
opts?: Options,
|
||||
) {
|
||||
const timeline = useMemo(() => timelineCacheService.createTimeline(key), [key]);
|
||||
|
||||
useEffect(() => {
|
||||
timeline.setQueryMap(createSimpleQueryMap(relays, query));
|
||||
if (query) timeline.setQueryMap(createSimpleQueryMap(relays, query));
|
||||
}, [timeline, JSON.stringify(query), relays.join("|")]);
|
||||
useEffect(() => {
|
||||
timeline.setEventFilter(opts?.eventFilter);
|
||||
@ -32,7 +37,7 @@ export default function useTimelineLoader(key: string, relays: string[], query:
|
||||
timeline.events.customSort = opts?.customSort;
|
||||
}, [timeline, opts?.customSort]);
|
||||
|
||||
const enabled = opts?.enabled ?? true;
|
||||
const enabled = opts?.enabled ?? !!query;
|
||||
useEffect(() => {
|
||||
if (enabled) {
|
||||
timeline.open();
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
import { Location, RouteObject, RouterProvider, To, createMemoryRouter, useNavigate } from "react-router-dom";
|
||||
|
||||
import { ErrorBoundary } from "../components/error-boundary";
|
||||
import NoteView from "../views/note";
|
||||
import ThreadView from "../views/note";
|
||||
import { ChevronLeftIcon, ChevronRightIcon, ExternalLinkIcon } from "../components/icons";
|
||||
import { PageProviders } from ".";
|
||||
import { logger } from "../helpers/debug";
|
||||
@ -67,7 +67,7 @@ function DrawerSubView({
|
||||
const routes: RouteObject[] = [
|
||||
{
|
||||
path: "/n/:id",
|
||||
element: <NoteView />,
|
||||
element: <ThreadView />,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { RouteObject } from "react-router-dom";
|
||||
|
||||
import NoteView from "./views/note";
|
||||
import ThreadView from "./views/note";
|
||||
|
||||
export const threadRoute: RouteObject = {
|
||||
path: "/n/:id",
|
||||
element: <NoteView />,
|
||||
element: <ThreadView />,
|
||||
};
|
||||
|
@ -1,14 +1,17 @@
|
||||
import TimelineLoader from "../classes/timeline-loader";
|
||||
import { logger } from "../helpers/debug";
|
||||
|
||||
const MAX_CACHE = 20;
|
||||
const MAX_CACHE = 30;
|
||||
|
||||
class TimelineCacheService {
|
||||
private timelines = new Map<string, TimelineLoader>();
|
||||
private cacheQueue: string[] = [];
|
||||
private log = logger.extend("TimelineCacheService");
|
||||
|
||||
createTimeline(key: string) {
|
||||
let timeline = this.timelines.get(key);
|
||||
if (!timeline) {
|
||||
this.log(`Creating ${key}`);
|
||||
timeline = new TimelineLoader(key);
|
||||
this.timelines.set(key, timeline);
|
||||
}
|
||||
@ -22,6 +25,7 @@ class TimelineCacheService {
|
||||
if (!deleteKey) break;
|
||||
const deadTimeline = this.timelines.get(deleteKey);
|
||||
if (deadTimeline) {
|
||||
this.log(`Destroying ${deadTimeline.name}`);
|
||||
this.timelines.delete(deleteKey);
|
||||
deadTimeline.cleanup();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { memo, useState } from "react";
|
||||
import { memo, useRef, useState } from "react";
|
||||
import {
|
||||
Alert,
|
||||
AlertIcon,
|
||||
@ -39,6 +39,7 @@ import NoteProxyLink from "../../../components/note/components/note-proxy-link";
|
||||
import { NoteDetailsButton } from "../../../components/note/components/note-details-button";
|
||||
import EventInteractionDetailsModal from "../../../components/event-interactions-modal";
|
||||
import { getNeventCodeWithRelays } from "../../../helpers/nip19";
|
||||
import { useRegisterIntersectionEntity } from "../../../providers/intersection-observer";
|
||||
|
||||
const LEVEL_COLORS = ["green", "blue", "red", "purple", "yellow", "cyan", "pink"];
|
||||
|
||||
@ -141,6 +142,9 @@ export const ThreadPost = memo(({ post, initShowReplies, focusId, level = -1 }:
|
||||
</Flex>
|
||||
);
|
||||
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
useRegisterIntersectionEntity(ref, post.event.id);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
@ -151,6 +155,7 @@ export const ThreadPost = memo(({ post, initShowReplies, focusId, level = -1 }:
|
||||
borderWidth=".1rem .1rem .1rem .35rem"
|
||||
borderColor={focusId === post.event.id ? focusColor : undefined}
|
||||
borderLeftColor={color + "." + colorValue}
|
||||
ref={ref}
|
||||
>
|
||||
{header}
|
||||
{expanded && renderContent()}
|
||||
|
@ -1,12 +1,21 @@
|
||||
import { Button, Spinner } from "@chakra-ui/react";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { Button, Code, Heading, Spinner } from "@chakra-ui/react";
|
||||
import { Kind, nip19 } from "nostr-tools";
|
||||
import { useParams, Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import Note from "../../components/note";
|
||||
import { getSharableEventAddress, isHexKey } from "../../helpers/nip19";
|
||||
import { useThreadLoader } from "../../hooks/use-thread-loader";
|
||||
import { ThreadPost } from "./components/thread-post";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import useSingleEvent from "../../hooks/use-single-event";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||
import { getReferences } from "../../helpers/nostr/events";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { ThreadItem, buildThread } from "../../helpers/thread";
|
||||
import IntersectionObserverProvider from "../../providers/intersection-observer";
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import singleEventService from "../../services/single-event";
|
||||
|
||||
function useNotePointer() {
|
||||
const { id } = useParams() as { id: string };
|
||||
@ -23,56 +32,101 @@ function useNotePointer() {
|
||||
}
|
||||
}
|
||||
|
||||
export default function NoteView() {
|
||||
const pointer = useNotePointer();
|
||||
|
||||
const { thread, events, rootId, focusId, loading } = useThreadLoader(pointer.id, pointer.relays, {
|
||||
enabled: !!pointer.id,
|
||||
});
|
||||
if (loading) return <Spinner />;
|
||||
|
||||
let pageContent = <span>Missing Event</span>;
|
||||
|
||||
function ThreadPage({ thread, rootId, focusId }: { thread: Map<string, ThreadItem>; rootId: string; focusId: string }) {
|
||||
const isRoot = rootId === focusId;
|
||||
|
||||
const focusedPost = thread.get(focusId);
|
||||
const rootPost = thread.get(rootId);
|
||||
if (isRoot && rootPost) {
|
||||
pageContent = <ThreadPost post={rootPost} initShowReplies focusId={focusId} />;
|
||||
return <ThreadPost post={rootPost} initShowReplies focusId={focusId} />;
|
||||
}
|
||||
|
||||
const post = thread.get(focusId);
|
||||
if (post) {
|
||||
const parentPosts = [];
|
||||
if (post.reply) {
|
||||
let p = post;
|
||||
while (p.reply) {
|
||||
parentPosts.unshift(p.reply);
|
||||
p = p.reply;
|
||||
}
|
||||
if (!focusedPost) return null;
|
||||
|
||||
const parentPosts = [];
|
||||
if (focusedPost.reply) {
|
||||
let p = focusedPost;
|
||||
while (p.reply) {
|
||||
parentPosts.unshift(p.reply);
|
||||
p = p.reply;
|
||||
}
|
||||
|
||||
pageContent = (
|
||||
<>
|
||||
{parentPosts.length > 1 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
h="4rem"
|
||||
w="full"
|
||||
as={RouterLink}
|
||||
to={`/n/${getSharableEventAddress(parentPosts[0].event)}`}
|
||||
>
|
||||
View full thread ({parentPosts.length - 1})
|
||||
</Button>
|
||||
)}
|
||||
{post.reply && (
|
||||
<Note key={post.reply.event.id + "-rely"} event={post.reply.event} hideDrawerButton showReplyLine={false} />
|
||||
)}
|
||||
<ThreadPost key={post.event.id} post={post} initShowReplies focusId={focusId} />
|
||||
</>
|
||||
);
|
||||
} else if (events[focusId]) {
|
||||
pageContent = <Note event={events[focusId]} variant="filled" hideDrawerButton />;
|
||||
}
|
||||
|
||||
return <VerticalPageLayout px={{ base: 0, md: "2" }}>{pageContent}</VerticalPageLayout>;
|
||||
return (
|
||||
<>
|
||||
{parentPosts.length > 1 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
h="4rem"
|
||||
w="full"
|
||||
as={RouterLink}
|
||||
to={`/n/${getSharableEventAddress(parentPosts[0].event)}`}
|
||||
>
|
||||
View full thread ({parentPosts.length - 1})
|
||||
</Button>
|
||||
)}
|
||||
{focusedPost.reply && (
|
||||
<Note
|
||||
key={focusedPost.reply.event.id + "-rely"}
|
||||
event={focusedPost.reply.event}
|
||||
hideDrawerButton
|
||||
showReplyLine={false}
|
||||
/>
|
||||
)}
|
||||
<ThreadPost key={focusedPost.event.id} post={focusedPost} initShowReplies focusId={focusId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ThreadView() {
|
||||
const pointer = useNotePointer();
|
||||
const readRelays = useReadRelayUrls(pointer.relays);
|
||||
|
||||
// load the event in focus
|
||||
const focused = useSingleEvent(pointer.id, pointer.relays);
|
||||
const refs = focused && getReferences(focused);
|
||||
const rootId = refs ? refs.rootId || pointer.id : undefined;
|
||||
|
||||
const timelineId = `${rootId}-replies`;
|
||||
const timeline = useTimelineLoader(
|
||||
timelineId,
|
||||
readRelays,
|
||||
rootId
|
||||
? {
|
||||
"#e": [rootId],
|
||||
kinds: [Kind.Text],
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
|
||||
const events = useSubject(timeline.timeline);
|
||||
|
||||
// mirror all events to single event cache
|
||||
useEffect(() => {
|
||||
for (const e of events) singleEventService.handleEvent(e);
|
||||
}, [events]);
|
||||
|
||||
const rootEvent = useSingleEvent(rootId, refs?.rootRelay ? [refs.rootRelay] : []);
|
||||
const thread = useMemo(() => {
|
||||
return rootEvent ? buildThread([...events, rootEvent]) : buildThread(events);
|
||||
}, [events, rootEvent]);
|
||||
|
||||
const callback = useTimelineCurserIntersectionCallback(timeline);
|
||||
|
||||
return (
|
||||
<VerticalPageLayout px={{ base: 0, md: "2" }}>
|
||||
{!focused && (
|
||||
<Heading mx="auto" my="4">
|
||||
<Spinner /> Loading note
|
||||
</Heading>
|
||||
)}
|
||||
{/* <Code as="pre">
|
||||
{JSON.stringify({ pointer, rootId, focused: focused?.id, refs, timelineId, events: events.length }, null, 2)}
|
||||
</Code> */}
|
||||
<IntersectionObserverProvider callback={callback}>
|
||||
{focused && rootId ? <ThreadPage thread={thread} rootId={rootId} focusId={focused.id} /> : <Spinner />}
|
||||
</IntersectionObserverProvider>
|
||||
</VerticalPageLayout>
|
||||
);
|
||||
}
|
||||
|
@ -86,7 +86,10 @@ function TorrentsPage() {
|
||||
() => (tags.length > 0 ? { ...filter, kinds: [TORRENT_KIND], "#t": tags } : { ...filter, kinds: [TORRENT_KIND] }),
|
||||
[tags.join(","), filter],
|
||||
);
|
||||
const timeline = useTimelineLoader(`${listId}-torrents`, relays, query, { eventFilter, enabled: !!filter });
|
||||
const timeline = useTimelineLoader(`${listId || "global"}-torrents`, relays, query, {
|
||||
eventFilter,
|
||||
enabled: !!filter,
|
||||
});
|
||||
|
||||
const torrents = useSubject(timeline.timeline);
|
||||
const callback = useTimelineCurserIntersectionCallback(timeline);
|
||||
|
Loading…
x
Reference in New Issue
Block a user