- {replies.map((reply) => {
- const info = replyMap.get(reply.id)
+ {replies.slice(0, showCount).map((reply) => {
+ const parentEventTag = getParentEventTag(reply)
+ const parentEventOriginalId = parentEventTag?.[1]
+ const parentEventId = parentEventTag ? generateEventIdFromETag(parentEventTag) : undefined
return (
(replyRefs.current[reply.id] = el)} key={reply.id}>
parentEventOriginalId && highlightReply(parentEventOriginalId)}
highlight={highlightReplyId === reply.id}
/>
diff --git a/src/hooks/useFetchEvent.tsx b/src/hooks/useFetchEvent.tsx
index 5973ac35..ae846a55 100644
--- a/src/hooks/useFetchEvent.tsx
+++ b/src/hooks/useFetchEvent.tsx
@@ -1,9 +1,11 @@
+import { useReply } from '@/providers/ReplyProvider'
import client from '@/services/client.service'
import { Event } from 'nostr-tools'
import { useEffect, useState } from 'react'
export function useFetchEvent(eventId?: string) {
const [isFetching, setIsFetching] = useState(true)
+ const { addReplies } = useReply()
const [error, setError] = useState
(null)
const [event, setEvent] = useState(undefined)
@@ -20,6 +22,7 @@ export function useFetchEvent(eventId?: string) {
const event = await client.fetchEvent(eventId)
if (event) {
setEvent(event)
+ addReplies([event])
}
} catch (error) {
setError(error as Error)
diff --git a/src/lib/link.ts b/src/lib/link.ts
index a8a18b91..308e5d33 100644
--- a/src/lib/link.ts
+++ b/src/lib/link.ts
@@ -1,12 +1,11 @@
-import client from '@/services/client.service'
import { Event, nip19 } from 'nostr-tools'
import { getSharableEventId } from './event'
+import { generateEventId } from './tag'
export const toHome = () => '/'
export const toNote = (eventOrId: Pick | string) => {
if (typeof eventOrId === 'string') return `/notes/${eventOrId}`
- const relay = client.getEventHint(eventOrId.id)
- const nevent = nip19.neventEncode({ id: eventOrId.id, author: eventOrId.pubkey, relays: [relay] })
+ const nevent = generateEventId(eventOrId)
return `/notes/${nevent}`
}
export const toNoteList = ({ hashtag, search }: { hashtag?: string; search?: string }) => {
diff --git a/src/lib/tag.ts b/src/lib/tag.ts
index 55f8893b..a05a3ea8 100644
--- a/src/lib/tag.ts
+++ b/src/lib/tag.ts
@@ -1,6 +1,7 @@
+import client from '@/services/client.service'
import { TImageInfo } from '@/types'
import { isBlurhashValid } from 'blurhash'
-import { nip19 } from 'nostr-tools'
+import { Event, nip19 } from 'nostr-tools'
import { isValidPubkey } from './pubkey'
export function tagNameEquals(tagName: string) {
@@ -28,6 +29,11 @@ export function generateEventIdFromETag(tag: string[]) {
}
}
+export function generateEventId(event: Pick) {
+ const relay = client.getEventHint(event.id)
+ return nip19.neventEncode({ id: event.id, author: event.pubkey, relays: [relay] })
+}
+
export function extractImageInfoFromTag(tag: string[]): TImageInfo | null {
if (tag[0] !== 'imeta') return null
const urlItem = tag.find((item) => item.startsWith('url '))
diff --git a/src/providers/NoteStatsProvider.tsx b/src/providers/NoteStatsProvider.tsx
index 63abb42c..6d918045 100644
--- a/src/providers/NoteStatsProvider.tsx
+++ b/src/providers/NoteStatsProvider.tsx
@@ -11,13 +11,11 @@ export type TNoteStats = {
likes: { id: string; pubkey: string; created_at: number; emoji: TEmoji | string }[]
reposts: Set
zaps: { pr: string; pubkey: string; amount: number; comment?: string }[]
- replyCount: number
updatedAt?: number
}
type TNoteStatsContext = {
noteStatsMap: Map>
- updateNoteReplyCount: (noteId: string, replyCount: number) => void
addZap: (eventId: string, pr: string, amount: number, comment?: string) => void
updateNoteStatsByEvents: (events: Event[]) => void
fetchNoteStats: (event: Event) => Promise | undefined>
@@ -215,20 +213,6 @@ export function NoteStatsProvider({ children }: { children: React.ReactNode }) {
return
}
- const updateNoteReplyCount = (noteId: string, replyCount: number) => {
- setNoteStatsMap((prev) => {
- const old = prev.get(noteId)
- if (!old) {
- prev.set(noteId, { replyCount })
- return new Map(prev)
- } else if (old.replyCount === undefined || old.replyCount < replyCount) {
- prev.set(noteId, { ...old, replyCount })
- return new Map(prev)
- }
- return prev
- })
- }
-
const addZap = (eventId: string, pr: string, amount: number, comment?: string) => {
if (!pubkey) return
setNoteStatsMap((prev) => {
@@ -247,7 +231,6 @@ export function NoteStatsProvider({ children }: { children: React.ReactNode }) {
value={{
noteStatsMap,
fetchNoteStats,
- updateNoteReplyCount,
addZap,
updateNoteStatsByEvents
}}
diff --git a/src/providers/ReplyProvider.tsx b/src/providers/ReplyProvider.tsx
new file mode 100644
index 00000000..0a3f8d9f
--- /dev/null
+++ b/src/providers/ReplyProvider.tsx
@@ -0,0 +1,70 @@
+import { getParentEventTag, getRootEventTag } from '@/lib/event'
+import { Event } from 'nostr-tools'
+import { createContext, useCallback, useContext, useState } from 'react'
+
+type TReplyContext = {
+ repliesMap: Map }>
+ addReplies: (replies: Event[]) => void
+}
+
+const ReplyContext = createContext(undefined)
+
+export const useReply = () => {
+ const context = useContext(ReplyContext)
+ if (!context) {
+ throw new Error('useReply must be used within a ReplyProvider')
+ }
+ return context
+}
+
+export function ReplyProvider({ children }: { children: React.ReactNode }) {
+ const [repliesMap, setRepliesMap] = useState<
+ Map }>
+ >(new Map())
+
+ const addReplies = useCallback((replies: Event[]) => {
+ const newReplyIdSet = new Set()
+ const newReplyEventMap = new Map()
+ replies.forEach((reply) => {
+ if (newReplyIdSet.has(reply.id)) return
+ newReplyIdSet.add(reply.id)
+
+ const rootETag = getRootEventTag(reply)
+ if (!rootETag) return
+ const rootId = rootETag[1]
+ newReplyEventMap.set(rootId, [...(newReplyEventMap.get(rootId) || []), reply])
+
+ const parentETag = getParentEventTag(reply)
+ if (!parentETag) return
+ const parentId = parentETag[1]
+ newReplyEventMap.set(parentId, [...(newReplyEventMap.get(parentId) || []), reply])
+ })
+ if (newReplyEventMap.size === 0) return
+
+ setRepliesMap((prev) => {
+ for (const [id, newReplyEvents] of newReplyEventMap.entries()) {
+ const replies = prev.get(id) || { events: [], eventIdSet: new Set() }
+ newReplyEvents.forEach((reply) => {
+ if (!replies.eventIdSet.has(reply.id)) {
+ replies.events.push(reply)
+ replies.eventIdSet.add(reply.id)
+ }
+ })
+ replies.events.sort((a, b) => a.created_at - b.created_at)
+ prev.set(id, replies)
+ }
+ return new Map(prev)
+ })
+ }, [])
+
+ return (
+
+ {children}
+
+ )
+}