refactor: optimize note interaction data processing
This commit is contained in:
parent
2d2153448d
commit
18ce08ce07
13 changed files with 289 additions and 293 deletions
|
|
@ -4,10 +4,11 @@ import {
|
|||
DropdownMenuContent,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useNoteStatsById } from '@/hooks/useNoteStatsById'
|
||||
import { createReactionDraftEvent } from '@/lib/draft-event'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useNoteStats } from '@/providers/NoteStatsProvider'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import noteStatsService from '@/services/note-stats.service'
|
||||
import { Loader, SmilePlus } from 'lucide-react'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
|
@ -21,15 +22,15 @@ export default function LikeButton({ event }: { event: Event }) {
|
|||
const { t } = useTranslation()
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const { pubkey, publish, checkLogin } = useNostr()
|
||||
const { noteStatsMap, updateNoteStatsByEvents, fetchNoteStats } = useNoteStats()
|
||||
const [liking, setLiking] = useState(false)
|
||||
const [isEmojiReactionsOpen, setIsEmojiReactionsOpen] = useState(false)
|
||||
const [isPickerOpen, setIsPickerOpen] = useState(false)
|
||||
const noteStats = useNoteStatsById(event.id)
|
||||
const { myLastEmoji, likeCount } = useMemo(() => {
|
||||
const stats = noteStatsMap.get(event.id) || {}
|
||||
const stats = noteStats || {}
|
||||
const like = stats.likes?.find((like) => like.pubkey === pubkey)
|
||||
return { myLastEmoji: like?.emoji, likeCount: stats.likes?.length }
|
||||
}, [noteStatsMap, event, pubkey])
|
||||
}, [noteStats, pubkey])
|
||||
|
||||
const like = async (emoji: string) => {
|
||||
checkLogin(async () => {
|
||||
|
|
@ -39,14 +40,13 @@ export default function LikeButton({ event }: { event: Event }) {
|
|||
const timer = setTimeout(() => setLiking(false), 10_000)
|
||||
|
||||
try {
|
||||
const noteStats = noteStatsMap.get(event.id)
|
||||
if (!noteStats?.updatedAt) {
|
||||
await fetchNoteStats(event)
|
||||
await noteStatsService.fetchNoteStats(event, pubkey)
|
||||
}
|
||||
|
||||
const reaction = createReactionDraftEvent(event, emoji)
|
||||
const evt = await publish(reaction)
|
||||
updateNoteStatsByEvents([evt])
|
||||
noteStatsService.updateNoteStatsByEvents([evt])
|
||||
} catch (error) {
|
||||
console.error('like failed', error)
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'
|
||||
import { useNoteStatsById } from '@/hooks/useNoteStatsById'
|
||||
import { createReactionDraftEvent } from '@/lib/draft-event'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useNoteStats } from '@/providers/NoteStatsProvider'
|
||||
import noteStatsService from '@/services/note-stats.service'
|
||||
import { TEmoji } from '@/types'
|
||||
import { Loader } from 'lucide-react'
|
||||
import { Event } from 'nostr-tools'
|
||||
|
|
@ -11,10 +12,10 @@ import Emoji from '../Emoji'
|
|||
|
||||
export default function Likes({ event }: { event: Event }) {
|
||||
const { pubkey, checkLogin, publish } = useNostr()
|
||||
const { noteStatsMap, updateNoteStatsByEvents } = useNoteStats()
|
||||
const noteStats = useNoteStatsById(event.id)
|
||||
const [liking, setLiking] = useState<string | null>(null)
|
||||
const likes = useMemo(() => {
|
||||
const _likes = noteStatsMap.get(event.id)?.likes
|
||||
const _likes = noteStats?.likes
|
||||
if (!_likes) return []
|
||||
|
||||
const stats = new Map<string, { key: string; emoji: TEmoji | string; pubkeys: Set<string> }>()
|
||||
|
|
@ -26,7 +27,7 @@ export default function Likes({ event }: { event: Event }) {
|
|||
stats.get(key)?.pubkeys.add(item.pubkey)
|
||||
})
|
||||
return Array.from(stats.values()).sort((a, b) => b.pubkeys.size - a.pubkeys.size)
|
||||
}, [noteStatsMap, event])
|
||||
}, [noteStats, event])
|
||||
|
||||
if (!likes.length) return null
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ export default function Likes({ event }: { event: Event }) {
|
|||
try {
|
||||
const reaction = createReactionDraftEvent(event, emoji)
|
||||
const evt = await publish(reaction)
|
||||
updateNoteStatsByEvents([evt])
|
||||
noteStatsService.updateNoteStatsByEvents([evt])
|
||||
} catch (error) {
|
||||
console.error('like failed', error)
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ import {
|
|||
DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useNoteStatsById } from '@/hooks/useNoteStatsById'
|
||||
import { createRepostDraftEvent } from '@/lib/draft-event'
|
||||
import { getSharableEventId } from '@/lib/event'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useNoteStats } from '@/providers/NoteStatsProvider'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import noteStatsService from '@/services/note-stats.service'
|
||||
import { Loader, PencilLine, Repeat } from 'lucide-react'
|
||||
import { Event, kinds } from 'nostr-tools'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
|
@ -23,17 +24,16 @@ export default function RepostButton({ event }: { event: Event }) {
|
|||
const { t } = useTranslation()
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const { publish, checkLogin, pubkey } = useNostr()
|
||||
const { noteStatsMap, updateNoteStatsByEvents, fetchNoteStats } = useNoteStats()
|
||||
const noteStats = useNoteStatsById(event.id)
|
||||
const [reposting, setReposting] = useState(false)
|
||||
const [isPostDialogOpen, setIsPostDialogOpen] = useState(false)
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
|
||||
const { repostCount, hasReposted } = useMemo(() => {
|
||||
const stats = noteStatsMap.get(event.id) || {}
|
||||
return {
|
||||
repostCount: stats.reposts?.size,
|
||||
hasReposted: pubkey ? stats.reposts?.has(pubkey) : false
|
||||
repostCount: noteStats?.reposts?.size,
|
||||
hasReposted: pubkey ? noteStats?.reposts?.has(pubkey) : false
|
||||
}
|
||||
}, [noteStatsMap, event.id])
|
||||
}, [noteStats, event.id])
|
||||
const canRepost = !hasReposted && !reposting
|
||||
|
||||
const repost = async () => {
|
||||
|
|
@ -44,11 +44,10 @@ export default function RepostButton({ event }: { event: Event }) {
|
|||
const timer = setTimeout(() => setReposting(false), 5000)
|
||||
|
||||
try {
|
||||
const noteStats = noteStatsMap.get(event.id)
|
||||
const hasReposted = noteStats?.reposts?.has(pubkey)
|
||||
if (hasReposted) return
|
||||
if (!noteStats?.updatedAt) {
|
||||
const events = await fetchNoteStats(event)
|
||||
const events = await noteStatsService.fetchNoteStats(event, pubkey)
|
||||
if (events.some((e) => e.kind === kinds.Repost && e.pubkey === pubkey)) {
|
||||
return
|
||||
}
|
||||
|
|
@ -56,7 +55,7 @@ export default function RepostButton({ event }: { event: Event }) {
|
|||
|
||||
const repost = createRepostDraftEvent(event)
|
||||
const evt = await publish(repost)
|
||||
updateNoteStatsByEvents([evt])
|
||||
noteStatsService.updateNoteStatsByEvents([evt])
|
||||
} catch (error) {
|
||||
console.error('repost failed', error)
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'
|
||||
import { useNoteStatsById } from '@/hooks/useNoteStatsById'
|
||||
import { formatAmount } from '@/lib/lightning'
|
||||
import { useNoteStats } from '@/providers/NoteStatsProvider'
|
||||
import { Zap } from 'lucide-react'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
|
@ -8,12 +8,11 @@ import { SimpleUserAvatar } from '../UserAvatar'
|
|||
import ZapDialog from '../ZapDialog'
|
||||
|
||||
export default function TopZaps({ event }: { event: Event }) {
|
||||
const { noteStatsMap } = useNoteStats()
|
||||
const noteStats = useNoteStatsById(event.id)
|
||||
const [zapIndex, setZapIndex] = useState(-1)
|
||||
const topZaps = useMemo(() => {
|
||||
const stats = noteStatsMap.get(event.id) || {}
|
||||
return stats.zaps?.slice(0, 10) || []
|
||||
}, [noteStatsMap, event])
|
||||
return noteStats?.zaps?.sort((a, b) => b.amount - a.amount).slice(0, 10) || []
|
||||
}, [noteStats])
|
||||
|
||||
if (!topZaps.length) return null
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { useNoteStatsById } from '@/hooks/useNoteStatsById'
|
||||
import { getLightningAddressFromProfile } from '@/lib/lightning'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useNoteStats } from '@/providers/NoteStatsProvider'
|
||||
import { useZap } from '@/providers/ZapProvider'
|
||||
import client from '@/services/client.service'
|
||||
import lightning from '@/services/lightning.service'
|
||||
import noteStatsService from '@/services/note-stats.service'
|
||||
import { Loader, Zap } from 'lucide-react'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { MouseEvent, TouchEvent, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
|
@ -15,18 +16,17 @@ import ZapDialog from '../ZapDialog'
|
|||
export default function ZapButton({ event }: { event: Event }) {
|
||||
const { t } = useTranslation()
|
||||
const { checkLogin, pubkey } = useNostr()
|
||||
const { noteStatsMap, addZap } = useNoteStats()
|
||||
const noteStats = useNoteStatsById(event.id)
|
||||
const { defaultZapSats, defaultZapComment, quickZap } = useZap()
|
||||
const [touchStart, setTouchStart] = useState<{ x: number; y: number } | null>(null)
|
||||
const [openZapDialog, setOpenZapDialog] = useState(false)
|
||||
const [zapping, setZapping] = useState(false)
|
||||
const { zapAmount, hasZapped } = useMemo(() => {
|
||||
const stats = noteStatsMap.get(event.id) || {}
|
||||
return {
|
||||
zapAmount: stats.zaps?.reduce((acc, zap) => acc + zap.amount, 0),
|
||||
hasZapped: pubkey ? stats.zaps?.some((zap) => zap.pubkey === pubkey) : false
|
||||
zapAmount: noteStats?.zaps?.reduce((acc, zap) => acc + zap.amount, 0),
|
||||
hasZapped: pubkey ? noteStats?.zaps?.some((zap) => zap.pubkey === pubkey) : false
|
||||
}
|
||||
}, [noteStatsMap, event, pubkey])
|
||||
}, [noteStats, pubkey])
|
||||
const [disable, setDisable] = useState(true)
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const isLongPressRef = useRef(false)
|
||||
|
|
@ -57,7 +57,13 @@ export default function ZapButton({ event }: { event: Event }) {
|
|||
if (!zapResult) {
|
||||
return
|
||||
}
|
||||
addZap(event.id, zapResult.invoice, defaultZapSats, defaultZapComment)
|
||||
noteStatsService.addZap(
|
||||
pubkey,
|
||||
event.id,
|
||||
zapResult.invoice,
|
||||
defaultZapSats,
|
||||
defaultZapComment
|
||||
)
|
||||
} catch (error) {
|
||||
toast.error(`${t('Zap failed')}: ${(error as Error).message}`)
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { cn } from '@/lib/utils'
|
||||
import { useNoteStats } from '@/providers/NoteStatsProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import noteStatsService from '@/services/note-stats.service'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useEffect, useState } from 'react'
|
||||
import BookmarkButton from '../BookmarkButton'
|
||||
|
|
@ -28,13 +29,13 @@ export default function NoteStats({
|
|||
displayTopZapsAndLikes?: boolean
|
||||
}) {
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const { fetchNoteStats } = useNoteStats()
|
||||
const { pubkey } = useNostr()
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!fetchIfNotExisting) return
|
||||
setLoading(true)
|
||||
fetchNoteStats(event).finally(() => setLoading(false))
|
||||
noteStatsService.fetchNoteStats(event, pubkey).finally(() => setLoading(false))
|
||||
}, [event, fetchIfNotExisting])
|
||||
|
||||
if (isSmallScreen) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue