From e48e5f5b4d6a43e1aeac4b024a70d210b76396fd Mon Sep 17 00:00:00 2001 From: Dorian Date: Sat, 14 Feb 2026 15:55:02 +0000 Subject: [PATCH] feat: enhance zap integration with backend stats and UI improvements - Updated ContentDetailModal to display zap statistics from both Nostr and backend sources, improving visibility of zap activities. - Refactored zap data handling to merge Nostr relay and backend zap stats, ensuring accurate totals and recent zapper information. - Introduced a new function to fetch backend zap stats, enhancing the modal's responsiveness to user interactions. - Enhanced error handling and added mock data support in the IndeehubApiService for development purposes. These changes improve the user experience by providing comprehensive zap insights and ensuring the UI reflects real-time data accurately. --- src/components/ContentDetailModal.vue | 56 ++++++++++++++++++++++----- src/components/ContentRow.vue | 33 +--------------- src/services/indeehub-api.service.ts | 43 +++++++++++++++++--- 3 files changed, 86 insertions(+), 46 deletions(-) diff --git a/src/components/ContentDetailModal.vue b/src/components/ContentDetailModal.vue index 79a00f4..a9382d9 100644 --- a/src/components/ContentDetailModal.vue +++ b/src/components/ContentDetailModal.vue @@ -143,8 +143,8 @@ {{ content.creator }} - -
+ +
@@ -157,7 +157,7 @@ v-for="(zap, idx) in displayZaps" :key="zap.pubkey + '-' + zap.timestamp + '-' + idx" class="zap-avatar-pill" - :title="getZapperName(zap.pubkey) + ' — ' + zap.amount.toLocaleString() + ' sats'" + :title="zap.amount > 0 ? getZapperName(zap.pubkey) + ' — ' + zap.amount.toLocaleString() + ' sats' : getZapperName(zap.pubkey) + ' zapped'" > nostr.reactionCounts.value) const isLoadingComments = computed(() => nostr.isLoading.value) const commentCount = computed(() => nostr.commentCount.value) -// Zap data from relay -const zapsList = computed(() => nostr.zaps.value) +// Backend zap stats (BTCPay in-app zaps) so modal shows total + who zapped +const backendZapStats = ref<{ + zapCount: number + zapAmountSats: number + recentZapperPubkeys: string[] +} | null>(null) + +// Zap data: merge Nostr relay (9735) + backend so in-app zaps show too +const zapsList = computed(() => { + const fromNostr = nostr.zaps.value + const backend = backendZapStats.value + const nostrPubkeys = new Set(fromNostr.map((z) => z.pubkey)) + const fromBackend: { pubkey: string; amount: number; timestamp: number }[] = [] + if (backend?.recentZapperPubkeys?.length) { + for (const pk of backend.recentZapperPubkeys) { + if (!nostrPubkeys.has(pk)) { + fromBackend.push({ pubkey: pk, amount: 0, timestamp: 0 }) + } + } + } + const merged = [...fromNostr, ...fromBackend] + return merged.sort((a, b) => b.timestamp - a.timestamp) +}) const displayZaps = computed(() => zapsList.value.slice(0, 8)) -const totalZapSats = computed(() => zapsList.value.reduce((sum, z) => sum + z.amount, 0)) +const totalZapSats = computed(() => { + const fromNostr = nostr.zaps.value.reduce((sum, z) => sum + z.amount, 0) + const fromBackend = backendZapStats.value?.zapAmountSats ?? 0 + return fromNostr + fromBackend +}) // User's existing reaction read from relay (not local state) const userReaction = computed(() => nostr.userContentReaction.value) @@ -400,6 +426,7 @@ watch(() => props.content?.id, (newId) => { if (newId && props.isOpen) { loadSocialData(newId) checkRentalAccess() + fetchBackendZapStats(newId) } }) @@ -407,13 +434,23 @@ watch(() => props.isOpen, (open) => { if (open && props.content?.id) { loadSocialData(props.content.id) checkRentalAccess() + fetchBackendZapStats(props.content.id) } else if (!open) { - // Reset rental state when modal closes hasActiveRental.value = false rentalExpiresAt.value = null + backendZapStats.value = null } }) +async function fetchBackendZapStats(contentId: string) { + try { + const data = await indeehubApiService.getZapStats([contentId]) + backendZapStats.value = data[contentId] ?? null + } catch { + backendZapStats.value = null + } +} + function loadSocialData(contentId: string) { nostr.cleanup() nostr.subscribeToContent(contentId) @@ -505,8 +542,8 @@ function handleZap() { } function handleZapped(_amount: number) { - // The zap was confirmed — the relay subscription will pick up - // the zap receipt automatically and update zapsList. + // In-app zaps go through BTCPay; refetch backend stats so modal updates. + if (props.content?.id) fetchBackendZapStats(props.content.id) } function getZapperName(pubkey: string): string { @@ -522,6 +559,7 @@ function getZapperPicture(pubkey: string): string { } function formatZapAmount(sats: number): string { + if (sats <= 0) return '—' if (sats >= 1_000_000) return (sats / 1_000_000).toFixed(1) + 'M' if (sats >= 1_000) return (sats / 1_000).toFixed(sats >= 10_000 ? 0 : 1) + 'k' return sats.toLocaleString() diff --git a/src/components/ContentRow.vue b/src/components/ContentRow.vue index 168316f..0b18a5c 100644 --- a/src/components/ContentRow.vue +++ b/src/components/ContentRow.vue @@ -45,21 +45,7 @@ {{ getCommentCount(content.id) }} - -