From 990bf985719e1666987785a92bea21a4622276cf Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Fri, 31 Mar 2023 11:36:23 +0700 Subject: [PATCH] refactor newsfeed to use react-virtuoso --- src/components/note/base.tsx | 2 +- src/pages/newsfeed/following.tsx | 121 +++++++++++++++++++++++-------- src/stores/note.tsx | 17 ----- src/utils/storage.tsx | 14 +++- 4 files changed, 104 insertions(+), 50 deletions(-) diff --git a/src/components/note/base.tsx b/src/components/note/base.tsx index 53f035eb..15bdc045 100644 --- a/src/components/note/base.tsx +++ b/src/components/note/base.tsx @@ -80,7 +80,7 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) { return (
openThread(e)} - className="relative z-10 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-5 px-3 hover:bg-black/20" + className="relative z-10 m-0 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-5 px-3 hover:bg-black/20" > <>{getParent}
diff --git a/src/pages/newsfeed/following.tsx b/src/pages/newsfeed/following.tsx index d9dbc998..39ac11cf 100644 --- a/src/pages/newsfeed/following.tsx +++ b/src/pages/newsfeed/following.tsx @@ -5,48 +5,109 @@ import FormBase from '@components/form/base'; import { NoteBase } from '@components/note/base'; import { Placeholder } from '@components/note/placeholder'; -import { notesAtom } from '@stores/note'; +import { hasNewerNoteAtom } from '@stores/note'; -import { useVirtualizer } from '@tanstack/react-virtual'; +import { dateToUnix } from '@utils/getDate'; +import { getLatestNotes, getNotes } from '@utils/storage'; + +import { ArrowUpIcon } from '@radix-ui/react-icons'; import { useAtom } from 'jotai'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, Suspense, useRef } from 'react'; +import { + JSXElementConstructor, + ReactElement, + ReactFragment, + ReactPortal, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import { Virtuoso } from 'react-virtuoso'; export default function Page() { - const [data]: any = useAtom(notesAtom); - const parentRef = useRef(null); + const [data, setData] = useState([]); + const [hasNewerNote, setHasNewerNote] = useAtom(hasNewerNoteAtom); - const virtualizer = useVirtualizer({ - count: data.length, - estimateSize: () => 500, - getScrollElement: () => parentRef.current, - getItemKey: (index) => data[index].id, - }); - const items = virtualizer.getVirtualItems(); + const virtuosoRef = useRef(null); + const now = useRef(new Date()); + const limit = useRef(20); + const offset = useRef(0); + + const itemContent: any = useCallback( + (index: string | number) => { + return ; + }, + [data] + ); + + const computeItemKey = useCallback( + (index: string | number) => { + return data[index].id; + }, + [data] + ); + + const initialData = useCallback(async () => { + const result: any = await getNotes(dateToUnix(now.current), limit.current, offset.current); + setData((data) => [...data, ...result]); + }, []); + + const loadMore = useCallback(async () => { + offset.current += limit.current; + // next query + const result: any = await getNotes(dateToUnix(now.current), limit.current, offset.current); + setData((data) => [...data, ...result]); + }, []); + + const loadLatest = useCallback(async () => { + offset.current += limit.current; + // next query + const result: any = await getLatestNotes(dateToUnix(now.current)); + // update data + setData((data) => [...result, ...data]); + // hide newer trigger + setHasNewerNote(false); + // scroll to top + virtuosoRef.current.scrollToIndex({ index: 0 }); + }, [setHasNewerNote]); + + useEffect(() => { + initialData().catch(console.error); + }, [initialData]); return ( -
-
- -
- }> -
- {items.length > 0 && ( -
-
- {items.map((virtualRow) => ( -
- -
- ))} -
-
- )} +
+ {hasNewerNote && ( +
+
- + )} +
); } +const COMPONENTS = { + Header: () => , + EmptyPlaceholder: () => , + ScrollSeekPlaceholder: () => , +}; + Page.getLayout = function getLayout( page: | string diff --git a/src/stores/note.tsx b/src/stores/note.tsx index 443ebc38..920ab931 100644 --- a/src/stores/note.tsx +++ b/src/stores/note.tsx @@ -1,24 +1,7 @@ -import { isSSR } from '@utils/ssr'; -import { getAllNotes } from '@utils/storage'; - import { atom } from 'jotai'; -import { atomsWithQuery } from 'jotai-tanstack-query'; import { atomWithReset } from 'jotai/utils'; // note content export const noteContentAtom = atomWithReset(''); // notify user that connector has receive newer note export const hasNewerNoteAtom = atom(false); -// query notes from database -export const [notesAtom] = atomsWithQuery(() => ({ - queryKey: ['notes'], - queryFn: async ({ queryKey: [] }) => { - const res = isSSR ? [] : await getAllNotes(); - return res; - }, - refetchInterval: 1000000, - refetchOnReconnect: true, - refetchOnWindowFocus: true, - refetchOnMount: true, - keepPreviousData: false, -})); diff --git a/src/utils/storage.tsx b/src/utils/storage.tsx index 708aa4a1..e9a5a812 100644 --- a/src/utils/storage.tsx +++ b/src/utils/storage.tsx @@ -89,9 +89,19 @@ export async function getCacheProfile(id) { } // get all notes -export async function getAllNotes() { +export async function getNotes(time, limit, offset) { const db = await connect(); - return await db.select(`SELECT * FROM cache_notes GROUP BY parent_id ORDER BY created_at DESC LIMIT 500`); + return await db.select( + `SELECT * FROM cache_notes WHERE created_at <= "${time}" GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}"` + ); +} + +// get all latest notes +export async function getLatestNotes(time) { + const db = await connect(); + return await db.select( + `SELECT * FROM cache_notes WHERE created_at > "${time}" GROUP BY parent_id ORDER BY created_at DESC` + ); } // get note by id