From b73f06c2b2f4edb975487e3c73462cd53e9429b2 Mon Sep 17 00:00:00 2001 From: R0m1k3 Date: Thu, 16 Apr 2026 14:26:19 +0200 Subject: [PATCH] =?UTF-8?q?Refonte=20th=C3=A8me=20+=20fixes=20lecteur=20+?= =?UTF-8?q?=20suppression=20faux=20stats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AudioPlayer: - Supprime section vitesse de lecture - -30s/+30s (compact et expanded), avec icônes RotateCcw/RotateCw - Rewind compact passé de -15s à -30s Thème: - globals.css refait : animations slide-up/fade-in/reveal définies, glass card améliorée - Background plus profond (#080d1a), glow/blur plus soignés - Boutons neon-button avec hover translateY Corrections: - Supprime Settings2 (bouton sans action) dans Library - Supprime stat "2.4k mins listened" inventée dans Navigation - Stats Library passent de 3 à 2 colonnes (réelles uniquement) Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/AudioPlayer.tsx | 359 +++++++++++------------- frontend/src/components/Navigation.tsx | 2 +- frontend/src/pages/Library.tsx | 59 ++-- frontend/src/styles/globals.css | 150 +++++----- 4 files changed, 267 insertions(+), 303 deletions(-) diff --git a/frontend/src/components/AudioPlayer.tsx b/frontend/src/components/AudioPlayer.tsx index 2c2204f..67dc981 100644 --- a/frontend/src/components/AudioPlayer.tsx +++ b/frontend/src/components/AudioPlayer.tsx @@ -5,17 +5,12 @@ import { Play, Pause, Volume2, - SkipBack, - SkipForward, + VolumeX, ChevronDown, Maximize2, - Minimize2, - List, - VolumeX, - FastForward, - Rewind, X, - Info + RotateCcw, + RotateCw, } from 'lucide-react'; interface AudioPlayerProps { @@ -30,8 +25,6 @@ export default function AudioPlayer({ episode, onClose, userId }: AudioPlayerPro const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); const [volume, setVolume] = useState(1); - const [playbackSpeed, setPlaybackSpeed] = useState(1); - const [isSaving, setIsSaving] = useState(false); const [isMuted, setIsMuted] = useState(false); const [isExpanded, setIsExpanded] = useState(false); @@ -40,256 +33,230 @@ export default function AudioPlayer({ episode, onClose, userId }: AudioPlayerPro // Save progress periodically useEffect(() => { if (!episode || !userId) return; - - const saveProgressInterval = setInterval(async () => { + const interval = setInterval(async () => { if (isPlaying) { try { - setIsSaving(true); - await episodeService.saveProgress( - episode._id, - Math.floor(currentTime), - false - ); - } catch (error) { - console.error('Failed to save progress:', error); - } finally { - setIsSaving(false); - } + await episodeService.saveProgress(episode._id, Math.floor(currentTime), false); + } catch {} } - }, 30000); // Save every 30 seconds - - return () => clearInterval(saveProgressInterval); + }, 30000); + return () => clearInterval(interval); }, [episode, userId, currentTime, isPlaying]); // Load previous progress useEffect(() => { if (!episode || !userId) return; - - const loadProgress = async () => { - try { - const response = await episodeService.getProgress(episode._id); - if (response.progress) { - if (audioRef.current) { - audioRef.current.currentTime = response.progress.position; - } - } - } catch (error) { - console.error('Failed to load progress:', error); + episodeService.getProgress(episode._id).then((r) => { + if (r.progress?.position && audioRef.current) { + audioRef.current.currentTime = r.progress.position; } - }; - - loadProgress(); + }).catch(() => {}); }, [episode, userId]); const handlePlayPause = () => { if (!audioRef.current) return; - - if (isPlaying) { - audioRef.current.pause(); - } else { - audioRef.current.play(); - } + if (isPlaying) audioRef.current.pause(); + else audioRef.current.play(); setIsPlaying(!isPlaying); }; - const handleTimeUpdate = () => { - if (audioRef.current) { - setCurrentTime(audioRef.current.currentTime); - } - }; - - const handleLoadedMetadata = () => { - if (audioRef.current) { - setDuration(audioRef.current.duration); - } + const handleSeek = (delta: number) => { + if (!audioRef.current) return; + audioRef.current.currentTime = Math.max(0, Math.min(duration, currentTime + delta)); }; const handleProgressChange = (e: React.ChangeEvent) => { - const newTime = parseFloat(e.target.value); - setCurrentTime(newTime); - if (audioRef.current) { - audioRef.current.currentTime = newTime; - } + const t = parseFloat(e.target.value); + setCurrentTime(t); + if (audioRef.current) audioRef.current.currentTime = t; }; const handleVolumeChange = (e: React.ChangeEvent) => { - const newVolume = parseFloat(e.target.value); - setVolume(newVolume); - if (audioRef.current) { - audioRef.current.volume = newVolume; - } + const v = parseFloat(e.target.value); + setVolume(v); + setIsMuted(v === 0); + if (audioRef.current) audioRef.current.volume = v; }; - const handleSpeedChange = (speed: number) => { - setPlaybackSpeed(speed); - if (audioRef.current) { - audioRef.current.playbackRate = speed; - } + const toggleMute = () => { + if (!audioRef.current) return; + const muted = !isMuted; + setIsMuted(muted); + audioRef.current.volume = muted ? 0 : volume; }; - const formatTime = (seconds: number) => { - if (!isFinite(seconds)) return '0:00'; - const mins = Math.floor(seconds / 60); - const secs = Math.floor(seconds % 60); - return `${mins}:${secs.toString().padStart(2, '0')}`; + const formatTime = (s: number) => { + if (!isFinite(s)) return '0:00'; + const m = Math.floor(s / 60); + const sec = Math.floor(s % 60); + return `${m}:${sec.toString().padStart(2, '0')}`; }; if (!episode) return null; + const progress = duration > 0 ? (currentTime / duration) * 100 : 0; + return ( -
+
+