feat: enhance zap functionality with stats tracking and pubkey support

- Added a new endpoint in ZapsController to retrieve zap statistics by project IDs, including total counts, amounts, and recent zapper pubkeys.
- Updated ZapsService to record zap statistics, including optional zapper pubkey for tracking who zapped.
- Enhanced CreateZapInvoiceDto to include an optional zapperPubkey field.
- Modified frontend components to display zap stats and integrate with the new backend functionality, improving user engagement and transparency.

These changes improve the overall zap experience by providing detailed insights into zap activities and enhancing the tracking of contributors.
This commit is contained in:
Dorian
2026-02-14 15:35:59 +00:00
parent bb4f13fc65
commit 66db9376ed
10 changed files with 208 additions and 16 deletions

View File

@@ -89,22 +89,46 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { ref, watch, onMounted, onUnmounted } from 'vue'
import type { Content } from '../types/content'
import { useContentDiscovery } from '../composables/useContentDiscovery'
import { indeehubApiService } from '../services/indeehub-api.service'
interface Props {
title: string
contents: Content[]
}
defineProps<Props>()
const props = defineProps<Props>()
defineEmits<{
'content-click': [content: Content]
}>()
const { getStats } = useContentDiscovery()
/** Backend zap stats (BTCPay zaps) so film cards show total + who zapped. */
const backendZapStats = ref<Record<string, { zapCount: number; zapAmountSats: number; recentZapperPubkeys: string[] }>>({})
watch(
() => props.contents,
(contents) => {
const ids = contents?.map((c) => c.id).filter(Boolean) ?? []
if (ids.length === 0) {
backendZapStats.value = {}
return
}
indeehubApiService
.getZapStats(ids)
.then((data) => {
backendZapStats.value = data
})
.catch(() => {
backendZapStats.value = {}
})
},
{ immediate: true },
)
function getReactionCount(contentId: string): number {
return getStats(contentId).plusCount ?? 0
}
@@ -114,11 +138,22 @@ function getCommentCount(contentId: string): number {
}
function getZapCount(contentId: string): number {
return getStats(contentId).zapCount ?? 0
const discovery = getStats(contentId).zapCount ?? 0
const backend = backendZapStats.value[contentId]?.zapCount ?? 0
return discovery + backend
}
function getZapperPubkeys(contentId: string): string[] {
return getStats(contentId).recentZapperPubkeys ?? []
const discovery = getStats(contentId).recentZapperPubkeys ?? []
const backend = backendZapStats.value[contentId]?.recentZapperPubkeys ?? []
const seen = new Set<string>()
const merged: string[] = []
for (const pk of [...discovery, ...backend]) {
if (seen.has(pk) || merged.length >= 5) continue
seen.add(pk)
merged.push(pk)
}
return merged
}
function zapperAvatarUrl(pubkey: string): string {

View File

@@ -204,6 +204,7 @@ import { ref, computed, watch, onUnmounted } from 'vue'
import QRCode from 'qrcode'
import type { Content } from '../types/content'
import { indeehubApiService } from '../services/indeehub-api.service'
import { useAuth } from '../composables/useAuth'
interface Props {
isOpen: boolean
@@ -244,6 +245,8 @@ const creatorName = ref<string | null>(null)
const noCreator = ref(false)
const successQuote = ref('')
const { nostrPubkey } = useAuth()
// Creator id for backend zap invoice (we pay user → BTCPay → we pay creator)
const ownerFilmmakerId = ref<string | null>(null)
const zapInvoiceId = ref<string | null>(null)
@@ -436,6 +439,7 @@ async function handleZap() {
projectId: props.content.id,
filmmakerId: ownerFilmmakerId.value,
amountSats,
...(nostrPubkey.value ? { zapperPubkey: nostrPubkey.value } : {}),
},
)