Stop refetching /me on every page mount, warm shared accounting queries, prefetch nav routes, and keep the shell visible during soft SPA transitions. Co-authored-by: Cursor <cursoragent@cursor.com>
61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import { Suspense, useEffect, useState } from "react";
|
|
import { usePathname, useSearchParams } from "next/navigation";
|
|
|
|
function RouteProgressInner() {
|
|
const pathname = usePathname();
|
|
const searchParams = useSearchParams();
|
|
const [active, setActive] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const onClick = (e: MouseEvent) => {
|
|
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
|
|
return;
|
|
}
|
|
const el = (e.target as HTMLElement | null)?.closest?.("a[href]");
|
|
if (!el) return;
|
|
const href = el.getAttribute("href");
|
|
if (!href || href.startsWith("http") || href.startsWith("mailto:") || href.startsWith("#")) {
|
|
return;
|
|
}
|
|
if (!href.startsWith("/")) return;
|
|
const nextPath = href.split("?")[0];
|
|
if (nextPath === window.location.pathname && href === `${window.location.pathname}${window.location.search}`) {
|
|
return;
|
|
}
|
|
setActive(true);
|
|
};
|
|
document.addEventListener("click", onClick, true);
|
|
return () => document.removeEventListener("click", onClick, true);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!active) return;
|
|
const t = window.setTimeout(() => setActive(false), 350);
|
|
return () => window.clearTimeout(t);
|
|
}, [pathname, searchParams, active]);
|
|
|
|
if (!active) return null;
|
|
|
|
return (
|
|
<div className="pointer-events-none fixed inset-x-0 top-0 z-[200] h-[3px] overflow-hidden" aria-hidden>
|
|
<div
|
|
className="h-full w-full bg-primary"
|
|
style={{
|
|
animation: "ty-route-progress 0.85s ease-in-out infinite",
|
|
transformOrigin: "right center",
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function RouteProgress() {
|
|
return (
|
|
<Suspense fallback={null}>
|
|
<RouteProgressInner />
|
|
</Suspense>
|
|
);
|
|
}
|