import React, { useState, useRef, useEffect } from 'react'; import { useRouter, usePathname } from 'next/navigation'; import { useAuth } from '@/contexts/AuthContext'; import { Menu, X, LogOut, Camera, Upload, Trash2, Moon, Sun, MessageSquare, UserCircle, LayoutDashboard, Volume2, VolumeX } from 'lucide-react'; import { apiLayer } from '@/lib/api'; import FeedbackModal from '@/components/shared/FeedbackModal'; interface NavbarProps { onAnalyzeClick?: () => void; } export default function Navbar({ onAnalyzeClick }: NavbarProps) { const router = useRouter(); const pathname = usePathname(); const { isAuthenticated, user, logout, updateUser } = useAuth(); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false); const [dropdownPinned, setDropdownPinned] = useState(false); const [soundEnabled, setSoundEnabled] = useState(true); const [navLoading, setNavLoading] = useState(false); const closeTimeoutRef = useRef(null); const handleMouseEnter = () => { if (!dropdownPinned) { if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); setDropdownOpen(true); } }; const handleMouseLeave = () => { if (!dropdownPinned) { closeTimeoutRef.current = setTimeout(() => { setDropdownOpen(false); }, 300); // 300ms grace period } }; useEffect(() => { if (typeof window !== 'undefined') { const saved = localStorage.getItem('spotix_sound'); if (saved !== null) { setSoundEnabled(saved === 'true'); } } }, []); const toggleSound = (e: React.MouseEvent) => { e.stopPropagation(); const newState = !soundEnabled; setSoundEnabled(newState); localStorage.setItem('spotix_sound', String(newState)); }; // Avatar logic const fileInputRef = useRef(null); const dropdownRef = useRef(null); const [localAvatar, setLocalAvatar] = useState(null); // UI State const [feedbackModalOpen, setFeedbackModalOpen] = useState(false); useEffect(() => { if (user?.avatar_url) { setLocalAvatar(user.avatar_url); } else if (user?.google_avatar_url) { setLocalAvatar(user.google_avatar_url); } else { setLocalAvatar(null); } }, [user?.avatar_url, user?.google_avatar_url]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setDropdownOpen(false); setDropdownPinned(false); } }; const handleEsc = (event: KeyboardEvent) => { if (event.key === 'Escape') { setDropdownOpen(false); setDropdownPinned(false); } }; if (dropdownOpen) { document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleEsc); } return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleEsc); }; }, [dropdownOpen]); const handleAvatarChange = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (!file.type.startsWith('image/')) { alert("Only image files are allowed for avatars."); return; } const reader = new FileReader(); reader.onloadend = async () => { const base64String = reader.result as string; setLocalAvatar(base64String); try { await apiLayer.updateAvatar(base64String); updateUser({ avatar_url: base64String }); } catch (err) { console.error("Failed to update avatar:", err); } }; reader.readAsDataURL(file); }; const handleRemoveAvatar = async () => { if (user?.google_avatar_url) { setLocalAvatar(user.google_avatar_url); } else { setLocalAvatar(null); } try { await apiLayer.updateAvatar(""); updateUser({ avatar_url: null }); } catch (err) { console.error("Failed to remove avatar:", err); } setDropdownOpen(false); }; return ( <> {/* Mobile Menu */} setFeedbackModalOpen(false)} /> ); }