This commit is contained in:
Ren Amamiya
2023-03-24 14:59:01 +07:00
parent e2fa8cbe03
commit c1f06f8b28
29 changed files with 179 additions and 229 deletions
+45 -113
View File
@@ -1,132 +1,64 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
import { DatabaseContext } from '@components/contexts/database';
import { Note } from '@components/note';
import FormBasic from '@components/note/form/basic';
import { Placeholder } from '@components/note/placeholder';
import { hasNewerNoteAtom } from '@stores/note';
import { dateToUnix } from '@utils/getDate';
import { hasNewerNoteAtom, notesAtom } from '@stores/note';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useAtom } from 'jotai';
import { Key, useCallback, useState } from 'react';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useEffect, useRef } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useRef } from 'react';
export default function Page() {
const { db }: any = useContext(DatabaseContext);
const [data, setData] = useState([]);
const [reload, setReload] = useState(false);
const [data]: any = useAtom(notesAtom);
const [hasNewerNote, setHasNewerNote] = useAtom(hasNewerNoteAtom);
const now = useRef(new Date());
const limit = useRef(30);
const offset = useRef(0);
const parentRef = useRef(null);
const loadMore = useCallback(async () => {
offset.current += limit.current;
// next query
const result = await db.select(
`SELECT * FROM
cache_notes
WHERE created_at <= ${dateToUnix(now.current)} AND is_root = 0
ORDER BY created_at DESC
LIMIT ${limit.current} OFFSET ${offset.current}`
);
setData((data) => [...data, ...result]);
}, [db]);
const virtualizer = useVirtualizer({
count: data.length,
overscan: 5,
estimateSize: () => 600,
getScrollElement: () => parentRef.current,
getItemKey: (index) => data[index].id,
});
const items = virtualizer.getVirtualItems();
const loadNewest = useCallback(async () => {
const result = await db.select(
`SELECT * FROM
cache_notes
WHERE created_at > ${dateToUnix(now.current)} AND is_root = 0
ORDER BY created_at DESC
LIMIT ${limit.current}`
);
// update data
setData((data) => [...result, ...data]);
// update hasNewerNote to false to disable button
setHasNewerNote(false);
// update current time, fixed duplicate note
now.current = new Date();
}, [db, setHasNewerNote]);
const ItemContent = useCallback(
(index: Key) => {
const event = data[index];
return <Note event={event} />;
},
[data]
);
const computeItemKey = useCallback(
(index: Key) => {
return data[index].id + data[index].created_at;
},
[data]
);
useEffect(() => {
const getData = async () => {
const result = await db.select(
`SELECT * FROM cache_notes WHERE is_root = 0 ORDER BY created_at DESC LIMIT ${limit.current}`
);
if (result.length > 0) {
setData(result);
} else {
setReload(true);
}
};
if (reload === false) {
getData().catch(console.error);
} else {
// auto reload after 8s
const timer = setTimeout(() => {
getData().catch(console.error);
}, 8000);
return () => clearTimeout(timer);
}
}, [db, reload]);
const loadNewest = () => {
console.log('load');
};
return (
<div className="relative h-full w-full">
{hasNewerNote && (
<div className="absolute top-8 left-1/2 z-50 -translate-x-1/2 transform">
<button
onClick={() => loadNewest()}
className="inline-flex h-8 transform items-center justify-center gap-1 rounded-full bg-fuchsia-500 px-3 text-sm shadow-lg shadow-fuchsia-900/50 active:translate-y-1"
>
<span className="text-white drop-shadow">Load newest</span>
</button>
</div>
)}
<Virtuoso
data={data}
itemContent={ItemContent}
components={{
Header: () => <FormBasic />,
EmptyPlaceholder: () => <Placeholder />,
ScrollSeekPlaceholder: () => <Placeholder />,
}}
computeItemKey={computeItemKey}
scrollSeekConfiguration={{
enter: (velocity) => Math.abs(velocity) > 800,
exit: (velocity) => Math.abs(velocity) < 500,
}}
endReached={loadMore}
overscan={800}
increaseViewportBy={1000}
className="scrollbar-hide relative h-full w-full"
style={{
contain: 'strict',
}}
/>
<div ref={parentRef} className="scrollbar-hide h-full w-full overflow-y-auto" style={{ contain: 'strict' }}>
<div>
{hasNewerNote && (
<div className="absolute top-8 left-1/2 z-50 -translate-x-1/2 transform">
<button
onClick={() => loadNewest()}
className="inline-flex h-8 transform items-center justify-center gap-1 rounded-full bg-fuchsia-500 px-3 text-sm shadow-lg shadow-fuchsia-900/50 active:translate-y-1"
>
<span className="text-white drop-shadow">Load newest</span>
</button>
</div>
)}
</div>
<div>
<FormBasic />
</div>
<div>
{items.length > 0 && (
<div className="relative w-full" style={{ height: virtualizer.getTotalSize() }}>
<div className="absolute top-0 left-0 w-full" style={{ transform: `translateY(${items[0].start}px)` }}>
{items.map((virtualRow) => (
<div key={virtualRow.key} data-index={virtualRow.index} ref={virtualizer.measureElement}>
<Note event={data[virtualRow.index]} />
</div>
))}
</div>
</div>
)}
</div>
</div>
);
}