From a2c6bec3b3ba53d1c892364dc45aaa37e558a4bb Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Sun, 16 Feb 2025 16:21:40 +0100 Subject: [PATCH] refacto: network loader is now truther --- src/components/ui/network-progress.tsx | 20 ++-- src/lib/hooks/use-network-request.ts | 129 +++++++++++++++++++++---- 2 files changed, 125 insertions(+), 24 deletions(-) diff --git a/src/components/ui/network-progress.tsx b/src/components/ui/network-progress.tsx index ffe3b32..30d4f2a 100644 --- a/src/components/ui/network-progress.tsx +++ b/src/components/ui/network-progress.tsx @@ -34,6 +34,7 @@ export function NetworkProgressProvider({ children }: { children: React.ReactNod const totalProgress = Object.values(activeRequests).reduce((acc, curr) => acc + curr, 0); const requestCount = Object.keys(activeRequests).length; const averageProgress = requestCount > 0 ? totalProgress / requestCount : 0; + const displayProgress = Math.min(Math.round(averageProgress), 100); return ( @@ -42,19 +43,24 @@ export function NetworkProgressProvider({ children }: { children: React.ReactNod <> {/* Barre de progression en haut */}
-
+
{/* Indicateur de progression au centre */} -
-
- - {Math.round(averageProgress)}% +
+
+ + + Chargement {displayProgress < 100 && `${displayProgress}%`} +
diff --git a/src/lib/hooks/use-network-request.ts b/src/lib/hooks/use-network-request.ts index dee8f2a..08d9fc0 100644 --- a/src/lib/hooks/use-network-request.ts +++ b/src/lib/hooks/use-network-request.ts @@ -1,39 +1,134 @@ "use client"; -import { useNetworkProgress, simulateProgress } from "@/components/ui/network-progress"; -import { useCallback } from "react"; +import { useNetworkProgress } from "@/components/ui/network-progress"; +import { useCallback, useRef, useEffect } from "react"; +import { usePathname, useSearchParams } from "next/navigation"; export function useNetworkRequest() { const { startProgress, updateProgress, completeProgress } = useNetworkProgress(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + const currentRequestId = useRef(null); + const abortControllerRef = useRef(null); + const progressIntervalRef = useRef(null); + + // Fonction pour simuler une progression graduelle + const simulateProgress = useCallback( + (requestId: string, start: number, end: number) => { + let current = start; + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + } + + progressIntervalRef.current = setInterval(() => { + if (current < end) { + current = Math.min(current + Math.random() * 2, end); + if (requestId === currentRequestId.current) { + updateProgress(requestId, current); + } + } else { + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + progressIntervalRef.current = null; + } + } + }, 200); + }, + [updateProgress] + ); + + useEffect(() => { + // Si on a un requestId en cours, c'est que la navigation est terminée + if (currentRequestId.current) { + updateProgress(currentRequestId.current, 100); + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + progressIntervalRef.current = null; + } + setTimeout(() => { + if (currentRequestId.current) { + completeProgress(currentRequestId.current); + currentRequestId.current = null; + } + }, 100); + } + + // Cleanup + return () => { + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + progressIntervalRef.current = null; + } + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + abortControllerRef.current = null; + } + }; + }, [pathname, searchParams, updateProgress, completeProgress]); const executeRequest = useCallback( async (requestFn: () => Promise): Promise => { - const requestId = Math.random().toString(36).substring(7); + // On démarre un nouveau requestId uniquement si on n'en a pas déjà un + if (!currentRequestId.current) { + currentRequestId.current = Math.random().toString(36).substring(7); + startProgress(currentRequestId.current); + + // Création d'un nouveau AbortController pour cette requête + abortControllerRef.current = new AbortController(); + + // On commence à 10% et on simule jusqu'à 30% + updateProgress(currentRequestId.current, 10); + simulateProgress(currentRequestId.current, 10, 30); + } try { - startProgress(requestId); + // On wrap la fonction de requête pour intercepter les appels fetch + const wrappedFn = async () => { + const originalFetch = window.fetch; + window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => { + if (!currentRequestId.current) return originalFetch(input, init); - // Démarrer la simulation de progression - const progressPromise = simulateProgress(requestId, updateProgress, 500); + // On passe à 40% quand la requête démarre + updateProgress(currentRequestId.current, 40); + simulateProgress(currentRequestId.current, 40, 80); - // Exécuter la requête - const result = await requestFn(); + const response = await originalFetch(input, { + ...init, + signal: abortControllerRef.current?.signal, + }); - // Attendre que la simulation soit terminée - await progressPromise; + // On passe à 90% quand la requête est terminée + updateProgress(currentRequestId.current, 90); + simulateProgress(currentRequestId.current, 90, 95); - // Forcer la progression à 100% avant de terminer - updateProgress(requestId, 100); - setTimeout(() => completeProgress(requestId), 100); + return response; + }; - return result; + try { + return await requestFn(); + } finally { + window.fetch = originalFetch; + } + }; + + return await wrappedFn(); } catch (error) { - // En cas d'erreur, on termine quand même la progression - completeProgress(requestId); + // En cas d'erreur, on nettoie tout + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + progressIntervalRef.current = null; + } + if (currentRequestId.current) { + completeProgress(currentRequestId.current); + currentRequestId.current = null; + } + if (abortControllerRef.current) { + abortControllerRef.current = null; + } throw error; } }, - [startProgress, updateProgress, completeProgress] + [startProgress, updateProgress, completeProgress, simulateProgress] ); return { executeRequest };