276 lines
11 KiB
TypeScript
276 lines
11 KiB
TypeScript
import { motion, AnimatePresence } from "motion/react";
|
||
import { useMemo } from "react";
|
||
import { Sparkles, Gift, Check } from "lucide-react";
|
||
|
||
interface RewardModalProps {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
topicTitle: string;
|
||
}
|
||
|
||
interface FloatingParticle {
|
||
id: number;
|
||
x: number;
|
||
duration: number;
|
||
delay: number;
|
||
color: string;
|
||
}
|
||
|
||
export function RewardModal({ isOpen, onClose, topicTitle }: RewardModalProps) {
|
||
const floatingParticles = useMemo<FloatingParticle[]>(() => {
|
||
const viewportWidth =
|
||
typeof window !== "undefined" ? window.innerWidth : 390;
|
||
|
||
return Array.from({ length: 20 }, (_, i) => ({
|
||
id: i,
|
||
x: Math.random() * viewportWidth,
|
||
duration: 3 + Math.random() * 2,
|
||
delay: Math.random() * 2,
|
||
color: i % 2 === 0 ? "#FFB800" : "#8ACEE0",
|
||
}));
|
||
}, []);
|
||
|
||
return (
|
||
<AnimatePresence>
|
||
{isOpen && (
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
className="fixed inset-0 z-[100] flex items-center justify-center p-4"
|
||
onClick={onClose}
|
||
>
|
||
{/* Backdrop */}
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
className="absolute inset-0"
|
||
style={{
|
||
background: "radial-gradient(circle at center, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.95) 100%)",
|
||
backdropFilter: "blur(8px)",
|
||
}}
|
||
/>
|
||
|
||
{/* Modal Content */}
|
||
<motion.div
|
||
initial={{ scale: 0.5, opacity: 0, y: 50 }}
|
||
animate={{ scale: 1, opacity: 1, y: 0 }}
|
||
exit={{ scale: 0.8, opacity: 0, y: 30 }}
|
||
transition={{ type: "spring", stiffness: 300, damping: 25 }}
|
||
onClick={(e) => e.stopPropagation()}
|
||
className="relative max-w-sm w-full"
|
||
dir="rtl"
|
||
>
|
||
{/* Sparkle effects around the card */}
|
||
{Array.from({ length: 12 }).map((_, i) => (
|
||
<motion.div
|
||
key={i}
|
||
initial={{ opacity: 0, scale: 0 }}
|
||
animate={{
|
||
opacity: [0, 1, 0],
|
||
scale: [0, 1.5, 0],
|
||
x: [0, Math.cos((i / 12) * 2 * Math.PI) * 100],
|
||
y: [0, Math.sin((i / 12) * 2 * Math.PI) * 100],
|
||
}}
|
||
transition={{
|
||
duration: 2,
|
||
delay: i * 0.1,
|
||
repeat: Infinity,
|
||
repeatDelay: 1,
|
||
}}
|
||
className="absolute top-1/2 left-1/2 w-2 h-2 rounded-full"
|
||
style={{
|
||
background: "radial-gradient(circle, rgba(255, 215, 0, 1) 0%, rgba(255, 165, 0, 0.8) 100%)",
|
||
boxShadow: "0 0 10px rgba(255, 215, 0, 0.8)",
|
||
}}
|
||
/>
|
||
))}
|
||
|
||
{/* Main Card */}
|
||
<div
|
||
className="relative overflow-hidden rounded-3xl p-8"
|
||
style={{
|
||
background: "linear-gradient(135deg, rgba(30, 60, 90, 0.95) 0%, rgba(20, 40, 70, 0.98) 100%)",
|
||
border: "3px solid rgba(138, 206, 224, 0.6)",
|
||
boxShadow: "0 20px 60px rgba(0, 0, 0, 0.6), 0 0 40px rgba(138, 206, 224, 0.3), inset 0 2px 10px rgba(138, 206, 224, 0.2)",
|
||
}}
|
||
>
|
||
{/* Animated background gradient */}
|
||
<motion.div
|
||
animate={{
|
||
background: [
|
||
"radial-gradient(circle at 20% 20%, rgba(138, 206, 224, 0.15) 0%, transparent 50%)",
|
||
"radial-gradient(circle at 80% 80%, rgba(138, 206, 224, 0.15) 0%, transparent 50%)",
|
||
"radial-gradient(circle at 20% 20%, rgba(138, 206, 224, 0.15) 0%, transparent 50%)",
|
||
],
|
||
}}
|
||
transition={{ duration: 4, repeat: Infinity }}
|
||
className="absolute inset-0 pointer-events-none"
|
||
/>
|
||
|
||
{/* Success Icon */}
|
||
<div className="flex justify-center mb-4">
|
||
<motion.div
|
||
initial={{ scale: 0, rotate: -180 }}
|
||
animate={{ scale: 1, rotate: 0 }}
|
||
transition={{ delay: 0.2, type: "spring", stiffness: 200 }}
|
||
className="relative"
|
||
>
|
||
<div
|
||
className="w-20 h-20 rounded-full flex items-center justify-center"
|
||
style={{
|
||
background: "linear-gradient(135deg, #4ade80 0%, #22c55e 100%)",
|
||
boxShadow: "0 10px 30px rgba(34, 197, 94, 0.5), inset 0 2px 10px rgba(255, 255, 255, 0.3)",
|
||
}}
|
||
>
|
||
<Check className="w-10 h-10 text-white" strokeWidth={3} />
|
||
</div>
|
||
{/* Glow effect */}
|
||
<motion.div
|
||
animate={{ scale: [1, 1.3, 1], opacity: [0.5, 0, 0.5] }}
|
||
transition={{ duration: 2, repeat: Infinity }}
|
||
className="absolute inset-0 rounded-full"
|
||
style={{
|
||
background: "radial-gradient(circle, rgba(74, 222, 128, 0.6) 0%, transparent 70%)",
|
||
}}
|
||
/>
|
||
</motion.div>
|
||
</div>
|
||
|
||
{/* Title */}
|
||
<motion.h2
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ delay: 0.3 }}
|
||
className="text-2xl font-black text-center mb-3"
|
||
style={{
|
||
color: "#8ACEE0",
|
||
textShadow: "0 2px 10px rgba(138, 206, 224, 0.5), 0 0 20px rgba(138, 206, 224, 0.3)",
|
||
}}
|
||
>
|
||
🎉 تبریک! 🎉
|
||
</motion.h2>
|
||
|
||
{/* Description */}
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
transition={{ delay: 0.5 }}
|
||
className="space-y-3 mb-6"
|
||
>
|
||
<p className="text-white text-center text-base leading-relaxed font-bold">
|
||
چالش با موفقیت به پایان رسید!
|
||
</p>
|
||
<p className="text-white/80 text-center text-sm leading-relaxed">
|
||
پست شما بعد از بررسی منتشر میشود
|
||
</p>
|
||
</motion.div>
|
||
|
||
{/* Reward Section */}
|
||
<motion.div
|
||
initial={{ scale: 0, opacity: 0 }}
|
||
animate={{ scale: 1, opacity: 1 }}
|
||
transition={{ delay: 0.7, type: "spring", stiffness: 150 }}
|
||
className="relative mb-6"
|
||
>
|
||
<div
|
||
className="relative overflow-hidden rounded-2xl p-6"
|
||
style={{
|
||
background: "linear-gradient(135deg, rgba(255, 184, 0, 0.2) 0%, rgba(255, 140, 0, 0.15) 100%)",
|
||
border: "2px solid rgba(255, 184, 0, 0.5)",
|
||
boxShadow: "0 10px 30px rgba(255, 165, 0, 0.3), inset 0 2px 8px rgba(255, 255, 255, 0.2)",
|
||
}}
|
||
>
|
||
{/* Animated shine effect */}
|
||
<motion.div
|
||
animate={{
|
||
x: ["-100%", "200%"],
|
||
}}
|
||
transition={{
|
||
duration: 2,
|
||
repeat: Infinity,
|
||
repeatDelay: 1,
|
||
}}
|
||
className="absolute inset-0"
|
||
style={{
|
||
background: "linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%)",
|
||
}}
|
||
/>
|
||
|
||
<div className="flex items-center justify-center gap-3 mb-2">
|
||
<motion.div
|
||
animate={{ rotate: [0, 10, -10, 0] }}
|
||
transition={{ duration: 1, repeat: Infinity, repeatDelay: 1 }}
|
||
>
|
||
<Gift className="w-8 h-8" style={{ color: "#FFB800" }} />
|
||
</motion.div>
|
||
<h3 className="text-xl font-black" style={{ color: "#FFB800", textShadow: "0 2px 10px rgba(255, 184, 0, 0.5)" }}>
|
||
جایزه دریافت شد!
|
||
</h3>
|
||
<motion.div
|
||
animate={{ rotate: [0, -10, 10, 0] }}
|
||
transition={{ duration: 1, repeat: Infinity, repeatDelay: 1, delay: 0.2 }}
|
||
>
|
||
<Sparkles className="w-7 h-7" style={{ color: "#FFB800" }} />
|
||
</motion.div>
|
||
</div>
|
||
|
||
<p className="text-white text-center text-base font-bold leading-relaxed">
|
||
مدال <span style={{ color: "#FFB800" }}>{topicTitle}</span> به کیف جادوییت اضافه شد!
|
||
</p>
|
||
</div>
|
||
</motion.div>
|
||
|
||
{/* Close Button */}
|
||
<motion.button
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ delay: 0.9 }}
|
||
whileHover={{ scale: 1.05 }}
|
||
whileTap={{ scale: 0.95 }}
|
||
onClick={onClose}
|
||
className="w-full py-4 rounded-full font-black text-lg"
|
||
style={{
|
||
background: "linear-gradient(180deg, #8ACEE0 0%, #5A9FB0 100%)",
|
||
boxShadow: "0 6px 20px rgba(138, 206, 224, 0.4), inset 0 2px 4px rgba(255, 255, 255, 0.3)",
|
||
color: "#0a1f2e",
|
||
textShadow: "0 1px 0 rgba(255, 255, 255, 0.3)",
|
||
}}
|
||
>
|
||
باشه، بریم!
|
||
</motion.button>
|
||
</div>
|
||
</motion.div>
|
||
|
||
{/* Floating particles in background */}
|
||
{floatingParticles.map((particle) => (
|
||
<motion.div
|
||
key={`particle-${particle.id}`}
|
||
initial={{
|
||
x: particle.x,
|
||
y: window.innerHeight + 20,
|
||
opacity: 0,
|
||
}}
|
||
animate={{
|
||
y: -20,
|
||
opacity: [0, 1, 0],
|
||
}}
|
||
transition={{
|
||
duration: particle.duration,
|
||
delay: particle.delay,
|
||
repeat: Infinity,
|
||
}}
|
||
className="absolute w-1 h-1 rounded-full"
|
||
style={{
|
||
background: particle.color,
|
||
boxShadow: `0 0 6px ${particle.color}`,
|
||
}}
|
||
/>
|
||
))}
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>
|
||
);
|
||
}
|