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";