import React, { createContext, useContext, useReducer, useCallback, useEffect } from 'react'; import { ApiMangaRepository } from '../../infrastructure/api/apiMangaRepository'; import { GetMangaCollection } from '../../application/useCases/getMangaCollection'; import { GetMangaDetail } from '../../application/useCases/getMangaDetail'; const mangaRepository = new ApiMangaRepository(); const getMangaCollection = new GetMangaCollection(mangaRepository); const getMangaDetail = new GetMangaDetail(mangaRepository); const MangaContext = createContext(null); const initialState = { collection: null, detailedMangas: {}, loading: false, error: null, lastCollectionUpdate: null, isBackgroundLoading: false }; function mangaReducer(state, action) { switch (action.type) { case 'SET_LOADING': return { ...state, loading: action.payload }; case 'SET_BACKGROUND_LOADING': return { ...state, isBackgroundLoading: action.payload }; case 'SET_ERROR': return { ...state, error: action.payload, loading: false }; case 'SET_COLLECTION': return { ...state, collection: action.payload, loading: false, error: null, lastCollectionUpdate: Date.now() }; case 'UPDATE_COLLECTION': return { ...state, collection: action.payload, isBackgroundLoading: false, lastCollectionUpdate: Date.now() }; case 'SET_MANGA_DETAIL': // Mettre à jour également le manga dans la collection si présent const updatedCollection = state.collection ? { ...state.collection, items: state.collection.items.map(manga => manga.slug === action.payload.slug ? { ...manga, ...action.payload, createdAt: manga.createdAt || action.payload.createdAt } : manga ) } : state.collection; return { ...state, collection: updatedCollection, detailedMangas: { ...state.detailedMangas, [action.payload.slug]: { ...action.payload, createdAt: state.collection?.items.find(m => m.slug === action.payload.slug)?.createdAt || action.payload.createdAt } }, loading: false, error: null }; default: return state; } } export function MangaProvider({ children }) { const [state, dispatch] = useReducer(mangaReducer, initialState); // Fonction pour charger la collection en arrière-plan const refreshCollectionInBackground = useCallback(async () => { if (state.isBackgroundLoading) return; dispatch({ type: 'SET_BACKGROUND_LOADING', payload: true }); try { const collection = await getMangaCollection.execute(1); dispatch({ type: 'UPDATE_COLLECTION', payload: collection }); } catch (error) { console.error('Background collection refresh failed:', error); dispatch({ type: 'SET_BACKGROUND_LOADING', payload: false }); } }, [state.isBackgroundLoading]); // Rafraîchir la collection toutes les 5 minutes si elle est chargée useEffect(() => { if (!state.collection) return; const interval = setInterval(() => { refreshCollectionInBackground(); }, 5 * 60 * 1000); return () => clearInterval(interval); }, [state.collection, refreshCollectionInBackground]); const loadCollection = useCallback(async () => { // Si nous avons déjà des données, les afficher immédiatement if (state.collection) { // Rafraîchir en arrière-plan si les données sont vieilles de plus de 30 secondes const isStale = state.lastCollectionUpdate && (Date.now() - state.lastCollectionUpdate) > 30 * 1000; if (isStale && !state.isBackgroundLoading) { refreshCollectionInBackground(); } return; } dispatch({ type: 'SET_LOADING', payload: true }); try { const collection = await getMangaCollection.execute(1); dispatch({ type: 'SET_COLLECTION', payload: collection }); } catch (error) { dispatch({ type: 'SET_ERROR', payload: 'Failed to load manga collection' }); console.error(error); } }, [state.collection, state.lastCollectionUpdate, state.isBackgroundLoading, refreshCollectionInBackground]); const loadMangaDetail = useCallback(async (slug) => { // Retourner les données en cache si disponibles if (state.detailedMangas[slug]) { // Rafraîchir en arrière-plan si les données sont vieilles de plus de 5 minutes const cachedManga = state.detailedMangas[slug]; const isStale = cachedManga.lastUpdate && (Date.now() - cachedManga.lastUpdate) > 5 * 60 * 1000; if (isStale) { // Charger les nouvelles données en arrière-plan getMangaDetail.execute(slug).then(manga => { dispatch({ type: 'SET_MANGA_DETAIL', payload: { ...manga, lastUpdate: Date.now() } }); }).catch(console.error); } return state.detailedMangas[slug]; } // Si le manga est dans la collection, l'utiliser comme données temporaires const collectionManga = getMangaFromCollection(slug); if (collectionManga) { dispatch({ type: 'SET_MANGA_DETAIL', payload: { ...collectionManga, isPartial: true, lastUpdate: Date.now() } }); } // Charger les détails complets dispatch({ type: 'SET_LOADING', payload: true }); try { const manga = await getMangaDetail.execute(slug); dispatch({ type: 'SET_MANGA_DETAIL', payload: { ...manga, lastUpdate: Date.now() } }); return manga; } catch (error) { dispatch({ type: 'SET_ERROR', payload: 'Failed to load manga details' }); console.error(error); return null; } }, [state.detailedMangas]); const getMangaFromCollection = useCallback((slug) => { if (!state.collection) return null; return state.collection.items.find(manga => manga.slug === slug); }, [state.collection]); const value = { ...state, loadCollection, loadMangaDetail, getMangaFromCollection, refreshCollectionInBackground }; return ( {children} ); } export function useManga() { const context = useContext(MangaContext); if (!context) { throw new Error('useManga must be used within a MangaProvider'); } return context; }