Collaborative-pixel-art/frontend/src/components/ui/CooldownTimer.tsx
martin 54d27a7ec5 Fix pixel persistence, improve mobile
- Fix pixel data storage to include user information (userId, username, timestamp)
- Enhance zoom controls to center properly without drift
- Improve mobile modal centering with flexbox layout
- Add dynamic backend URL detection for network access
- Fix CORS configuration for development mode
- Add mobile-optimized touch targets and safe area support

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 20:14:48 +02:00

80 lines
No EOL
2.3 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
interface CooldownTimerProps {
isActive: boolean;
duration: number; // in seconds
onComplete: () => void;
}
export function CooldownTimer({ isActive, duration, onComplete }: CooldownTimerProps) {
const [timeLeft, setTimeLeft] = useState(0);
useEffect(() => {
if (isActive) {
setTimeLeft(duration);
const interval = setInterval(() => {
setTimeLeft((prev) => {
if (prev <= 1) {
clearInterval(interval);
// Use setTimeout to avoid setState during render
setTimeout(() => onComplete(), 0);
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(interval);
}
}, [isActive, duration, onComplete]);
if (!isActive || timeLeft === 0) return null;
const progress = ((duration - timeLeft) / duration) * 100;
return (
<motion.div
className="fixed top-16 sm:top-20 left-1/2 transform -translate-x-1/2 z-50"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{
type: "spring",
stiffness: 400,
damping: 25
}}
>
<motion.div
className="bg-white/5 backdrop-blur-2xl rounded-full px-4 sm:px-6 py-2 sm:py-3 border border-white/20 shadow-lg ring-1 ring-white/10"
whileHover={{
scale: 1.05
}}
transition={{ type: "spring", stiffness: 500, damping: 25 }}
>
<div className="flex items-center gap-3 sm:gap-4">
<motion.div
className="text-white font-medium text-sm"
animate={{
color: timeLeft <= 3 ? "#f87171" : "#ffffff"
}}
transition={{ duration: 0.3 }}
>
{timeLeft}s
</motion.div>
<div className="w-16 sm:w-24 h-2 bg-white/20 rounded-full overflow-hidden">
<motion.div
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
initial={{ width: 0 }}
animate={{ width: `${progress}%` }}
transition={{ duration: 0.5, ease: "easeOut" }}
/>
</div>
</div>
</motion.div>
</motion.div>
);
}