diff --git a/src/app/inital-data/pages/index.page.tsx b/src/app/inital-data/pages/index.page.tsx index f4f83831..8f74db3d 100644 --- a/src/app/inital-data/pages/index.page.tsx +++ b/src/app/inital-data/pages/index.page.tsx @@ -20,6 +20,15 @@ import { getParentID, nip02ToArray } from "@utils/transform"; import { useContext, useEffect, useRef } from "react"; import { navigate } from "vite-plugin-ssr/client/router"; +function isJSON(str: string) { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return true; +} + export function Page() { const pool: any = useContext(RelayContext); const now = useRef(new Date()); @@ -157,19 +166,23 @@ export function Page() { ); break; // long post - case 30023: + case 30023: { // insert event to local database - createNote( - event.id, - account.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - "", - ); + const verifyMetadata = isJSON(event.tags); + if (verifyMetadata) { + createNote( + event.id, + account.id, + event.pubkey, + event.kind, + event.tags, + event.content, + event.created_at, + "", + ); + } break; + } default: break; } diff --git a/src/app/threads/_default.page.tsx b/src/app/threads/_default.page.tsx new file mode 100644 index 00000000..a2814770 --- /dev/null +++ b/src/app/threads/_default.page.tsx @@ -0,0 +1 @@ +export { LayoutNewsfeed as Layout } from "./layout"; diff --git a/src/app/threads/components/author.tsx b/src/app/threads/components/author.tsx new file mode 100644 index 00000000..f1e351ff --- /dev/null +++ b/src/app/threads/components/author.tsx @@ -0,0 +1,11 @@ +export function ThreadAuthor({ + pubkey, + time, +}: { pubkey: string; time: number }) { + return ( +
+

{pubkey}

+ {time} +
+ ); +} diff --git a/src/app/threads/components/base.tsx b/src/app/threads/components/base.tsx new file mode 100644 index 00000000..c8308bc9 --- /dev/null +++ b/src/app/threads/components/base.tsx @@ -0,0 +1,21 @@ +import { ThreadAuthor } from "@app/threads/components/author"; + +export function ThreadBase({ event }: { event: any }) { + const metadata = JSON.parse(event.metadata); + const title = metadata.find((i: any) => i[0] === "title")[1]; + const summary = metadata.find((i: any) => i[0] === "summary")[1] || ""; + + return ( +
+
+
+

{title}

+

{summary}

+
+
+ +
+
+
+ ); +} diff --git a/src/app/threads/layout.tsx b/src/app/threads/layout.tsx new file mode 100644 index 00000000..42e6af95 --- /dev/null +++ b/src/app/threads/layout.tsx @@ -0,0 +1,31 @@ +import AppHeader from "@shared/appHeader"; +import MultiAccounts from "@shared/multiAccounts"; +import Navigation from "@shared/navigation"; + +export function LayoutNewsfeed({ children }: { children: React.ReactNode }) { + return ( +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ {children} +
+
+
+
+
+ ); +} diff --git a/src/app/threads/pages/index.page.tsx b/src/app/threads/pages/index.page.tsx index 4e429c0d..bbabd391 100644 --- a/src/app/threads/pages/index.page.tsx +++ b/src/app/threads/pages/index.page.tsx @@ -1,7 +1,113 @@ +import { NoteSkeleton } from "@app/note/components/skeleton"; +import { ThreadBase } from "@app/threads/components/base"; +import { useInfiniteQuery } from "@tanstack/react-query"; +import { useVirtualizer } from "@tanstack/react-virtual"; +import { getLongNotes } from "@utils/storage"; +import { useEffect, useRef } from "react"; + +const ITEM_PER_PAGE = 10; +const TIME = Math.floor(Date.now() / 1000); + export function Page() { + const { + status, + error, + data, + fetchNextPage, + hasNextPage, + isFetching, + isFetchingNextPage, + }: any = useInfiniteQuery({ + queryKey: ["threads"], + queryFn: async ({ pageParam = 0 }) => { + return await getLongNotes(TIME, ITEM_PER_PAGE, pageParam); + }, + getNextPageParam: (lastPage) => lastPage.nextCursor, + }); + + const allRows = data ? data.pages.flatMap((d: { data: any }) => d.data) : []; + const parentRef = useRef(); + + const rowVirtualizer = useVirtualizer({ + count: hasNextPage ? allRows.length + 1 : allRows.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 500, + overscan: 2, + }); + + const itemsVirtualizer = rowVirtualizer.getVirtualItems(); + + useEffect(() => { + const [lastItem] = [...rowVirtualizer.getVirtualItems()].reverse(); + + if (!lastItem) { + return; + } + + if ( + lastItem.index >= allRows.length - 1 && + hasNextPage && + !isFetchingNextPage + ) { + fetchNextPage(); + } + }, [fetchNextPage, allRows.length, rowVirtualizer.getVirtualItems()]); + return ( -
-

MySpace

+
+ {status === "loading" ? ( +
+
+ +
+
+ ) : status === "error" ? ( +
{error.message}
+ ) : ( +
+
+ {rowVirtualizer.getVirtualItems().map((virtualRow) => { + const note = allRows[virtualRow.index]; + if (note) { + return ( +
+ +
+ ); + } + })} +
+
+ )} +
+ {isFetching && !isFetchingNextPage ? ( +
+
+ +
+
+ ) : null} +
); } diff --git a/src/app/today/pages/index.page.tsx b/src/app/today/pages/index.page.tsx index 52b95f43..37de0cf6 100644 --- a/src/app/today/pages/index.page.tsx +++ b/src/app/today/pages/index.page.tsx @@ -55,7 +55,6 @@ export function Page() { ) { fetchNextPage(); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [fetchNextPage, allRows.length, rowVirtualizer.getVirtualItems()]); return ( diff --git a/src/shared/eventCollector.tsx b/src/shared/eventCollector.tsx index 103f1066..c7a38056 100644 --- a/src/shared/eventCollector.tsx +++ b/src/shared/eventCollector.tsx @@ -14,6 +14,15 @@ import { useSetAtom } from "jotai"; import { useContext, useRef } from "react"; import useSWRSubscription from "swr/subscription"; +function isJSON(str: string) { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return true; +} + export default function EventCollector() { const pool: any = useContext(RelayContext); @@ -97,19 +106,23 @@ export default function EventCollector() { ); break; // long post - case 30023: - // insert event to local database - createNote( - event.id, - account.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - "", - ); + case 30023: { + const verifyMetadata = isJSON(event.tags); + if (verifyMetadata) { + // insert event to local database + createNote( + event.id, + account.id, + event.pubkey, + event.kind, + event.tags, + event.content, + event.created_at, + "", + ); + } break; + } default: break; } diff --git a/src/utils/storage.tsx b/src/utils/storage.tsx index ecff58dc..5f3e7161 100644 --- a/src/utils/storage.tsx +++ b/src/utils/storage.tsx @@ -132,7 +132,7 @@ export async function getLongNotes( const notes: any = { data: null, nextCursor: 0 }; const query: any = await db.select( - `SELECT * FROM notes WHERE created_at <= "${time}" AND kind = 30023 GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`, + `SELECT * FROM notes WHERE created_at <= "${time}" AND kind = 30023 ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`, ); notes["data"] = query;