From f1647fd85787a029d1c23c7d25a156e80eaabc4b Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Sat, 25 Mar 2023 15:26:32 +0700 Subject: [PATCH] refactor newsfeed and note --- next.config.js | 3 + .../20230226004139_create_tables.sql | 4 +- src/components/note/base.tsx | 105 ++++++++++++ src/components/note/content/extend.tsx | 68 -------- src/components/note/content/index.tsx | 65 -------- src/components/note/content/preview.tsx | 43 ----- src/components/note/extend.tsx | 78 +++++++++ src/components/note/index.tsx | 48 ------ src/components/note/mention.tsx | 84 ---------- .../comments.tsx => meta/comment.tsx} | 6 +- .../{counter/likes.tsx => meta/reaction.tsx} | 2 +- .../note/{content => }/metadata.tsx | 8 +- src/components/note/parent.tsx | 155 ++++++++++++++++++ src/components/note/preview/image.tsx | 29 ++-- src/components/note/preview/video.tsx | 6 +- src/components/note/repost.tsx | 121 ++++++++++++++ src/components/note/root.tsx | 84 ---------- src/pages/newsfeed/[id].tsx | 16 +- src/pages/newsfeed/following.tsx | 4 +- src/stores/account.tsx | 4 +- src/stores/relays.tsx | 4 +- src/utils/storage.tsx | 16 +- src/utils/transform.tsx | 21 +++ 23 files changed, 530 insertions(+), 444 deletions(-) create mode 100644 src/components/note/base.tsx delete mode 100644 src/components/note/content/extend.tsx delete mode 100644 src/components/note/content/index.tsx delete mode 100644 src/components/note/content/preview.tsx create mode 100644 src/components/note/extend.tsx delete mode 100644 src/components/note/index.tsx delete mode 100644 src/components/note/mention.tsx rename src/components/note/{counter/comments.tsx => meta/comment.tsx} (98%) rename src/components/note/{counter/likes.tsx => meta/reaction.tsx} (96%) rename src/components/note/{content => }/metadata.tsx (87%) create mode 100644 src/components/note/parent.tsx create mode 100644 src/components/note/repost.tsx delete mode 100644 src/components/note/root.tsx diff --git a/next.config.js b/next.config.js index 07e914ab..3bd71dbc 100644 --- a/next.config.js +++ b/next.config.js @@ -9,6 +9,9 @@ module.exports = removeImports({ typescript: { ignoreBuildErrors: true, }, + experimental: { + scrollRestoration: true, + }, webpack: (config) => { config.experiments = { ...config.experiments, topLevelAwait: true }; return config; diff --git a/src-tauri/migrations/20230226004139_create_tables.sql b/src-tauri/migrations/20230226004139_create_tables.sql index c040da30..2855380c 100644 --- a/src-tauri/migrations/20230226004139_create_tables.sql +++ b/src-tauri/migrations/20230226004139_create_tables.sql @@ -85,7 +85,5 @@ CREATE TABLE kind INTEGER NOT NULL DEFAULT 1, tags TEXT NOT NULL, content TEXT NOT NULL, - is_circle INTEGER NOT NULL DEFAULT 0, - is_root INTEGER NOT NULL DEFAULT 0, - is_reply INTEGER NOT NULL DEFAULT 0 + parent_id TEXT ); \ No newline at end of file diff --git a/src/components/note/base.tsx b/src/components/note/base.tsx new file mode 100644 index 00000000..fce9bb4e --- /dev/null +++ b/src/components/note/base.tsx @@ -0,0 +1,105 @@ +import NoteMetadata from '@components/note/metadata'; +import { NoteParent } from '@components/note/parent'; +import { ImagePreview } from '@components/note/preview/image'; +import { VideoPreview } from '@components/note/preview/video'; +import { UserExtend } from '@components/user/extend'; +import { UserMention } from '@components/user/mention'; + +import destr from 'destr'; +import { useRouter } from 'next/router'; +import { memo, useMemo } from 'react'; +import ReactPlayer from 'react-player/lazy'; +import reactStringReplace from 'react-string-replace'; + +import { NoteRepost } from './repost'; + +export const NoteBase = memo(function NoteBase({ event }: { event: any }) { + const router = useRouter(); + + const content = useMemo(() => { + let parsedContent = event.content; + // get data tags + const tags = destr(event.tags); + // handle urls + parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => { + if (match.toLowerCase().match(/\.(jpg|jpeg|gif|png|webp)$/)) { + // image url + return ; + } else if (ReactPlayer.canPlay(match)) { + return ; + } else { + return ( + + {match} + + ); + } + }); + // handle #-hashtags + parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => ( + + #{match} + + )); + // handle mentions + if (tags.length > 0) { + parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => { + if (tags[match][0] === 'p') { + // @-mentions + return ; + } else if (tags[match][0] === 'e') { + // note-mentions + return ; + } else { + return; + } + }); + } + + return parsedContent; + }, [event.content, event.tags]); + + const getParent = useMemo(() => { + if (event.parent_id !== event.id && !event.content.includes('#[0]')) { + return ; + } + + return; + }, [event.content, event.id, event.parent_id]); + + const openThread = (e) => { + const selection = window.getSelection(); + if (selection.toString().length === 0) { + router.push(`/newsfeed/${event.parent_id}`); + } else { + e.stopPropagation(); + } + }; + + 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" + > + <>{getParent} +
+ +
+
+
+ {content} +
+
+
+
e.stopPropagation()} className="mt-5 pl-[52px]"> + +
+
+
+ ); +}); diff --git a/src/components/note/content/extend.tsx b/src/components/note/content/extend.tsx deleted file mode 100644 index f803ad74..00000000 --- a/src/components/note/content/extend.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import NoteMetadata from '@components/note/content/metadata'; -import NotePreview from '@components/note/content/preview'; -import { UserLarge } from '@components/user/large'; -import { UserMention } from '@components/user/mention'; - -import destr from 'destr'; -import { memo, useMemo } from 'react'; -import reactStringReplace from 'react-string-replace'; - -export const ContentExtend = memo(function ContentExtend({ data }: { data: any }) { - const content = useMemo(() => { - let parsedContent; - // get data tags - const tags = destr(data.tags); - // remove all image urls - parsedContent = data.content.replace(/(https?:\/\/.*\.(jpg|jpeg|gif|png|webp|mp4|webm)((\?.*)$|$))/gim, ''); - // handle urls - parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => ( - - {match} - - )); - // handle hashtags - parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => ( - - #{match} - - )); - // handle mentions - if (tags.length > 0) { - parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => { - if (tags[match][0] === 'p') { - return ; - } else { - // #TODO: handle mention other note - // console.log(tags[match]); - } - }); - } - - return parsedContent; - }, [data.content, data.tags]); - - return ( -
- -
-
-
- {content} -
- -
-
-
e.stopPropagation()} - className="mt-5 flex items-center border-t border-b border-zinc-800 py-2" - > - -
-
- ); -}); diff --git a/src/components/note/content/index.tsx b/src/components/note/content/index.tsx deleted file mode 100644 index ba1d09c2..00000000 --- a/src/components/note/content/index.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import NoteMetadata from '@components/note/content/metadata'; -import NotePreview from '@components/note/content/preview'; -import { MentionNote } from '@components/note/mention'; -import { UserExtend } from '@components/user/extend'; -import { UserMention } from '@components/user/mention'; - -import destr from 'destr'; -import { memo, useMemo } from 'react'; -import reactStringReplace from 'react-string-replace'; - -export const Content = memo(function Content({ data }: { data: any }) { - const content = useMemo(() => { - let parsedContent; - // get data tags - const tags = destr(data.tags); - // remove all image urls - parsedContent = data.content.replace(/(https?:\/\/.*\.(jpg|jpeg|gif|png|webp|mp4|webm)((\?.*)$|$))/gim, ''); - // handle urls - parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => ( - - {match} - - )); - // handle hashtags - parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => ( - - #{match} - - )); - // handle mentions - if (tags.length > 0) { - parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => { - if (tags[match][0] === 'p') { - return ; - } else if (tags[match][0] === 'e') { - return ; - } - }); - } - - return parsedContent; - }, [data.content, data.tags]); - - return ( -
- -
-
-
- {content} -
- -
-
-
e.stopPropagation()} className="mt-5 pl-[52px]"> - -
-
- ); -}); diff --git a/src/components/note/content/preview.tsx b/src/components/note/content/preview.tsx deleted file mode 100644 index 505e275e..00000000 --- a/src/components/note/content/preview.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { ImagePreview } from '@components/note/preview/image'; -import { VideoPreview } from '@components/note/preview/video'; - -import { useEffect, useState } from 'react'; -import ReactPlayer from 'react-player'; - -export default function NotePreview({ content }: { content: string }) { - const [video, setVideo] = useState(null); - const [images, setImages] = useState([]); - - useEffect(() => { - const urls = content.match( - /((http|ftp|https):\/\/)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi - ); - - if (urls !== null && urls.length > 0) { - urls.forEach((url) => { - // make sure url alway have http:// - if (!/^https?:\/\//i.test(url)) { - url = 'http://' + url; - } - // parse url with new URL(); - const parseURL = new URL(url, 'https://uselume.xyz'); - // parse image url - if (parseURL.pathname.toLowerCase().match(/\.(jpg|jpeg|gif|png|webp)$/)) { - // add image to preview - setImages((images) => [...images, parseURL.href]); - } else if (ReactPlayer.canPlay(parseURL.href)) { - // add video to preview - setVideo(parseURL.href); - } - }); - } - }, [content]); - - if (video) { - return ; - } else if (images.length > 0) { - return ; - } else { - return <>; - } -} diff --git a/src/components/note/extend.tsx b/src/components/note/extend.tsx new file mode 100644 index 00000000..c37dbabb --- /dev/null +++ b/src/components/note/extend.tsx @@ -0,0 +1,78 @@ +import NoteMetadata from '@components/note/metadata'; +import { ImagePreview } from '@components/note/preview/image'; +import { VideoPreview } from '@components/note/preview/video'; +import { UserLarge } from '@components/user/large'; +import { UserMention } from '@components/user/mention'; + +import destr from 'destr'; +import { memo, useMemo } from 'react'; +import ReactPlayer from 'react-player/lazy'; +import reactStringReplace from 'react-string-replace'; + +export const NoteExtend = memo(function NoteExtend({ event }: { event: any }) { + const content = useMemo(() => { + let parsedContent = event.content; + // get data tags + const tags = destr(event.tags); + // handle urls + parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => { + if (match.toLowerCase().match(/\.(jpg|jpeg|gif|png|webp)$/)) { + // image url + return ; + } else if (ReactPlayer.canPlay(match)) { + return ; + } else { + return ( + + {match} + + ); + } + }); + // handle #-hashtags + parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => ( + + #{match} + + )); + // handle mentions + if (tags.length > 0) { + parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => { + if (tags[match][0] === 'p') { + // @-mentions + return ; + } else if (tags[match][0] === 'e') { + // note-mentions + return

note-{tags[match][1]}

; + } else { + return; + } + }); + } + + return parsedContent; + }, [event.content, event.tags]); + + return ( +
+
+ +
+
+
+ {content} +
+
+
+
+ +
+
+
+ ); +}); diff --git a/src/components/note/index.tsx b/src/components/note/index.tsx deleted file mode 100644 index cecca427..00000000 --- a/src/components/note/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { RootNote } from '@components/note/root'; - -import destr from 'destr'; -import { useRouter } from 'next/router'; -import { memo, useCallback, useRef } from 'react'; - -export const Note = memo(function Note({ event }: { event: any }) { - const router = useRouter(); - const tags = destr(event.tags); - const rootEventID = useRef(null); - - const fetchRootEvent = useCallback(() => { - if (tags.length > 0) { - if (tags[0][0] === 'e' || tags[0][2] === 'root') { - rootEventID.current = tags[0][1]; - return ; - } else { - tags.every((tag) => { - if (tag[0] === 'e' && tag[2] === 'root') { - rootEventID.current = tag[1]; - return ; - } - return <>; - }); - } - } else { - return <>; - } - }, [tags]); - - const openThread = (e) => { - const selection = window.getSelection(); - if (selection.toString().length === 0) { - router.push(`/newsfeed/${rootEventID.current || event.id}`); - } else { - e.stopPropagation(); - } - }; - - 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" - > -

{event.content}

-
- ); -}); diff --git a/src/components/note/mention.tsx b/src/components/note/mention.tsx deleted file mode 100644 index 4ab3cf5e..00000000 --- a/src/components/note/mention.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Content } from '@components/note/content'; -import { RelayContext } from '@components/relaysProvider'; - -import { relaysAtom } from '@stores/relays'; - -import { createCacheNote, getNoteByID } from '@utils/storage'; - -import { useAtomValue } from 'jotai'; -import { memo, useCallback, useContext, useEffect, useState } from 'react'; - -export const MentionNote = memo(function MentionNote({ id }: { id: string }) { - const pool: any = useContext(RelayContext); - - const relays = useAtomValue(relaysAtom); - const [event, setEvent] = useState(null); - - const fetchEvent = useCallback(() => { - pool.subscribe( - [ - { - ids: [id], - kinds: [1], - }, - ], - relays, - (event: any) => { - // update state - setEvent(event); - // insert to database - createCacheNote(event); - }, - undefined, - undefined, - { - unsubscribeOnEose: true, - } - ); - }, [id, pool, relays]); - - useEffect(() => { - getNoteByID(id).then((res) => { - if (res) { - setEvent(res); - } else { - fetchEvent(); - } - }); - }, [fetchEvent, id]); - - if (event) { - return ( -
- -
- ); - } else { - return ( -
-
-
-
-
-
-
- · -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ); - } -}); diff --git a/src/components/note/counter/comments.tsx b/src/components/note/meta/comment.tsx similarity index 98% rename from src/components/note/counter/comments.tsx rename to src/components/note/meta/comment.tsx index 819de01c..f2540658 100644 --- a/src/components/note/counter/comments.tsx +++ b/src/components/note/meta/comment.tsx @@ -16,7 +16,7 @@ import { useRouter } from 'next/router'; import { getEventHash, signEvent } from 'nostr-tools'; import { memo, useContext, useState } from 'react'; -export const CommentsCounter = memo(function CommentsCounter({ +export const NoteComment = memo(function NoteComment({ count, eventID, eventPubkey, @@ -79,7 +79,7 @@ export const CommentsCounter = memo(function CommentsCounter({
-
+
{eventContent}
@@ -88,7 +88,7 @@ export const CommentsCounter = memo(function CommentsCounter({
{/* comment form */} -
+
- - +
); } diff --git a/src/components/note/parent.tsx b/src/components/note/parent.tsx new file mode 100644 index 00000000..af7a7a34 --- /dev/null +++ b/src/components/note/parent.tsx @@ -0,0 +1,155 @@ +import NoteMetadata from '@components/note/metadata'; +import { ImagePreview } from '@components/note/preview/image'; +import { VideoPreview } from '@components/note/preview/video'; +import { RelayContext } from '@components/relaysProvider'; +import { UserExtend } from '@components/user/extend'; +import { UserMention } from '@components/user/mention'; + +import { relaysAtom } from '@stores/relays'; + +import { createCacheNote, getNoteByID } from '@utils/storage'; + +import destr from 'destr'; +import { useAtomValue } from 'jotai'; +import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import ReactPlayer from 'react-player'; +import reactStringReplace from 'react-string-replace'; + +export const NoteParent = memo(function NoteParent({ id }: { id: string }) { + const pool: any = useContext(RelayContext); + + const relays = useAtomValue(relaysAtom); + const [event, setEvent] = useState(null); + + const fetchEvent = useCallback(() => { + pool.subscribe( + [ + { + ids: [id], + kinds: [1], + }, + ], + relays, + (event: any) => { + // update state + setEvent(event); + // insert to database + createCacheNote(event); + }, + undefined, + undefined, + { + unsubscribeOnEose: true, + } + ); + }, [id, pool, relays]); + + useEffect(() => { + getNoteByID(id).then((res) => { + if (res) { + setEvent(res); + } else { + fetchEvent(); + } + }); + }, [fetchEvent, id]); + + const content = useMemo(() => { + let parsedContent = event ? event.content : null; + + if (parsedContent !== null) { + // get data tags + const tags = destr(event.tags); + // handle urls + parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => { + if (match.toLowerCase().match(/\.(jpg|jpeg|gif|png|webp)$/)) { + // image url + return ; + } else if (ReactPlayer.canPlay(match)) { + return ; + } else { + return ( + + {match} + + ); + } + }); + // handle #-hashtags + parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => ( + + #{match} + + )); + // handle mentions + if (tags.length > 0) { + parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => { + if (tags[match][0] === 'p') { + // @-mentions + return ; + } else if (tags[match][0] === 'e') { + // note-mentions + return

note-{tags[match][1]}

; + } else { + return; + } + }); + } + } + + return parsedContent; + }, [event]); + + if (event) { + return ( +
+
+
+ +
+
+
+ {content} +
+
+
+
e.stopPropagation()} className="mt-5 pl-[52px]"> + +
+
+
+ ); + } else { + return ( +
+
+
+
+
+
+
+ · +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); + } +}); diff --git a/src/components/note/preview/image.tsx b/src/components/note/preview/image.tsx index 9d762ad9..3761c296 100644 --- a/src/components/note/preview/image.tsx +++ b/src/components/note/preview/image.tsx @@ -1,23 +1,20 @@ import Image from 'next/image'; import { memo } from 'react'; -export const ImagePreview = memo(function ImagePreview({ data }: { data: any }) { +export const ImagePreview = memo(function ImagePreview({ url }: { url: string }) { return ( -
- {data.map((image: string, index: number) => ( -
= 1 ? 'mt-2' : ''}`}> - {image} -
- ))} +
+ {url}
); }); diff --git a/src/components/note/preview/video.tsx b/src/components/note/preview/video.tsx index 6bf912f7..6f5b9878 100644 --- a/src/components/note/preview/video.tsx +++ b/src/components/note/preview/video.tsx @@ -1,11 +1,11 @@ import { memo } from 'react'; import ReactPlayer from 'react-player/lazy'; -export const VideoPreview = memo(function VideoPreview({ data }: { data: string }) { +export const VideoPreview = memo(function VideoPreview({ url }: { url: string }) { return ( -
e.stopPropagation()} className="relative mt-2 flex flex-col overflow-hidden rounded-lg"> +
e.stopPropagation()} className="relative mt-3 flex flex-col overflow-hidden rounded-lg"> { + pool.subscribe( + [ + { + ids: [id], + kinds: [1], + }, + ], + relays, + (event: any) => { + // update state + setEvent(event); + // insert to database + createCacheNote(event); + }, + undefined, + undefined, + { + unsubscribeOnEose: true, + } + ); + }, [id, pool, relays]); + + useEffect(() => { + getNoteByID(id).then((res) => { + if (res) { + setEvent(res); + } else { + fetchEvent(); + } + }); + }, [fetchEvent, id]); + + const content = useMemo(() => { + let parsedContent = event ? event.content : null; + + if (parsedContent !== null) { + // get data tags + const tags = destr(event.tags); + // handle urls + parsedContent = reactStringReplace(parsedContent, /(https?:\/\/\S+)/g, (match, i) => ( + + {match} + + )); + // handle #-hashtags + parsedContent = reactStringReplace(parsedContent, /#(\w+)/g, (match, i) => ( + + #{match} + + )); + // handle mentions + if (tags.length > 0) { + parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => { + if (tags[match][0] === 'p') { + // @-mentions + return ; + } else { + return; + } + }); + } + } + + return parsedContent; + }, [event]); + + if (event) { + return ( +
+
+ +
+
+
+ {content} +
+
+
+
+
+ ); + } else { + return ( +
+
+
+
+
+
+
+ · +
+
+
+
+
+
+
+ ); + } +}); diff --git a/src/components/note/root.tsx b/src/components/note/root.tsx deleted file mode 100644 index 0a0cd0f9..00000000 --- a/src/components/note/root.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { RelayContext } from '@components/relaysProvider'; - -import { relaysAtom } from '@stores/relays'; - -import { createCacheNote, getNoteByID } from '@utils/storage'; - -import { useAtomValue } from 'jotai'; -import { memo, useCallback, useContext, useEffect, useState } from 'react'; - -export const RootNote = memo(function RootNote({ id }: { id: string }) { - const pool: any = useContext(RelayContext); - - const relays = useAtomValue(relaysAtom); - const [event, setEvent] = useState(null); - - const fetchEvent = useCallback(() => { - pool.subscribe( - [ - { - ids: [id], - kinds: [1], - }, - ], - relays, - (event: any) => { - // update state - setEvent(event); - // insert to database - createCacheNote(event); - }, - undefined, - undefined, - { - unsubscribeOnEose: true, - } - ); - }, [id, pool, relays]); - - useEffect(() => { - getNoteByID(id).then((res) => { - if (res) { - setEvent(res); - } else { - fetchEvent(); - } - }); - }, [fetchEvent, id]); - - if (event) { - return ( -
-
-

{event.content}

-
- ); - } else { - return ( -
-
-
-
-
-
-
- · -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ); - } -}); diff --git a/src/pages/newsfeed/[id].tsx b/src/pages/newsfeed/[id].tsx index 09cc132f..d5229596 100644 --- a/src/pages/newsfeed/[id].tsx +++ b/src/pages/newsfeed/[id].tsx @@ -1,8 +1,7 @@ import BaseLayout from '@layouts/base'; import WithSidebarLayout from '@layouts/withSidebar'; -import { Content } from '@components/note/content'; -import { ContentExtend } from '@components/note/content/extend'; +import { NoteExtend } from '@components/note/extend'; import FormComment from '@components/note/form/comment'; import { RelayContext } from '@components/relaysProvider'; @@ -11,7 +10,6 @@ import { relaysAtom } from '@stores/relays'; import { getNoteByID } from '@utils/storage'; import { useAtomValue } from 'jotai'; -import { GetStaticPaths } from 'next'; import { useRouter } from 'next/router'; import { JSXElementConstructor, @@ -65,21 +63,13 @@ export default function Page() { return (
- {rootEvent && } + {rootEvent && }
- {comments.length > 0 && - comments.map((comment) => ( -
- -
- ))} + {comments.length > 0 && comments.map((comment) =>

{comment.content}

)}
); diff --git a/src/pages/newsfeed/following.tsx b/src/pages/newsfeed/following.tsx index cacc1d0d..463ea0dc 100644 --- a/src/pages/newsfeed/following.tsx +++ b/src/pages/newsfeed/following.tsx @@ -1,7 +1,7 @@ import BaseLayout from '@layouts/base'; import WithSidebarLayout from '@layouts/withSidebar'; -import { Note } from '@components/note'; +import { NoteBase } from '@components/note/base'; import FormBasic from '@components/note/form/basic'; import { hasNewerNoteAtom, notesAtom } from '@stores/note'; @@ -52,7 +52,7 @@ export default function Page() {
{items.map((virtualRow) => (
- +
))}
diff --git a/src/stores/account.tsx b/src/stores/account.tsx index eb884ac8..67c92fee 100644 --- a/src/stores/account.tsx +++ b/src/stores/account.tsx @@ -1,9 +1,9 @@ import { isSSR } from '@utils/ssr'; import { getActiveAccount } from '@utils/storage'; -import { atomWithCache } from 'jotai-cache'; +import { atom } from 'jotai'; -export const activeAccountAtom = atomWithCache(async () => { +export const activeAccountAtom = atom(async () => { const response = isSSR ? {} : await getActiveAccount(); return response; }); diff --git a/src/stores/relays.tsx b/src/stores/relays.tsx index 8b9a06e7..0fac6a20 100644 --- a/src/stores/relays.tsx +++ b/src/stores/relays.tsx @@ -1,9 +1,9 @@ import { isSSR } from '@utils/ssr'; import { getAllRelays } from '@utils/storage'; -import { atomWithCache } from 'jotai-cache'; +import { atom } from 'jotai'; -export const relaysAtom = atomWithCache(async () => { +export const relaysAtom = atom(async () => { const response = isSSR ? [] : await getAllRelays(); return response; }); diff --git a/src/utils/storage.tsx b/src/utils/storage.tsx index 8dca4a06..e807127b 100644 --- a/src/utils/storage.tsx +++ b/src/utils/storage.tsx @@ -1,3 +1,5 @@ +import { getParentID } from '@utils/transform'; + import Database from 'tauri-plugin-sql-api'; let db: null | Database = null; @@ -89,7 +91,7 @@ export async function getCacheProfile(id) { // get note by id export async function getAllNotes() { const db = await connect(); - return await db.select(`SELECT * FROM cache_notes WHERE is_root = 0 ORDER BY created_at DESC LIMIT 1000`); + return await db.select(`SELECT * FROM cache_notes GROUP BY parent_id ORDER BY created_at DESC LIMIT 1000`); } // get note by id @@ -103,7 +105,15 @@ export async function getNoteByID(id) { export async function createCacheNote(data) { const db = await connect(); return await db.execute( - 'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, is_root) VALUES (?, ?, ?, ?, ?, ?, ?);', - [data.id, data.pubkey, data.created_at, data.kind, data.content, JSON.stringify(data.tags), 0] + 'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, parent_id) VALUES (?, ?, ?, ?, ?, ?, ?);', + [ + data.id, + data.pubkey, + data.created_at, + data.kind, + data.content, + JSON.stringify(data.tags), + getParentID(data.tags, data.id), + ] ); } diff --git a/src/utils/transform.tsx b/src/utils/transform.tsx index e034ce63..31dda620 100644 --- a/src/utils/transform.tsx +++ b/src/utils/transform.tsx @@ -1,3 +1,5 @@ +import destr from 'destr'; + export const tagsToArray = (arr) => { const newarr = []; // push item to newarr @@ -15,3 +17,22 @@ export const pubkeyArray = (arr) => { }); return newarr; }; + +export const getParentID = (arr, fallback) => { + const tags = destr(arr); + let parentID = fallback; + + if (tags.length > 0) { + if (tags[0][0] === 'e' || tags[0][2] === 'root' || tags[0][3] === 'root') { + parentID = tags[0][1]; + } else { + tags.forEach((tag) => { + if (tag[0] === 'e' && (tag[2] === 'root' || tag[3] === 'root')) { + parentID = tag[1]; + } + }); + } + } + + return parentID; +};