- 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>
80 lines
No EOL
2.3 KiB
TypeScript
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>
|
|
);
|
|
} |