import { useSeoMeta } from '@unhead/react';
import { useMemo } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import { useSkateSpots } from '@/hooks/useSkateSpots';
import { useSkateEvents } from '@/hooks/useSkateEvents';
import { useAuthor } from '@/hooks/useAuthor';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { useTrustScore } from '@/hooks/useTrustScore';
import { genUserName } from '@/lib/genUserName';
import { Trophy, Medal } from 'lucide-react';
import { cn } from '@/lib/utils';
function ContributorRow({ pubkey, spots, events, rank }: { pubkey: string; spots: number; events: number; rank: number }) {
const author = useAuthor(pubkey);
const { data: trustScore } = useTrustScore(pubkey);
const metadata = author.data?.metadata;
const displayName = metadata?.name ?? genUserName(pubkey);
const total = spots + events;
const rankColor = rank === 1 ? 'text-yellow-400' : rank === 2 ? 'text-gray-300' : rank === 3 ? 'text-orange-400' : 'text-gray-500';
const rankEmoji = rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : '';
// Trust score color coding
const trustScoreColor = trustScore === undefined ? 'text-gray-500' :
trustScore >= 80 ? 'text-green-500' :
trustScore >= 50 ? 'text-yellow-500' :
'text-gray-500';
return (
{rankEmoji || `#${rank}`}
{displayName.slice(0, 2).toUpperCase()}
{displayName}
{metadata?.nip05 && (
)}
{trustScore ?? '—'}
Trust
);
}
export default function PyramidPage() {
useSeoMeta({
title: 'Pyramid Rankings - SkateSpots',
description: 'Top contributors and community leaders.',
});
const { user } = useCurrentUser();
const { data: allSpots } = useSkateSpots(500);
const { data: allEvents } = useSkateEvents(500);
const { data: userTrustScore } = useTrustScore(user?.pubkey);
// Calculate contributor rankings
const rankings = useMemo(() => {
if (!allSpots && !allEvents) return [];
const contributorMap = new Map();
// Count spots per contributor
allSpots?.forEach(spot => {
const current = contributorMap.get(spot.pubkey) || { spots: 0, events: 0 };
contributorMap.set(spot.pubkey, { ...current, spots: current.spots + 1 });
});
// Count events per contributor
allEvents?.forEach(event => {
const current = contributorMap.get(event.pubkey) || { spots: 0, events: 0 };
contributorMap.set(event.pubkey, { ...current, events: current.events + 1 });
});
// Convert to array and sort by total contributions
return Array.from(contributorMap.entries())
.map(([pubkey, stats]) => ({
pubkey,
spots: stats.spots,
events: stats.events,
total: stats.spots + stats.events,
}))
.sort((a, b) => b.total - a.total);
}, [allSpots, allEvents]);
const userRank = rankings.findIndex(r => r.pubkey === user?.pubkey) + 1;
const userStats = rankings.find(r => r.pubkey === user?.pubkey);
return (
<>
PYRAMID RANKINGS
Top contributors in your city
{/* Your Stats */}
{user && userStats && (
#{userRank}
Your Rank
Keep contributing to climb higher!
{userStats.events}
Events
= 80 ? "text-green-500" :
userTrustScore >= 50 ? "text-yellow-500" :
"text-gray-500"
)}>
{userTrustScore ?? '—'}
Trust
)}
{/* Leaderboard */}
Top Contributors
{rankings.length > 0 ? (
{rankings.slice(0, 20).map((contributor, idx) => (
))}
) : (
No contributors yet. Be the first to add a spot or create an event!
)}
>
);
}