import React, { useState, useEffect, useCallback } from 'react'; import { Dialog } from 'primereact/dialog'; import Image from 'next/image'; import { useNDKContext } from '@/context/NDKContext'; import { useSession } from 'next-auth/react'; import { ProgressSpinner } from 'primereact/progressspinner'; import { nip19 } from 'nostr-tools'; const UserBadges = ({ visible, onHide }) => { const [badges, setBadges] = useState([]); const [loading, setLoading] = useState(true); const { ndk } = useNDKContext(); const { data: session } = useSession(); // Define fetchBadges as a useCallback to prevent unnecessary recreations const fetchBadges = useCallback(async () => { if (!ndk || !session?.user?.pubkey) return; setLoading(true); try { // Fetch badge definitions (kind 30009) const badgeDefinitions = await ndk.fetchEvents({ // todo: add the plebdevs hardcoded badge ids (probably in config?) ids: ["4054a68f028edf38cd1d71cc4693d4ff5c9c54b0b44532361fe6abb29530cbf6", "5d38fea9a3c1fb4c55c9635c3132d34608c91de640f772438faa1942677087a8", "3ba20936d66523adb6d71793649bc77f3cea34f50c21ec7bb2c041f936022214", "41edee5af6d4e833d11f9411c2c27cc48c14d2a3c7966ae7648568e825eda1ed"] }); // Fetch badge awards (kind 8) using fetchEvents instead of subscribe const badgeAwards = await ndk.fetchEvents({ kinds: [8], // todo: add the plebdevs author pubkey authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"], "#p": [session.user.pubkey] }); // Create a map to store the latest badge for each definition const latestBadgeMap = new Map(); // Process all awards for (const award of badgeAwards) { const definition = Array.from(badgeDefinitions).find(def => { const defDTag = def.tags.find(t => t[0] === 'd')?.[1]; const awardATag = award.tags.find(t => t[0] === 'a')?.[1]; return awardATag?.includes(defDTag); }); if (definition) { const defId = definition.id; const currentBadge = { name: definition.tags.find(t => t[0] === 'name')?.[1] || 'Unknown Badge', description: definition.tags.find(t => t[0] === 'description')?.[1] || '', image: definition.tags.find(t => t[0] === 'image')?.[1] || '', thumbnail: definition.tags.find(t => t[0] === 'thumb')?.[1] || '', awardedOn: new Date(award.created_at * 1000).toISOString(), nostrId: award.id, naddr: nip19.naddrEncode({ pubkey: definition.pubkey, kind: definition.kind, identifier: definition.tags.find(t => t[0] === 'd')?.[1] }) }; // Only update if this is the first instance or if it's newer than the existing one if (!latestBadgeMap.has(defId) || new Date(currentBadge.awardedOn) > new Date(latestBadgeMap.get(defId).awardedOn)) { latestBadgeMap.set(defId, currentBadge); } } } // Convert map values to array for state update setBadges(Array.from(latestBadgeMap.values())); } catch (error) { console.error('Error fetching badges:', error); } finally { setLoading(false); } }, [ndk, session?.user?.pubkey]); // Initial fetch effect useEffect(() => { if (visible) { fetchBadges(); } }, [visible, fetchBadges]); const formatDate = (dateString) => { return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); }; return (
{loading ? (
) : badges.length === 0 ? (
No badges earned yet. Complete courses to earn badges!
) : (
{badges.map((badge, index) => (
{badge.name}

{badge.name}

{badge.description}

Earned on {formatDate(badge.awardedOn)}
View on Nostr
))}
)}
); }; export default UserBadges;