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

View File

@@ -2,7 +2,7 @@ import RelayProvider from '@components/relaysProvider';
import { relaysAtom } from '@stores/relays';
import { Provider, useAtom } from 'jotai';
import { Provider, useAtomValue } from 'jotai';
import type { NextPage } from 'next';
import type { AppProps } from 'next/app';
import { ReactElement, ReactNode } from 'react';
@@ -21,7 +21,7 @@ type AppPropsWithLayout = AppProps & {
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page);
const [relays] = useAtom(relaysAtom);
const relays = useAtomValue(relaysAtom);
return (
<Provider>

View File

@@ -14,7 +14,7 @@ export default function Page() {
getAccounts()
.then((res: any) => {
if (res.length > 0) {
router.push('/newsfeed/circle');
router.push('/newsfeed/following');
} else {
router.push('/onboarding');
}

View File

@@ -6,9 +6,11 @@ import { ContentExtend } from '@components/note/content/extend';
import FormComment from '@components/note/form/comment';
import { RelayContext } from '@components/relaysProvider';
import { relaysAtom } from '@stores/relays';
import { getNoteByID } from '@utils/storage';
import useLocalStorage from '@rehooks/local-storage';
import { useAtomValue } from 'jotai';
import { useRouter } from 'next/router';
import {
JSXElementConstructor,
@@ -26,7 +28,7 @@ export default function Page() {
const router = useRouter();
const id = router.query.id;
const [relays]: any = useLocalStorage('relays');
const relays: any = useAtomValue(relaysAtom);
const [rootEvent, setRootEvent] = useState(null);
const [comments, setComments] = useState([]);

View File

@@ -1,43 +1,10 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
import { Note } from '@components/note';
import { notesAtom } from '@stores/note';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useAtom } from 'jotai';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useRef } from 'react';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
export default function Page() {
const [data]: any = useAtom(notesAtom);
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
count: data.length,
overscan: 5,
estimateSize: () => 600,
getScrollElement: () => parentRef.current,
getItemKey: (index) => data[index].id,
});
const items = virtualizer.getVirtualItems();
return (
<div ref={parentRef} className="scrollbar-hide h-full w-full overflow-y-auto" style={{ contain: 'strict' }}>
{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>
);
return <></>;
}
Page.getLayout = function getLayout(

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>
);
}

View File

@@ -7,7 +7,7 @@ import { relaysAtom } from '@stores/relays';
import { createAccount } from '@utils/storage';
import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
@@ -22,7 +22,7 @@ export default function Page() {
const router = useRouter();
const pool: any = useContext(RelayContext);
const [relays] = useAtom(relaysAtom);
const relays = useAtomValue(relaysAtom);
const [type, setType] = useState('password');
const [loading, setLoading] = useState(false);

View File

@@ -9,7 +9,7 @@ import { createFollows } from '@utils/storage';
import { CheckCircledIcon } from '@radix-ui/react-icons';
import { createClient } from '@supabase/supabase-js';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai';
import { useRouter } from 'next/router';
import { getEventHash, signEvent } from 'nostr-tools';
import {
@@ -69,7 +69,7 @@ export default function Page() {
const router = useRouter();
const { id, privkey }: any = router.query;
const [relays] = useAtom(relaysAtom);
const relays = useAtomValue(relaysAtom);
const [loading, setLoading] = useState(false);
const [list, setList]: any = useState(initialList);
const [follows, setFollows] = useState([]);

View File

@@ -5,11 +5,11 @@ import { RelayContext } from '@components/relaysProvider';
import { relaysAtom } from '@stores/relays';
import { createAccount, createFollows } from '@utils/storage';
import { tagsToArray } from '@utils/tags';
import { tagsToArray } from '@utils/transform';
import { truncate } from '@utils/truncate';
import destr from 'destr';
import { useAtom } from 'jotai';
import { useAtomValue } from 'jotai';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { getPublicKey, nip19 } from 'nostr-tools';
@@ -30,7 +30,7 @@ export default function Page() {
const privkey: any = router.query.privkey;
const pubkey = getPublicKey(privkey);
const [relays] = useAtom(relaysAtom);
const relays = useAtomValue(relaysAtom);
const [profile, setProfile] = useState(null);
useEffect(() => {