From 5606dcb32fd876e98a11857bf964512a23a528d8 Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Mon, 17 Jul 2023 13:37:01 +0700 Subject: [PATCH] update replies --- src/app/note/index.tsx | 15 ++--- src/app/space/components/blocks/thread.tsx | 46 +++++-------- src/libs/storage.tsx | 4 +- src/shared/notes/index.tsx | 2 + src/shared/notes/mentions/user.tsx | 2 +- src/shared/notes/replies/item.tsx | 22 +++++-- src/shared/notes/replies/list.tsx | 20 ++++-- src/shared/notes/stats.tsx | 76 ++++++++++++++++++++++ src/shared/notes/users/thread.tsx | 46 +++++++++++++ src/utils/shortenKey.tsx | 14 ++++ src/utils/transform.tsx | 2 +- 11 files changed, 193 insertions(+), 56 deletions(-) create mode 100644 src/shared/notes/stats.tsx create mode 100644 src/shared/notes/users/thread.tsx diff --git a/src/app/note/index.tsx b/src/app/note/index.tsx index 20016f4e..bd019934 100644 --- a/src/app/note/index.tsx +++ b/src/app/note/index.tsx @@ -1,10 +1,7 @@ -import { useQuery } from '@tanstack/react-query'; import { useParams } from 'react-router-dom'; import { useLiveThread } from '@app/space/hooks/useLiveThread'; -import { getNoteByID } from '@libs/storage'; - import { NoteMetadata } from '@shared/notes/metadata'; import { NoteReplyForm } from '@shared/notes/replies/form'; import { RepliesList } from '@shared/notes/replies/list'; @@ -12,16 +9,12 @@ import { NoteSkeleton } from '@shared/notes/skeleton'; import { User } from '@shared/user'; import { useAccount } from '@utils/hooks/useAccount'; -import { parser } from '@utils/parser'; +import { useEvent } from '@utils/hooks/useEvent'; export function NoteScreen() { const { id } = useParams(); const { account } = useAccount(); - const { status, data } = useQuery(['thread', id], async () => { - const res = await getNoteByID(id); - res['content'] = parser(res); - return res; - }); + const { status, data } = useEvent(id); useLiveThread(id); @@ -39,7 +32,7 @@ export function NoteScreen() {
- +
@@ -48,7 +41,7 @@ export function NoteScreen() {
)}
- +
diff --git a/src/app/space/components/blocks/thread.tsx b/src/app/space/components/blocks/thread.tsx index a3fe7b06..f1e37821 100644 --- a/src/app/space/components/blocks/thread.tsx +++ b/src/app/space/components/blocks/thread.tsx @@ -1,31 +1,20 @@ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { Link } from 'react-router-dom'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useLiveThread } from '@app/space/hooks/useLiveThread'; +// import { useLiveThread } from '@app/space/hooks/useLiveThread'; +import { removeBlock } from '@libs/storage'; -import { getNoteByID, removeBlock } from '@libs/storage'; - -import { NoteReplyForm } from '@shared/notes/replies/form'; +import { NoteContent, NoteStats, ThreadUser } from '@shared/notes'; import { RepliesList } from '@shared/notes/replies/list'; import { NoteSkeleton } from '@shared/notes/skeleton'; import { TitleBar } from '@shared/titleBar'; -import { User } from '@shared/user'; -import { useAccount } from '@utils/hooks/useAccount'; -import { parser } from '@utils/parser'; +import { useEvent } from '@utils/hooks/useEvent'; import { Block } from '@utils/types'; export function ThreadBlock({ params }: { params: Block }) { - useLiveThread(params.content); - const queryClient = useQueryClient(); - const { account } = useAccount(); - const { status, data } = useQuery(['thread', params.content], async () => { - const res = await getNoteByID(params.content); - res['content'] = parser(res); - return res; - }); + const { status, data } = useEvent(params.content); const block = useMutation({ mutationFn: (id: string) => { @@ -36,33 +25,34 @@ export function ThreadBlock({ params }: { params: Block }) { }, }); + // subscribe to live reply + // useLiveThread(params.content); + return (
block.mutate(params.id)} />
{status === 'loading' ? (
-
+
) : (
-
- -
- Focus +
+ +
+ +
+
+
-
-
- {account && ( - - )}
)}
- +
diff --git a/src/libs/storage.tsx b/src/libs/storage.tsx index 0d568fc8..61369494 100644 --- a/src/libs/storage.tsx +++ b/src/libs/storage.tsx @@ -169,7 +169,7 @@ export async function createNote( event_id: string, pubkey: string, kind: number, - tags: string[], + tags: string[][], content: string, created_at: number ) { @@ -186,7 +186,7 @@ export async function createNote( // get note replies export async function getReplies(parent_id: string) { const db = await connect(); - const result: any = await db.select( + const result: Array = await db.select( `SELECT * FROM replies WHERE parent_id = "${parent_id}" ORDER BY created_at DESC;` ); return result; diff --git a/src/shared/notes/index.tsx b/src/shared/notes/index.tsx index a9ba8762..0f7531e8 100644 --- a/src/shared/notes/index.tsx +++ b/src/shared/notes/index.tsx @@ -15,6 +15,7 @@ export * from './kinds/kind1063'; export * from './metadata'; export * from './users/mini'; export * from './users/repost'; +export * from './users/thread'; export * from './kinds/thread'; export * from './kinds/repost'; export * from './kinds/sub'; @@ -22,3 +23,4 @@ export * from './skeleton'; export * from './actions'; export * from './content'; export * from './hashtag'; +export * from './stats'; diff --git a/src/shared/notes/mentions/user.tsx b/src/shared/notes/mentions/user.tsx index d2e9bd36..c0ddf550 100644 --- a/src/shared/notes/mentions/user.tsx +++ b/src/shared/notes/mentions/user.tsx @@ -9,7 +9,7 @@ export function MentionUser({ pubkey }: { pubkey: string }) { type="button" className="break-words rounded bg-zinc-800 px-2 py-px text-sm font-normal text-blue-400 no-underline hover:bg-zinc-700 hover:text-blue-500" > - @{user?.name || user?.displayName || shortenKey(pubkey)} + {'@' + user?.name || user?.displayName || shortenKey(pubkey)} ); } diff --git a/src/shared/notes/replies/item.tsx b/src/shared/notes/replies/item.tsx index c6718bd8..98b5d19b 100644 --- a/src/shared/notes/replies/item.tsx +++ b/src/shared/notes/replies/item.tsx @@ -1,17 +1,25 @@ -import { NoteMetadata } from '@shared/notes/metadata'; +import { NoteActions, NoteContent } from '@shared/notes'; import { User } from '@shared/user'; import { parser } from '@utils/parser'; +import { LumeEvent } from '@utils/types'; -export function Reply({ data }: { data: any }) { +export function Reply({ data }: { data: LumeEvent }) { const content = parser(data); return ( -
-
- -
- +
+
+
+ +
+
+
+ + +
+
+
diff --git a/src/shared/notes/replies/list.tsx b/src/shared/notes/replies/list.tsx index d27b6622..7be43a9a 100644 --- a/src/shared/notes/replies/list.tsx +++ b/src/shared/notes/replies/list.tsx @@ -1,17 +1,25 @@ import { NDKEvent } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; -import { getReplies } from '@libs/storage'; +import { useNDK } from '@libs/ndk/provider'; import { Reply } from '@shared/notes/replies/item'; -export function RepliesList({ parent_id }: { parent_id: string }) { - const { status, data } = useQuery(['replies', parent_id], async () => { - return await getReplies(parent_id); +import { LumeEvent } from '@utils/types'; + +export function RepliesList({ id }: { id: string }) { + const { relayUrls, fetcher } = useNDK(); + const { status, data } = useQuery(['thread', id], async () => { + const events = (await fetcher.fetchAllEvents( + relayUrls, + { kinds: [1], '#e': [id] }, + { since: 0 } + )) as unknown as LumeEvent[]; + return events; }); return ( -
+
Replies
@@ -28,7 +36,7 @@ export function RepliesList({ parent_id }: { parent_id: string }) {
) : data.length === 0 ? (
-
+

๐Ÿ‘‹

Share your thought on it...

diff --git a/src/shared/notes/stats.tsx b/src/shared/notes/stats.tsx new file mode 100644 index 00000000..ead08097 --- /dev/null +++ b/src/shared/notes/stats.tsx @@ -0,0 +1,76 @@ +import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'; +import { useQuery } from '@tanstack/react-query'; +import { decode } from 'light-bolt11-decoder'; + +import { useNDK } from '@libs/ndk/provider'; + +import { LoaderIcon } from '@shared/icons'; + +export function NoteStats({ id }: { id: string }) { + const { ndk } = useNDK(); + const { status, data } = useQuery( + ['note-stats', id], + async () => { + let reactions = 0; + let reposts = 0; + let zaps = 0; + + const filter: NDKFilter = { + '#e': [id], + kinds: [6, 7, 9735], + }; + + const events = await ndk.fetchEvents(filter); + events.forEach((event: NDKEvent) => { + switch (event.kind) { + case 6: + reposts += 1; + break; + case 7: + reactions += 1; + break; + case 9735: { + const bolt11 = event.tags.find((tag) => tag[0] === 'bolt11')[1]; + if (bolt11) { + const decoded = decode(bolt11); + const amount = decoded.sections.find((item) => item.name === 'amount'); + const sats = amount.value / 1000; + zaps += sats; + } + break; + } + default: + break; + } + }); + + return { reposts, reactions, zaps }; + }, + { refetchOnWindowFocus: false, refetchOnReconnect: false } + ); + + if (status === 'loading') { + return ( +
+ +
+ ); + } + + return ( +
+

+ {data.reactions} + reactions +

+

+ {data.reposts} + reposts +

+

+ {data.zaps} + zaps +

+
+ ); +} diff --git a/src/shared/notes/users/thread.tsx b/src/shared/notes/users/thread.tsx new file mode 100644 index 00000000..c7ee4346 --- /dev/null +++ b/src/shared/notes/users/thread.tsx @@ -0,0 +1,46 @@ +import { VerticalDotsIcon } from '@shared/icons'; +import { Image } from '@shared/image'; + +import { DEFAULT_AVATAR } from '@stores/constants'; + +import { formatCreatedAt } from '@utils/createdAt'; +import { useProfile } from '@utils/hooks/useProfile'; +import { displayNpub } from '@utils/shortenKey'; + +export function ThreadUser({ pubkey, time }: { pubkey: string; time: number }) { + const { status, user } = useProfile(pubkey); + const createdAt = formatCreatedAt(time); + + if (status === 'loading') { + return
; + } + + return ( +
+ {pubkey} +
+
+
+ {user?.nip05?.toLowerCase() || user?.name || user?.display_name} +
+ +
+
+ {createdAt} + ยท + {displayNpub(pubkey, 16)} +
+
+
+ ); +} diff --git a/src/utils/shortenKey.tsx b/src/utils/shortenKey.tsx index 4e90b4f5..cf950fd3 100644 --- a/src/utils/shortenKey.tsx +++ b/src/utils/shortenKey.tsx @@ -4,3 +4,17 @@ export function shortenKey(pubkey: string) { const npub = nip19.npubEncode(pubkey); return npub.substring(0, 16).concat('...'); } + +export function displayNpub(pubkey: string, len: number, separator?: string) { + const npub = nip19.npubEncode(pubkey) as string; + if (npub.length <= len) return npub; + + separator = separator || ' ... '; + + const sepLen = separator.length, + charsToShow = len - sepLen, + frontChars = Math.ceil(charsToShow / 2), + backChars = Math.floor(charsToShow / 2); + + return npub.substr(0, frontChars) + separator + npub.substr(npub.length - backChars); +} diff --git a/src/utils/transform.tsx b/src/utils/transform.tsx index e4f771a6..7068694f 100644 --- a/src/utils/transform.tsx +++ b/src/utils/transform.tsx @@ -47,7 +47,7 @@ export function arrayObjToPureArr(arr: any) { } // get parent id from event tags -export function getParentID(arr: string[], fallback: string) { +export function getParentID(arr: string[][], fallback: string) { const tags = destr(arr); let parentID = fallback;