diff --git a/components/page-progress.tsx b/components/page-progress.tsx index 3e95cbb..90d87fc 100644 --- a/components/page-progress.tsx +++ b/components/page-progress.tsx @@ -13,6 +13,8 @@ function PageProgressContent() { // Start progress on click useEffect(() => { const handleClick = (e: MouseEvent) => { + if (e.defaultPrevented) return + const target = (e.target as HTMLElement).closest("a") if (!target) return @@ -48,6 +50,30 @@ function PageProgressContent() { return () => document.removeEventListener("click", handleClick) }, []) + // A canceled native beforeunload prompt leaves the current document alive. + // The timeout only gets a chance to run when the user chooses to stay. + useEffect(() => { + const handleBeforeUnload = () => { + const navigationUrl = navigationUrlRef.current + if (!navigationUrl) return + + window.setTimeout(() => { + if (window.location.href !== navigationUrl) return + + if (intervalRef.current) { + clearInterval(intervalRef.current) + intervalRef.current = null + } + navigationUrlRef.current = null + setIsVisible(false) + setProgress(0) + }, 0) + } + + window.addEventListener("beforeunload", handleBeforeUnload) + return () => window.removeEventListener("beforeunload", handleBeforeUnload) + }, []) + // Detect completion without reading Next.js URL hooks during prerendering. useEffect(() => { if (!isVisible || !navigationUrlRef.current) return @@ -58,13 +84,8 @@ function PageProgressContent() { setNavigationKey((key) => key + 1) } }, 50) - const fallback = window.setTimeout(() => { - window.clearInterval(checkInterval) - setNavigationKey((key) => key + 1) - }, 10_000) return () => { window.clearInterval(checkInterval) - window.clearTimeout(fallback) } }, [isVisible])