import { useState, useEffect, useCallback, useRef } from 'react'; import { api } from '@/lib/api'; import type { DiscoverPlaylist, DiscoverConfig } from '../types'; interface BatchStatus { active: boolean; status: "downloading" | "scanning" | null; batchId?: string; progress?: number; completed?: number; failed?: number; total?: number; } export function useDiscoverData() { const [playlist, setPlaylist] = useState(null); const [config, setConfig] = useState(null); const [loading, setLoading] = useState(true); const [batchStatus, setBatchStatus] = useState(null); const [pendingGeneration, setPendingGeneration] = useState(false); const pollingRef = useRef(null); const wasActiveRef = useRef(false); const pendingRef = useRef(false); // Track pending state for polling callback // Keep pendingRef in sync with pendingGeneration useEffect(() => { pendingRef.current = pendingGeneration; }, [pendingGeneration]); const loadData = useCallback(async () => { try { const [playlistData, configData] = await Promise.all([ api.getCurrentDiscoverWeekly().catch(() => null), api.getDiscoverConfig().catch(() => null), ]); setPlaylist(playlistData); setConfig(configData); } catch (error) { console.error('Failed to load discover data:', error); } }, []); const checkBatchStatus = useCallback(async () => { try { const status = await api.getDiscoverBatchStatus(); setBatchStatus(status); // Clear pending state once batch is confirmed active if (status.active) { setPendingGeneration(false); } // If batch was active and now isn't, reload data if (wasActiveRef.current && !status.active) { console.log('[Discover] Batch completed, reloading playlist data...'); wasActiveRef.current = false; setPendingGeneration(false); await loadData(); } // Track if batch is currently active if (status.active) { wasActiveRef.current = true; } return status; } catch (error) { console.error('Failed to check batch status:', error); setPendingGeneration(false); return null; } }, [loadData]); // Start polling for batch status const startPolling = useCallback(() => { if (pollingRef.current) return; // Already polling console.log('[Discover] Starting batch status polling...'); pollingRef.current = setInterval(async () => { const status = await checkBatchStatus(); // Only stop polling if: // 1. Status is not active AND // 2. We're not waiting for generation to start (pendingRef) AND // 3. We previously had an active batch (wasActiveRef) // This ensures we keep polling while waiting for the batch to be created if (status && !status.active && !pendingRef.current && wasActiveRef.current) { console.log('[Discover] Batch completed, stopping polling'); if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; } } }, 3000); // Poll every 3 seconds }, [checkBatchStatus]); // Stop polling const stopPolling = useCallback(() => { if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; } }, []); // Initial load useEffect(() => { const init = async () => { setLoading(true); // Check batch status first const status = await checkBatchStatus(); // Load playlist data await loadData(); // Start polling if batch is active if (status?.active) { startPolling(); } setTimeout(() => { setLoading(false); }, 100); }; init(); return () => { stopPolling(); }; }, []); // Start polling when batch becomes active OR when generation is pending // This ensures we catch the batch as soon as it's created useEffect(() => { if ((batchStatus?.active || pendingGeneration) && !pollingRef.current) { startPolling(); } }, [batchStatus?.active, pendingGeneration, startPolling]); // Optimistically update a track's liked status const updateTrackLiked = useCallback((albumId: string, isLiked: boolean) => { setPlaylist(prev => { if (!prev) return prev; return { ...prev, tracks: prev.tracks.map(track => track.albumId === albumId ? { ...track, isLiked, likedAt: isLiked ? new Date().toISOString() : null } : track ), }; }); }, []); return { playlist, config, setConfig, loading, reloadData: loadData, batchStatus, refreshBatchStatus: checkBatchStatus, setPendingGeneration, updateTrackLiked, isGenerating: pendingGeneration || batchStatus?.active || false, }; }