diff --git a/src/components/feed/NoteCard.tsx b/src/components/feed/NoteCard.tsx index 82a0383..fca30b1 100644 --- a/src/components/feed/NoteCard.tsx +++ b/src/components/feed/NoteCard.tsx @@ -2,6 +2,7 @@ import { useState, useRef } from "react"; import { NDKEvent } from "@nostr-dev-kit/ndk"; import { useProfile } from "../../hooks/useProfile"; import { useReactionCount } from "../../hooks/useReactionCount"; +import { useZapCount } from "../../hooks/useZapCount"; import { useUserStore } from "../../stores/user"; import { useMuteStore } from "../../stores/mute"; import { useUIStore } from "../../stores/ui"; @@ -34,6 +35,7 @@ export function NoteCard({ event }: NoteCardProps) { const [liked, setLiked] = useState(() => getLiked().has(event.id)); const [liking, setLiking] = useState(false); const [reactionCount, adjustReactionCount] = useReactionCount(event.id); + const zapData = useZapCount(event.id); const [showReply, setShowReply] = useState(false); const [replyText, setReplyText] = useState(""); const [replying, setReplying] = useState(false); @@ -203,11 +205,25 @@ export function NoteCard({ event }: NoteCardProps) { onClick={() => setShowZap(true)} className="text-[11px] text-text-dim hover:text-zap transition-colors" > - ⚡ zap + {zapData && zapData.totalSats > 0 + ? `⚡ ${zapData.totalSats.toLocaleString()} sats` + : "⚡ zap"} )} + {/* Stats visible when logged out */} + {!loggedIn && (reactionCount !== null && reactionCount > 0 || zapData !== null && zapData.totalSats > 0) && ( +
+ {reactionCount !== null && reactionCount > 0 && ( + ♥ {reactionCount} + )} + {zapData !== null && zapData.totalSats > 0 && ( + ⚡ {zapData.totalSats.toLocaleString()} sats + )} +
+ )} + {showZap && ( (); + +export function useZapCount(eventId: string): ZapData | null { + const [data, setData] = useState(() => cache.get(eventId) ?? null); + + useEffect(() => { + if (cache.has(eventId)) { + setData(cache.get(eventId)!); + return; + } + fetchZapCount(eventId).then((d) => { + cache.set(eventId, d); + setData(d); + }); + }, [eventId]); + + return data; +} diff --git a/src/lib/nostr/client.ts b/src/lib/nostr/client.ts index b678379..c276786 100644 --- a/src/lib/nostr/client.ts +++ b/src/lib/nostr/client.ts @@ -282,6 +282,26 @@ export async function searchUsers(query: string, limit = 20): Promise { + const instance = getNDK(); + const filter: NDKFilter = { kinds: [NDKKind.Zap], "#e": [eventId] }; + const events = await instance.fetchEvents(filter, { + cacheUsage: NDKSubscriptionCacheUsage.ONLY_RELAY, + }); + let totalSats = 0; + for (const event of events) { + const desc = event.tags.find((t) => t[0] === "description")?.[1]; + if (desc) { + try { + const zapReq = JSON.parse(desc) as { tags?: string[][] }; + const amountTag = zapReq.tags?.find((t) => t[0] === "amount"); + if (amountTag?.[1]) totalSats += Math.round(parseInt(amountTag[1]) / 1000); + } catch { /* malformed */ } + } + } + return { count: events.size, totalSats }; +} + export async function fetchReactionCount(eventId: string): Promise { const instance = getNDK(); const filter: NDKFilter = { diff --git a/src/lib/nostr/index.ts b/src/lib/nostr/index.ts index cd6559f..4d3c897 100644 --- a/src/lib/nostr/index.ts +++ b/src/lib/nostr/index.ts @@ -1 +1 @@ -export { getNDK, connectToRelays, fetchGlobalFeed, fetchFollowFeed, fetchReplies, publishNote, publishArticle, publishProfile, publishReaction, publishRepost, publishQuote, publishReply, publishContactList, fetchReactionCount, fetchUserNotes, fetchProfile, fetchArticle, fetchAuthorArticles, fetchZapsReceived, fetchZapsSent, fetchDMConversations, fetchDMThread, sendDM, decryptDM, fetchMuteList, publishMuteList, getStoredRelayUrls, addRelay, removeRelay, searchNotes, searchUsers } from "./client"; +export { getNDK, connectToRelays, fetchGlobalFeed, fetchFollowFeed, fetchReplies, publishNote, publishArticle, publishProfile, publishReaction, publishRepost, publishQuote, publishReply, publishContactList, fetchReactionCount, fetchZapCount, fetchUserNotes, fetchProfile, fetchArticle, fetchAuthorArticles, fetchZapsReceived, fetchZapsSent, fetchDMConversations, fetchDMThread, sendDM, decryptDM, fetchMuteList, publishMuteList, getStoredRelayUrls, addRelay, removeRelay, searchNotes, searchUsers } from "./client";