import { useState, useEffect, useRef } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Progress } from "@/components/ui/progress"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; import { Slider } from "@/components/ui/slider"; import { Play, Pause, Square, Volume2, VolumeX, Eye, EyeOff, Timer, Coffee, Target, Zap } from "lucide-react"; import { useTranslation } from "react-i18next"; interface FocusSession { id: string; duration: number; completed: boolean; startTime: Date; endTime?: Date; type: 'work' | 'break'; } interface FocusModeProps { isOpen: boolean; onClose: () => void; } const FOCUS_DURATIONS = { pomodoro: { work: 25, shortBreak: 5, longBreak: 15 }, custom: { work: 30, shortBreak: 10, longBreak: 20 }, deep: { work: 90, shortBreak: 20, longBreak: 30 } }; const AMBIENT_SOUNDS = [ { id: 'rain', name: 'Rain', file: '/sounds/rain.mp3' }, { id: 'forest', name: 'Forest', file: '/sounds/forest.mp3' }, { id: 'cafe', name: 'Café', file: '/sounds/cafe.mp3' }, { id: 'whitenoise', name: 'White Noise', file: '/sounds/whitenoise.mp3' }, { id: 'ocean', name: 'Ocean Waves', file: '/sounds/ocean.mp3' } ]; export default function FocusMode({ isOpen, onClose }: FocusModeProps) { const { t } = useTranslation(); const [isActive, setIsActive] = useState(false); const [timeLeft, setTimeLeft] = useState(25 * 60); // 25 minutes in seconds const [currentSession, setCurrentSession] = useState<'work' | 'break'>('work'); const [sessionCount, setSessionCount] = useState(0); const [focusType, setFocusType] = useState('pomodoro'); const [ambientSound, setAmbientSound] = useState(''); const [ambientVolume, setAmbientVolume] = useState([50]); const [blockDistractions, setBlockDistractions] = useState(true); const [showNotifications, setShowNotifications] = useState(true); const [sessions, setSessions] = useState([]); const intervalRef = useRef(); const audioRef = useRef(null); const startTimeRef = useRef(); useEffect(() => { if (isActive && timeLeft > 0) { intervalRef.current = setInterval(() => { setTimeLeft(prev => { if (prev <= 1) { handleSessionComplete(); return 0; } return prev - 1; }); }, 1000); } else { if (intervalRef.current) { clearInterval(intervalRef.current); } } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [isActive, timeLeft]); useEffect(() => { if (ambientSound && audioRef.current) { audioRef.current.volume = ambientVolume[0] / 100; if (isActive) { audioRef.current.play().catch(console.error); } else { audioRef.current.pause(); } } }, [ambientSound, ambientVolume, isActive]); useEffect(() => { // Apply focus mode styles when active if (isActive && blockDistractions) { document.body.classList.add('focus-mode-active'); // Hide distracting elements const distractingElements = document.querySelectorAll('.floating-voice-control, .notification-toast'); distractingElements.forEach(el => { (el as HTMLElement).style.display = 'none'; }); } else { document.body.classList.remove('focus-mode-active'); // Restore distracting elements const distractingElements = document.querySelectorAll('.floating-voice-control, .notification-toast'); distractingElements.forEach(el => { (el as HTMLElement).style.display = ''; }); } return () => { document.body.classList.remove('focus-mode-active'); const distractingElements = document.querySelectorAll('.floating-voice-control, .notification-toast'); distractingElements.forEach(el => { (el as HTMLElement).style.display = ''; }); }; }, [isActive, blockDistractions]); const handleSessionComplete = () => { const session: FocusSession = { id: Date.now().toString(), duration: getDuration(currentSession), completed: true, startTime: startTimeRef.current || new Date(), endTime: new Date(), type: currentSession }; setSessions(prev => [...prev, session]); if (showNotifications && 'Notification' in window) { new Notification( currentSession === 'work' ? t('focus.sessionComplete', 'Work session complete!') : t('focus.breakComplete', 'Break time over!') ); } // Auto-switch between work and break if (currentSession === 'work') { setSessionCount(prev => prev + 1); const isLongBreak = (sessionCount + 1) % 4 === 0; setCurrentSession('break'); setTimeLeft(getDuration(isLongBreak ? 'longBreak' : 'shortBreak')); } else { setCurrentSession('work'); setTimeLeft(getDuration('work')); } setIsActive(false); }; const getDuration = (type: 'work' | 'shortBreak' | 'longBreak' | 'break') => { if (type === 'break') type = 'shortBreak'; const durations = FOCUS_DURATIONS[focusType]; return durations[type] * 60; // Convert to seconds }; const startSession = () => { setIsActive(true); startTimeRef.current = new Date(); // Request notification permission if (showNotifications && 'Notification' in window && Notification.permission === 'default') { Notification.requestPermission(); } }; const pauseSession = () => { setIsActive(false); }; const stopSession = () => { setIsActive(false); setTimeLeft(getDuration('work')); setCurrentSession('work'); setSessionCount(0); }; const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; const getProgress = () => { const totalDuration = getDuration(currentSession); return ((totalDuration - timeLeft) / totalDuration) * 100; }; const getTodaysStats = () => { const today = new Date().toDateString(); const todaySessions = sessions.filter(s => s.startTime.toDateString() === today); const workSessions = todaySessions.filter(s => s.type === 'work' && s.completed); const totalTime = workSessions.reduce((acc, s) => acc + s.duration, 0); return { sessions: workSessions.length, totalTime: Math.round(totalTime / 60), // Convert to minutes streak: getStreak() }; }; const getStreak = () => { // Calculate consecutive days with at least one completed work session let streak = 0; const today = new Date(); for (let i = 0; i < 30; i++) { const checkDate = new Date(today); checkDate.setDate(today.getDate() - i); const dateString = checkDate.toDateString(); const hasSession = sessions.some(s => s.startTime.toDateString() === dateString && s.type === 'work' && s.completed ); if (hasSession) { streak++; } else if (i > 0) { break; } } return streak; }; const stats = getTodaysStats(); if (!isOpen) return null; return (
{/* Header */}

{t('focus.title', 'Focus Mode')}

{/* Main Timer */}
{currentSession === 'work' ? ( <> {t('focus.workSession', 'Work Session')} ) : ( <> {t('focus.breakTime', 'Break Time')} )} {t('focus.session', 'Session')} {sessionCount + 1} {/* Timer Display */}
{formatTime(timeLeft)}
{/* Progress Bar */}
{/* Controls */}
{!isActive ? ( ) : ( )}
{/* Session Type Selector */}
{/* Settings & Stats */}
{/* Today's Stats */} {t('focus.todaysStats', "Today's Stats")}
{stats.sessions}
{t('focus.sessions', 'Sessions')}
{stats.totalTime}m
{t('focus.focused', 'Focused')}
{stats.streak}
{t('focus.streak', 'Streak')}
{/* Settings */} {t('focus.settings', 'Settings')} {/* Block Distractions */}
{/* Notifications */}
{/* Ambient Sound */}
{ambientSound && (
)}
{/* Audio Element */} {ambientSound && (
); }