fix: get replies function (#213)

This commit is contained in:
雨宮蓮 2024-06-19 21:02:33 +07:00 committed by GitHub
parent 6c26f8967b
commit f8280ec8ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 55 deletions

View File

@ -1,57 +1,48 @@
import { Note } from "@/components/note"; import { Note } from "@/components/note";
import { type LumeEvent, NostrQuery, useEvent } from "@lume/system"; import { type LumeEvent, NostrQuery } from "@lume/system";
import { Box, Container, Spinner } from "@lume/ui"; import { Box, Container, Spinner } from "@lume/ui";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { useEffect, useState } from "react";
import { WindowVirtualizer } from "virtua"; import { WindowVirtualizer } from "virtua";
import { ReplyList } from "./-components/replyList"; import { Reply } from "./-components/reply";
export const Route = createFileRoute("/events/$eventId")({ export const Route = createFileRoute("/events/$eventId")({
beforeLoad: async () => { beforeLoad: async () => {
const settings = await NostrQuery.getUserSettings(); const settings = await NostrQuery.getUserSettings();
return { settings }; return { settings };
}, },
loader: async ({ params }) => {
const event = await NostrQuery.getEvent(params.eventId);
return event;
},
component: Screen, component: Screen,
}); });
function Screen() { function Screen() {
const { eventId } = Route.useParams(); const event = Route.useLoaderData();
const { isLoading, isError, data } = useEvent(eventId);
if (isLoading) { const [reload, setReload] = useState(false);
return ( const [replies, setReplies] = useState<LumeEvent[]>(null);
<div className="flex items-center justify-center w-full h-full">
<Spinner className="size-5" /> useEffect(() => {
</div> let mounted = true;
);
if (event) {
event.getAllReplies().then((data) => {
if (mounted) setReplies(data);
});
} }
if (isError) { return () => {
<div className="flex items-center justify-center w-full h-full"> mounted = false;
<p>Not found.</p> };
</div>; }, [event]);
}
return ( return (
<Container withDrag> <Container withDrag>
<Box className="scrollbar-none"> <Box className="scrollbar-none">
<WindowVirtualizer> <WindowVirtualizer>
<MainNote data={data} /> <Note.Provider event={event}>
{data ? (
<ReplyList eventId={eventId} />
) : (
<div className="flex items-center justify-center w-full h-full">
<Spinner className="size-5" />
</div>
)}
</WindowVirtualizer>
</Box>
</Container>
);
}
function MainNote({ data }: { data: LumeEvent }) {
return (
<Note.Provider event={data}>
<Note.Root> <Note.Root>
<div className="flex items-center justify-between px-3 h-14"> <div className="flex items-center justify-between px-3 h-14">
<Note.User /> <Note.User />
@ -65,5 +56,27 @@ function MainNote({ data }: { data: LumeEvent }) {
</div> </div>
</Note.Root> </Note.Root>
</Note.Provider> </Note.Provider>
<div className="flex flex-col">
<div className="flex items-center px-3 text-sm font-semibold border-t h-11 text-neutral-700 dark:text-neutral-300 border-neutral-100 dark:border-neutral-900">
Replies ({replies?.length ?? 0})
</div>
{!replies ? (
<Spinner />
) : !replies.length ? (
<div className="flex items-center justify-center w-full">
<div className="flex flex-col items-center justify-center gap-2 py-6">
<h3 className="text-3xl">👋</h3>
<p className="leading-none text-neutral-600 dark:text-neutral-400">
Be the first to Reply!
</p>
</div>
</div>
) : (
replies.map((event) => <Reply key={event.id} event={event} />)
)}
</div>
</WindowVirtualizer>
</Box>
</Container>
); );
} }

View File

@ -1,18 +1,18 @@
import type { EventWithReplies } from "@lume/types"; import { Note } from "@/components/note";
import type { LumeEvent } from "@lume/system";
import { cn } from "@lume/utils"; import { cn } from "@lume/utils";
import { SubReply } from "./subReply"; import { SubReply } from "./subReply";
import { Note } from "@/components/note";
export function Reply({ event }: { event: EventWithReplies }) { export function Reply({ event }: { event: LumeEvent }) {
return ( return (
<Note.Provider event={event}> <Note.Provider event={event}>
<Note.Root className="border-t border-neutral-100 dark:border-neutral-900"> <Note.Root className="border-t border-neutral-100 dark:border-neutral-900">
<div className="px-3 h-14 flex items-center justify-between"> <div className="flex items-center justify-between px-3 h-14">
<Note.User /> <Note.User />
<Note.Menu /> <Note.Menu />
</div> </div>
<Note.ContentLarge className="px-3" /> <Note.ContentLarge className="px-3" />
<div className="mt-3 flex items-center gap-4 px-3 h-14"> <div className="flex items-center gap-4 px-3 mt-3 h-14">
<Note.Reply /> <Note.Reply />
<Note.Repost /> <Note.Repost />
<Note.Zap /> <Note.Zap />

View File

@ -17,6 +17,7 @@ export class LumeEvent {
public sig: string; public sig: string;
public meta: Meta; public meta: Meta;
public relay?: string; public relay?: string;
public replies?: LumeEvent[];
#raw: NostrEvent; #raw: NostrEvent;
constructor(event: NostrEvent) { constructor(event: NostrEvent) {
@ -94,20 +95,22 @@ export class LumeEvent {
return { id, relayHint }; return { id, relayHint };
} }
public async getReplies(id: string) { public async getAllReplies() {
const query = await commands.getReplies(id); const query = await commands.getReplies(this.id);
if (query.status === "ok") { if (query.status === "ok") {
const events = query.data.map((item) => { const events = query.data.map((item) => {
const raw = JSON.parse(item.raw) as EventWithReplies; const nostrEvent: NostrEvent = JSON.parse(item.raw);
if (item.parsed) { if (item.parsed) {
raw.meta = item.parsed; nostrEvent.meta = item.parsed;
} else { } else {
raw.meta = null; nostrEvent.meta = null;
} }
return raw; const lumeEvent = new LumeEvent(nostrEvent);
return lumeEvent;
}); });
if (events.length > 0) { if (events.length > 0) {
@ -115,7 +118,7 @@ export class LumeEvent {
for (const event of events) { for (const event of events) {
const tags = event.tags.filter( const tags = event.tags.filter(
(el) => el[0] === "e" && el[1] !== id && el[3] !== "mention", (el) => el[0] === "e" && el[1] !== this.id && el[3] !== "mention",
); );
if (tags.length > 0) { if (tags.length > 0) {
@ -141,6 +144,9 @@ export class LumeEvent {
} }
return events; return events;
} else {
console.error(query.error);
return [];
} }
} }

View File

@ -10,7 +10,7 @@ import { LumeEvent } from "./event";
export class NostrQuery { export class NostrQuery {
static #toLumeEvents(richEvents: RichEvent[]) { static #toLumeEvents(richEvents: RichEvent[]) {
const events = richEvents.map((item) => { const events = richEvents.map((item) => {
const nostrEvent = JSON.parse(item.raw) as NostrEvent; const nostrEvent: NostrEvent = JSON.parse(item.raw);
if (item.parsed) { if (item.parsed) {
nostrEvent.meta = item.parsed; nostrEvent.meta = item.parsed;