import { useEffect, useState, useCallback, useRef, type MouseEvent } from "react"; import { useDiscordActivity } from "@/contexts/DiscordActivityContext"; import LoadingScreen from "@/components/LoadingScreen"; import { Heart, MessageCircle, Zap, Gamepad2, Briefcase, BookOpen, Sparkles, Shield, ExternalLink, Flame, Star, Target, Gift, CheckCircle, AlertCircle, Loader2, ChevronRight, Users, TrendingUp, Calendar, Award, } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; const APP_URL = "https://aethex.dev"; type ArmType = "labs" | "gameforge" | "corp" | "foundation" | "nexus" | "staff"; const ARM_CONFIG: Record = { labs: { label: "Labs", icon: Zap, color: "#facc15", gradient: "from-yellow-500/20 via-amber-500/10 to-transparent", glow: "shadow-yellow-500/30" }, gameforge: { label: "GameForge", icon: Gamepad2, color: "#4ade80", gradient: "from-green-500/20 via-emerald-500/10 to-transparent", glow: "shadow-green-500/30" }, corp: { label: "Corp", icon: Briefcase, color: "#60a5fa", gradient: "from-blue-500/20 via-sky-500/10 to-transparent", glow: "shadow-blue-500/30" }, foundation: { label: "Foundation", icon: BookOpen, color: "#f87171", gradient: "from-red-500/20 via-rose-500/10 to-transparent", glow: "shadow-red-500/30" }, nexus: { label: "Nexus", icon: Sparkles, color: "#c084fc", gradient: "from-purple-500/20 via-violet-500/10 to-transparent", glow: "shadow-purple-500/30" }, staff: { label: "Staff", icon: Shield, color: "#818cf8", gradient: "from-indigo-500/20 via-blue-500/10 to-transparent", glow: "shadow-indigo-500/30" }, }; interface Post { id: string; title: string; content: string; arm_affiliation: ArmType; author_id: string; created_at: string; likes_count: number; comments_count: number; tags?: string[]; user_profiles?: { id: string; username?: string; full_name?: string; avatar_url?: string }; } interface LeaderboardEntry { rank: number; user_id: string; username: string; avatar_url?: string; total_xp: number; level: number; current_streak?: number; } interface UserStats { total_xp: number; level: number; current_streak: number; longest_streak: number; rank?: number; } function XPRing({ xp, level, size = 64, strokeWidth = 4, color }: { xp: number; level: number; size?: number; strokeWidth?: number; color: string }) { const xpForLevel = 1000; const xpInCurrentLevel = xp % xpForLevel; const progress = xpInCurrentLevel / xpForLevel; const radius = (size - strokeWidth) / 2; const circumference = 2 * Math.PI * radius; const strokeDashoffset = circumference * (1 - progress); return (
{level}
); } function XPGainAnimation({ amount, onComplete }: { amount: number; onComplete: () => void }) { useEffect(() => { const timer = setTimeout(onComplete, 2000); return () => clearTimeout(timer); }, [onComplete]); return ( +{amount} XP ); } function ConfettiEffect() { const colors = ["#facc15", "#4ade80", "#60a5fa", "#c084fc", "#f87171"]; return (
{Array.from({ length: 50 }).map((_, i) => ( 0.5 ? 1 : -1), x: (Math.random() - 0.5) * 200 }} transition={{ duration: 2 + Math.random() * 2, delay: Math.random() * 0.5, ease: "easeOut" }} /> ))}
); } function FeedTab({ openExternalLink, userId }: { openExternalLink: (url: string) => Promise; userId?: string }) { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [likingPost, setLikingPost] = useState(null); const [likedPosts, setLikedPosts] = useState>(new Set()); const fetchPosts = useCallback(async () => { setLoading(true); setError(null); try { const response = await fetch("/api/feed?limit=10"); if (!response.ok) throw new Error("Failed to load"); const data = await response.json(); setPosts(data.data || []); } catch { setError("Couldn't load feed"); } finally { setLoading(false); } }, []); const handleQuickLike = async (postId: string, e: MouseEvent) => { e.stopPropagation(); if (!userId || likingPost) return; setLikingPost(postId); try { const response = await fetch(`/api/feed/${postId}/like`, { method: "POST", headers: { "Content-Type": "application/json" }, }); if (response.ok) { setLikedPosts(prev => new Set(prev).add(postId)); setPosts(prev => prev.map(p => p.id === postId ? { ...p, likes_count: p.likes_count + 1 } : p )); } } catch { // Silent fail for likes } finally { setLikingPost(null); } }; useEffect(() => { fetchPosts(); }, [fetchPosts]); if (loading) return (
); if (error) return (

{error}

); return ( {posts.length === 0 ? ( ) : ( posts.map((post, index) => { const config = ARM_CONFIG[post.arm_affiliation] || ARM_CONFIG.nexus; const isLiked = likedPosts.has(post.id); const isLiking = likingPost === post.id; return ( {post.comments_count} ); }) )} ); } function RealmsTab({ currentRealm, openExternalLink }: { currentRealm: ArmType; openExternalLink: (url: string) => Promise }) { const realms = Object.entries(ARM_CONFIG) as [ArmType, typeof ARM_CONFIG[ArmType]][]; return (
{realms.map(([key, config], index) => { const Icon = config.icon; const isActive = currentRealm === key; return ( openExternalLink(`${APP_URL}/${key}`)} initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: index * 0.05 }} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} className={`p-3 rounded-xl transition-all flex items-center gap-3 border ${ isActive ? `bg-gradient-to-br ${config.gradient} border-[${config.color}]/30 shadow-lg ${config.glow}` : "bg-[#232428] hover:bg-[#2b2d31] border-[#3f4147]" }`} >
{config.label} {isActive && }
); })}

Tap to explore each realm

); } function LeaderboardTab({ openExternalLink, currentUserId }: { openExternalLink: (url: string) => Promise; currentUserId?: string }) { const leaderboard: LeaderboardEntry[] = [ { rank: 1, user_id: "1", username: "PixelMaster", total_xp: 15420, level: 16, current_streak: 21 }, { rank: 2, user_id: "2", username: "CodeNinja", total_xp: 12800, level: 13, current_streak: 14 }, { rank: 3, user_id: "3", username: "BuilderX", total_xp: 11250, level: 12, current_streak: 7 }, { rank: 4, user_id: "4", username: "GameDev_Pro", total_xp: 9800, level: 10, current_streak: 5 }, { rank: 5, user_id: "5", username: "ForgeHero", total_xp: 8500, level: 9, current_streak: 12 }, { rank: 6, user_id: "6", username: "NexusCreator", total_xp: 7200, level: 8, current_streak: 3 }, { rank: 7, user_id: "7", username: "LabsExplorer", total_xp: 6100, level: 7 }, ]; const medals = ["πŸ₯‡", "πŸ₯ˆ", "πŸ₯‰"]; return (
Your Rank #12

Keep earning XP to climb!

{( leaderboard.map((entry, index) => ( {medals[index] || `#${index + 1}`} {entry.avatar_url ? ( ) : (
{entry.username?.[0]?.toUpperCase() || "?"}
)}

{entry.username}

Lvl {entry.level} Β· {entry.total_xp.toLocaleString()} XP

{entry.current_streak && entry.current_streak > 0 && ( {entry.current_streak} )}
)) )}
); } function QuestsTab({ userId, onXPGain }: { userId?: string; onXPGain: (amount: number) => void }) { const [dailyClaimed, setDailyClaimed] = useState(() => { try { const today = new Date().toISOString().slice(0, 10); const lastClaim = localStorage.getItem('aethex_daily_claim'); return lastClaim === today; } catch { return false; } }); const [claiming, setClaiming] = useState(false); const claimDailyXP = () => { if (claiming || dailyClaimed) return; setClaiming(true); setTimeout(() => { try { const today = new Date().toISOString().slice(0, 10); localStorage.setItem('aethex_daily_claim', today); } catch {} setDailyClaimed(true); onXPGain(25); setClaiming(false); }, 500); }; const quests = [ { id: "daily", title: "Daily Login", description: "Log in today", xp: 25, icon: Calendar, canClaim: !dailyClaimed, onClaim: claimDailyXP, claiming }, { id: "post", title: "Share Your Work", description: "Create a post", xp: 20, icon: Star, progress: 0, total: 1 }, { id: "like", title: "Show Support", description: "Like 5 posts", xp: 15, icon: Heart, progress: 3, total: 5 }, { id: "explore", title: "Realm Explorer", description: "Visit 3 realms", xp: 30, icon: Sparkles, progress: 2, total: 3 }, ]; return (
Daily Quests Resets at midnight UTC
{quests.map((quest, index) => { const Icon = quest.icon; const isCompleted = quest.progress !== undefined && quest.progress >= (quest.total || 1); return (
{quest.title} +{quest.xp} XP

{quest.description}

{quest.progress !== undefined && quest.total && (
{quest.progress}/{quest.total}
)}
{quest.canClaim !== undefined && ( {quest.claiming ? ( ) : quest.canClaim ? ( "Claim" ) : ( )} )} {isCompleted && !quest.canClaim && ( )}
); })}
); } function JobsTab({ openExternalLink }: { openExternalLink: (url: string) => Promise }) { const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const fetchJobs = async () => { try { const response = await fetch("/api/opportunities?limit=5&status=active"); if (response.ok) { const data = await response.json(); setJobs(data.data || []); } } catch { setJobs([]); } finally { setLoading(false); } }; fetchJobs(); }, []); const categories = [ { label: "Full-Time", icon: Briefcase, color: "#60a5fa" }, { label: "Contract", icon: Target, color: "#4ade80" }, { label: "Freelance", icon: Star, color: "#facc15" }, ]; if (loading) return (
); return (
{categories.map(({ label, icon: Icon, color }, index) => ( openExternalLink(`${APP_URL}/opportunities?type=${label.toLowerCase()}`)} initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: index * 0.05 }} whileHover={{ scale: 1.02, y: -2 }} whileTap={{ scale: 0.98 }} className="p-3 rounded-xl bg-[#232428] hover:bg-[#2b2d31] transition-all flex flex-col items-center gap-2 border border-[#3f4147]" > {label} ))}
{jobs.length > 0 && (

Latest Opportunities

{jobs.slice(0, 3).map((job, index) => ( openExternalLink(`${APP_URL}/opportunities/${job.id}`)} initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: 0.15 + index * 0.05 }} className="w-full p-3 rounded-xl bg-[#232428] hover:bg-[#2b2d31] transition-all text-left border border-[#3f4147] group" >

{job.title}

{job.company_name || "Remote"}

))}
)} openExternalLink(`${APP_URL}/opportunities`)} whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.99 }} className="w-full py-3 rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 transition-all text-white text-sm font-medium shadow-lg shadow-purple-500/20" > Browse All Opportunities
); } function BadgesTab({ userId, openExternalLink }: { userId?: string; openExternalLink: (url: string) => Promise }) { const displayBadges = [ { id: "1", name: "Early Adopter", icon: "πŸš€", description: "Joined during beta", unlocked: true }, { id: "2", name: "First Post", icon: "✨", description: "Created your first post", unlocked: true }, { id: "3", name: "Realm Explorer", icon: "πŸ—ΊοΈ", description: "Visited all 6 realms", unlocked: false, progress: 4, total: 6 }, { id: "4", name: "Social Butterfly", icon: "πŸ¦‹", description: "Made 10 connections", unlocked: false, progress: 6, total: 10 }, { id: "5", name: "Top Contributor", icon: "πŸ‘‘", description: "Reach top 10 leaderboard", unlocked: false }, { id: "6", name: "Week Warrior", icon: "βš”οΈ", description: "7-day login streak", unlocked: false, progress: 3, total: 7 }, ]; return (
Your Badges {displayBadges.filter(b => b.unlocked).length}/{displayBadges.length} unlocked
{displayBadges.map((badge, index) => ( {badge.icon}
{badge.name} {badge.unlocked && }

{badge.description}

{!badge.unlocked && badge.progress !== undefined && badge.total && (
{badge.progress}/{badge.total}
)}
{badge.unlocked && }
))}
); } function ParticipantsBar({ participants, currentUserId }: { participants: any[]; currentUserId?: string }) { const otherParticipants = participants.filter(p => p.id !== currentUserId); if (otherParticipants.length === 0) return null; return (
{otherParticipants.length} here
{otherParticipants.slice(0, 8).map((p) => ( {p.avatar ? ( {p.global_name ) : (
{(p.global_name || p.username)?.[0]?.toUpperCase() || "?"}
)} {p.speaking && ( )} ))} {otherParticipants.length > 8 && (
+{otherParticipants.length - 8}
)}
); } export default function Activity() { const { isActivity, isLoading, user, error, openExternalLink, participants } = useDiscordActivity(); const [activeTab, setActiveTab] = useState("feed"); const [xpGain, setXpGain] = useState(null); const [showConfetti, setShowConfetti] = useState(false); const [userStats, setUserStats] = useState({ total_xp: 0, level: 1, current_streak: 0, longest_streak: 0 }); const currentRealm: ArmType = (user?.primary_arm as ArmType) || "nexus"; useEffect(() => { // Use mock data for user stats (no API endpoint available) setUserStats({ total_xp: 2450, level: 3, current_streak: 5, longest_streak: 12, rank: 12 }); }, [user?.id]); const handleXPGain = useCallback((amount: number) => { setXpGain(amount); setUserStats(prev => ({ ...prev, total_xp: prev.total_xp + amount, level: Math.max(1, Math.floor((prev.total_xp + amount) / 1000) + 1) })); const newLevel = Math.max(1, Math.floor((userStats.total_xp + amount) / 1000) + 1); if (newLevel > userStats.level) { setShowConfetti(true); setTimeout(() => setShowConfetti(false), 3000); } }, [userStats]); if (isLoading) return ; if (error) return (

Connection Error

{error}

); if (!isActivity) return (

AeThex Activity

Launch this within Discord to access the full experience.

Visit aethex.dev
); const tabs = [ { id: "feed", label: "Feed", icon: MessageCircle }, { id: "realms", label: "Realms", icon: Sparkles }, { id: "quests", label: "Quests", icon: Target }, { id: "top", label: "Top", icon: TrendingUp }, { id: "jobs", label: "Jobs", icon: Briefcase }, { id: "badges", label: "Badges", icon: Award }, ]; const realmConfig = ARM_CONFIG[currentRealm]; const RealmIcon = realmConfig.icon; return (
{showConfetti && } {xpGain && setXpGain(null)} />} {/* Dynamic Gradient Header */}
{user?.avatar_url ? ( ) : (
{user?.username?.[0]?.toUpperCase() || "?"}
)}

{user?.full_name || user?.username || "Builder"}

{realmConfig.label}
{userStats.current_streak > 0 && (
{userStats.current_streak}d
)}
{userStats.total_xp.toLocaleString()} XP {1000 - (userStats.total_xp % 1000)} XP to Level {userStats.level + 1}
{/* Participants Bar */} {/* Tab Navigation */}
{tabs.map((tab) => { const Icon = tab.icon; const isActive = activeTab === tab.id; return ( ); })}
{/* Tab Content - fills remaining space */}
{activeTab === "feed" && } {activeTab === "realms" && } {activeTab === "quests" && } {activeTab === "top" && } {activeTab === "jobs" && } {activeTab === "badges" && }
); }