Spaces:
Sleeping
Sleeping
'use client'; | |
import type React from 'react'; | |
import { useState, useEffect } from 'react'; | |
import confetti from 'canvas-confetti'; | |
import { motion } from 'framer-motion'; | |
import { useLoadInfoStore } from '@/store/useLoadInfoStore'; | |
interface CelebrationEffectProps { | |
isVisible: boolean; | |
onClose: () => void; | |
} | |
const CelebrationEffect: React.FC<CelebrationEffectProps> = ({ isVisible, onClose }) => { | |
const [showMessage, setShowMessage] = useState(false); | |
const loadInfo = useLoadInfoStore((state) => state.loadInfo); | |
const secondMeName = loadInfo?.name || 'Second Me'; | |
function randomInRange(min: number, max: number) { | |
return Math.random() * (max - min) + min; | |
} | |
useEffect(() => { | |
if (isVisible) { | |
// Immediately show the message to prevent height changes | |
setShowMessage(true); | |
// Trigger confetti effect with more realistic settings | |
const duration = 5 * 1000; | |
const animationEnd = Date.now() + duration; | |
const defaults = { | |
startVelocity: 35, | |
spread: 360, | |
ticks: 100, | |
zIndex: 100, | |
gravity: 1.2, | |
drift: 0, | |
scalar: 1.2, | |
colors: [ | |
'#5D8BF4', | |
'#4CC9F0', | |
'#7209B7', | |
'#F72585', | |
'#4361EE', | |
'#FFD700', | |
'#00FF00', | |
'#FF4500' | |
] | |
}; | |
const interval: any = setInterval(function () { | |
const timeLeft = animationEnd - Date.now(); | |
if (timeLeft <= 0) { | |
return clearInterval(interval); | |
} | |
const particleCount = 50 * (timeLeft / duration); | |
// Launch colorful confetti from both sides | |
confetti({ | |
...defaults, | |
particleCount, | |
origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } | |
}); | |
confetti({ | |
...defaults, | |
particleCount, | |
origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } | |
}); | |
}, 250); | |
// No auto-close timer - only close when user clicks the button | |
return () => { | |
clearInterval(interval); | |
setShowMessage(false); | |
}; | |
} | |
}, [isVisible, onClose]); | |
// Don't render anything if not visible | |
if (!isVisible) return null; | |
return ( | |
<div className="fixed inset-0 flex items-center justify-center z-[100]"> | |
<motion.div | |
animate={{ opacity: 1 }} | |
className="bg-secondme-warm-bg p-10 rounded-2xl shadow-2xl max-w-md w-full h-[380px] text-center border-2 border-gray-800/10 relative overflow-hidden" | |
initial={{ opacity: 0 }} | |
transition={{ duration: 0.5 }} | |
> | |
{/* Background gradient decorations */} | |
<div className="absolute -top-20 -right-20 w-48 h-48 rounded-full bg-orange-50 opacity-70" /> | |
<div className="absolute -bottom-20 -left-20 w-48 h-48 rounded-full bg-orange-50 opacity-70" /> | |
<div className="relative z-10 h-full flex flex-col justify-center"> | |
<div | |
className={`transition-opacity duration-500 ${showMessage ? 'opacity-100' : 'opacity-0'}`} | |
> | |
<motion.div | |
animate={{ | |
scale: [1, 1.2, 1], | |
rotate: [0, 3, -3, 0], | |
y: [0, -10, 0] | |
}} | |
className="mb-6 flex justify-center" | |
transition={{ | |
repeat: Infinity, | |
repeatType: 'reverse', | |
duration: 2.5 | |
}} | |
> | |
<div className="relative"> | |
<span className="text-6xl filter drop-shadow-lg">β¨</span> | |
<motion.div | |
animate={{ opacity: [0.5, 1, 0.5], scale: [0.8, 1.1, 0.8] }} | |
className="absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center" | |
transition={{ repeat: Infinity, duration: 3 }} | |
> | |
<span className="text-6xl">π</span> | |
</motion.div> | |
</div> | |
</motion.div> | |
<motion.h2 | |
animate={{ y: [0, -5, 0] }} | |
className="text-3xl font-bold text-gray-900 mb-3" | |
transition={{ repeat: 2, duration: 1 }} | |
> | |
Training Complete! | |
</motion.h2> | |
<div> | |
<p className="text-gray-700 mb-2 text-lg"> | |
<span className="font-bold">{secondMeName}</span> has been born | |
</p> | |
<p className="text-gray-600 mb-4 text-sm leading-relaxed"> | |
Your Second Me has learned from your identity and memories, and is now ready to | |
chat, share spaces, and connect with other AIs in the network. | |
</p> | |
</div> | |
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}> | |
<button | |
className="px-6 py-2.5 bg-gray-900 text-white rounded-lg hover:bg-gray-800 transition-colors font-medium shadow-[3px_3px_0px_0px_rgba(0,0,0,0.1)]" | |
onClick={onClose} | |
> | |
Start the journey! | |
</button> | |
</motion.div> | |
</div> | |
</div> | |
</motion.div> | |
</div> | |
); | |
}; | |
export default CelebrationEffect; | |