'use client'; import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react'; import { useRouter, usePathname, useSearchParams } from 'next/navigation'; interface TransitionContextType { isTransitioning: boolean; transitionMessage: string | null; startTransition: (href: string, message?: string | null) => void; } const TransitionContext = createContext(undefined); export function TransitionProvider({ children }: { children: ReactNode }) { const [isTransitioning, setIsTransitioning] = useState(false); const [transitionMessage, setTransitionMessage] = useState(null); const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); // We use a ref to track transition state without triggering the route change effect when it turns true const isTransitioningRef = React.useRef(isTransitioning); useEffect(() => { isTransitioningRef.current = isTransitioning; }, [isTransitioning]); // Watch for route changes to complete the transition useEffect(() => { if (isTransitioningRef.current) { // The route has actually changed in the browser, or at least the RSC payload arrived const timer = setTimeout(() => { setIsTransitioning(false); setTimeout(() => setTransitionMessage(null), 300); }, 100); return () => clearTimeout(timer); } }, [pathname, searchParams]); // Run when pathname or search parameters change const startTransition = useCallback((href: string, message: string | null = null) => { // Wenn wir bereits navigieren, nichts tun if (isTransitioning) return; // Normalize paths to avoid false mismatches (e.g. /de vs /de/) const currentPath = typeof window !== 'undefined' ? window.location.pathname.replace(/\/$/, '') || '/' : ''; const targetPath = href.split('?')[0].replace(/\/$/, '') || '/'; const currentSearch = typeof window !== 'undefined' ? window.location.search : ''; const targetSearch = href.includes('?') ? '?' + href.split('?')[1] : ''; // Aktuelle URL prüfen, falls es dieselbe ist, nicht navigieren if (currentPath === targetPath && currentSearch === targetSearch) { // Optional: Wir könnten weich nach oben scrollen window.scrollTo({ top: 0, behavior: 'smooth' }); return; } setTransitionMessage(message); setIsTransitioning(true); // Die Animation des Shutters abwarten (ca. 700ms), dann pushen setTimeout(() => { router.push(href); // Fallback: If for some reason the route doesn't change after 60 seconds (useful for slow dev servers), open the shutter anyway setTimeout(() => { setIsTransitioning((current) => { if (current) { setTimeout(() => setTransitionMessage(null), 300); return false; } return current; }); }, 60000); }, 700); }, [isTransitioning, router]); return ( {children} ); } export function useTransition() { const context = useContext(TransitionContext); if (!context) { throw new Error('useTransition must be used within a TransitionProvider'); } return context; }