/** * Store Zustand pour gérer l'état du player * FE-TYPE-011: Fully typed store */ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import type { Track, PlayerState, PlayerControls } from '../types'; // FE-TYPE-011: Fully typed store interface export interface PlayerStore extends PlayerState, PlayerControls { setCurrentTime: (time: number) => void; setDuration: (duration: number) => void; removeFromQueue: (index: number) => void; reorderQueue: (from: number, to: number) => void; } export const usePlayerStore = create()( persist( (set, get) => ({ // État initial currentTrack: null, isPlaying: false, currentTime: 0, duration: 0, volume: 100, muted: false, queue: [], currentIndex: -1, repeat: 'off', shuffle: false, // Actions de contrôle play: (track?: Track) => { if (!track) { // Si pas de track fourni, reprendre la lecture set({ isPlaying: true }); return; } const { queue } = get(); const trackIndex = queue.findIndex((t) => t.id === track.id); if (trackIndex >= 0) { // Track déjà dans la queue set({ currentTrack: track, currentIndex: trackIndex, isPlaying: true, currentTime: 0, }); } else { // Nouvelle track, l'ajouter à la queue const newQueue = [...queue, track]; set({ currentTrack: track, currentIndex: newQueue.length - 1, queue: newQueue, isPlaying: true, currentTime: 0, }); } }, pause: () => { set({ isPlaying: false }); }, resume: () => { set({ isPlaying: true }); }, stop: () => { set({ isPlaying: false, currentTime: 0 }); }, next: () => { const { queue, currentIndex, repeat, shuffle } = get(); if (queue.length === 0) return; let nextIndex = currentIndex; if (shuffle) { // Mode shuffle: choisir une track aléatoire nextIndex = Math.floor(Math.random() * queue.length); } else if (currentIndex < queue.length - 1) { // Track suivante dans la queue nextIndex = currentIndex + 1; } else if (repeat === 'playlist') { // Répéter la playlist: revenir au début nextIndex = 0; } else { // Pas de track suivante return; } if (nextIndex !== currentIndex && nextIndex < queue.length) { set({ currentIndex: nextIndex, currentTrack: queue[nextIndex], currentTime: 0, isPlaying: true, }); } }, previous: () => { const { queue, currentIndex } = get(); if (queue.length === 0 || currentIndex <= 0) return; const prevIndex = currentIndex - 1; set({ currentIndex: prevIndex, currentTrack: queue[prevIndex], currentTime: 0, isPlaying: true, }); }, seek: (time: number) => { const { duration } = get(); const clampedTime = Math.max(0, Math.min(time, duration || 0)); set({ currentTime: clampedTime }); }, setCurrentTime: (time: number) => { const { duration } = get(); const clampedTime = Math.max(0, Math.min(time, duration || 0)); set({ currentTime: clampedTime }); }, setDuration: (duration: number) => { set({ duration: Math.max(0, duration) }); }, setVolume: (volume: number) => { const clampedVolume = Math.max(0, Math.min(100, volume)); set({ volume: clampedVolume }); }, toggleMute: () => { set({ muted: !get().muted }); }, toggleShuffle: () => { set({ shuffle: !get().shuffle }); }, setRepeat: (mode: 'off' | 'track' | 'playlist') => { set({ repeat: mode }); }, // Queue management addToQueue: (tracks: Track[]) => { const { queue } = get(); const newQueue = [...queue, ...tracks]; set({ queue: newQueue }); }, removeFromQueue: (index: number) => { const { queue, currentIndex } = get(); if (index < 0 || index >= queue.length) return; const newQueue = queue.filter((_, i) => i !== index); // Ajuster l'index courant si nécessaire let newIndex = currentIndex; if (index < currentIndex) { newIndex = currentIndex - 1; } else if (index === currentIndex) { // Si on supprime la track courante, passer à la suivante ou arrêter if (newQueue.length > 0) { newIndex = Math.min(newIndex, newQueue.length - 1); set({ queue: newQueue, currentIndex: newIndex, currentTrack: newQueue[newIndex] || null, isPlaying: newQueue[newIndex] ? get().isPlaying : false, }); return; } else { newIndex = -1; set({ queue: newQueue, currentIndex: newIndex, currentTrack: null, isPlaying: false, }); return; } } set({ queue: newQueue, currentIndex: newIndex }); }, reorderQueue: (from: number, to: number) => { const { queue, currentIndex } = get(); if ( from < 0 || from >= queue.length || to < 0 || to >= queue.length || from === to ) { return; } const newQueue = [...queue]; const [removed] = newQueue.splice(from, 1); newQueue.splice(to, 0, removed); // Ajuster l'index courant let newIndex = currentIndex; if (from === currentIndex) { newIndex = to; } else if (from < currentIndex && to >= currentIndex) { newIndex = currentIndex - 1; } else if (from > currentIndex && to <= currentIndex) { newIndex = currentIndex + 1; } set({ queue: newQueue, currentIndex: newIndex }); }, clearQueue: () => { set({ queue: [], currentIndex: -1, currentTrack: null, isPlaying: false, currentTime: 0, }); }, }), { name: 'player-storage', partialize: (state) => ({ volume: state.volume, muted: state.muted, repeat: state.repeat, shuffle: state.shuffle, queue: state.queue, currentIndex: state.currentIndex, currentTrack: state.currentTrack, }), }, ), );