import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { discoveryService, DiscoveryPodcast } from '../services/discoveryService'; import { podcastService } from '../services/podcastService'; import Header from '../components/Header'; import PodcastCard from '../components/PodcastCard'; import { authService } from '../services/authService'; import SuccessModal from '../components/SuccessModal'; import AlertModal, { AlertType } from '../components/AlertModal'; import { GENRES } from '../constants'; import { Flame, Loader, ChevronDown, Sparkles } from 'lucide-react'; export default function Trending() { const queryClient = useQueryClient(); const [user, setUser] = useState(null); const [subscribingId, setSubscribingId] = useState(null); const [selectedGenre, setSelectedGenre] = useState(''); const [showSuccessModal, setShowSuccessModal] = useState(false); const [successPodcast, setSuccessPodcast] = useState(null); const [podcasts, setPodcasts] = useState([]); const [skip, setSkip] = useState(0); const [hasMore, setHasMore] = useState(true); const LIMIT = 30; const [alertConfig, setAlertConfig] = useState<{ isOpen: boolean; title: string; message: string; type: AlertType; }>({ isOpen: false, title: '', message: '', type: 'error' }); useEffect(() => { authService.getMe().then(r => setUser(r.user)).catch(() => {}); }, []); const { data: subsData } = useQuery({ queryKey: ['podcasts', 'subscriptions'], queryFn: () => podcastService.getUserSubscriptions(), }); const { data: trendingData, isLoading, isFetching } = useQuery({ queryKey: ['discover', 'trending', selectedGenre, skip], queryFn: () => discoveryService.getTrendingPodcasts(LIMIT, selectedGenre), staleTime: 30 * 60 * 1000, }); useEffect(() => { if (trendingData?.podcasts) { if (skip === 0) { setPodcasts(trendingData.podcasts); } else { setPodcasts(prev => { const fresh = trendingData.podcasts.filter( (p: any) => !prev.some(old => old.id === p.id) ); return [...prev, ...fresh]; }); } setHasMore(trendingData.podcasts.length === LIMIT); } }, [trendingData, skip]); useEffect(() => { setSkip(0); }, [selectedGenre]); const loadMore = () => { if (hasMore && !isFetching) setSkip(prev => prev + LIMIT); }; const isSubscribed = (rssUrl: string) => subsData?.podcasts.some((p: any) => p.rssUrl === rssUrl); const subscribeMutation = useMutation({ mutationFn: (podcast: DiscoveryPodcast) => discoveryService.subscribeFromDiscovery(podcast.rssUrl, { title: podcast.title, author: podcast.author, imageUrl: podcast.imageUrl, }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['podcasts', 'subscriptions'] }); queryClient.invalidateQueries({ queryKey: ['episodes', 'latest'] }); }, onError: () => { setAlertConfig({ isOpen: true, title: "Erreur", message: "Impossible de s'abonner. Le flux est peut-être inaccessible.", type: 'error' }); }, }); const handleSubscribe = async (podcast: DiscoveryPodcast) => { try { setSubscribingId(podcast.id); await subscribeMutation.mutateAsync(podcast); setSuccessPodcast(podcast); setShowSuccessModal(true); queryClient.invalidateQueries({ queryKey: ['episodes', 'latest'] }); } catch { /* handled in onError */ } finally { setSubscribingId(null); } }; const handleLogout = async () => { try { await authService.logout(); authService.clearTokens(); window.location.href = '/login'; } catch { /* ignore */ } }; return (
{/* Section header */}

Top Podcasts

Mis à jour en temps réel
{/* Genre pills */}
{GENRES.map(genre => ( ))}
{/* Fade edges on mobile */}
{/* Results */} {isLoading && skip === 0 ? (
Analyse des tendances...
) : podcasts.length > 0 ? ( <>
{podcasts.map((podcast, i) => ( handleSubscribe(podcast)} isSubscribed={isSubscribed(podcast.rssUrl)} isSubscribing={subscribingId === podcast.id} /> ))}
{hasMore && (
)} ) : (

Aucune tendance pour le moment

)}
setShowSuccessModal(false)} podcastTitle={successPodcast?.title || ""} podcastAuthor={successPodcast?.author || ""} podcastImage={successPodcast?.imageUrl || ""} /> setAlertConfig(prev => ({ ...prev, isOpen: false }))} />
); }