SkateSpots/src/hooks/useRiderStats.ts
shakespeare.diy 838ef3f355 feat: SkateSpots Rider Cards — 3 card types, 6 themes, holographic shimmer, print-ready
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 14:29:54 +07:00

64 lines
2.2 KiB
TypeScript

/**
* useRiderStats
*
* Fetches on-chain stats for a given pubkey to display on the Rider Card:
* - spotsAdded: how many skate spots (kind 30818) this user has published
* - eventsCreated: how many calendar events (kind 31923) they created
* - zapsReceived: count of zap receipts (kind 9735) targeting their pubkey
* - shopCards: kind 37097 shop rider cards issued TO this user
* - parkCards: kind 35679 park access grants issued TO this user
*
* All queries hit the SkateSpots community relay so results are skate-specific.
*/
import { useQuery } from '@tanstack/react-query';
import { useNostr } from '@nostrify/react';
export interface RiderStats {
spotsAdded: number;
eventsCreated: number;
zapsReceived: number;
shopCards: number;
parkCards: number;
}
export function useRiderStats(pubkey: string | undefined) {
const { nostr } = useNostr();
return useQuery({
queryKey: ['rider-stats', pubkey],
enabled: !!pubkey,
staleTime: 60_000,
queryFn: async (c) => {
if (!pubkey) return null;
const signal = AbortSignal.any([c.signal, AbortSignal.timeout(8000)]);
// Single query — multiple filter objects, one round-trip
const events = await nostr.query(
[
// Spots the user added
{ kinds: [30818], authors: [pubkey], '#t': ['skatespot'], limit: 200 },
// Events they created
{ kinds: [31923], authors: [pubkey], limit: 100 },
// Zap receipts targeting their pubkey
{ kinds: [9735], '#p': [pubkey], limit: 200 },
// Shop rider cards issued TO this user (kind 37097)
{ kinds: [37097], '#p': [pubkey], limit: 50 },
// Park access cards issued TO this user (kind 35679)
{ kinds: [35679], '#p': [pubkey], limit: 50 },
],
{ signal },
);
const stats: RiderStats = {
spotsAdded: events.filter(e => e.kind === 30818).length,
eventsCreated: events.filter(e => e.kind === 31923).length,
zapsReceived: events.filter(e => e.kind === 9735).length,
shopCards: events.filter(e => e.kind === 37097).length,
parkCards: events.filter(e => e.kind === 35679).length,
};
return stats;
},
});
}