-
-
+
);
}
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 (
+
+
+
+
+
+
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' : ''}`}>
-
-
- ))}
+
+
);
});
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 (
+
+ );
+ } 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 (
-
- );
- } 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;
+};