Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 29 additions & 49 deletions apps/dashboard/src/@/lib/DashboardRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,64 @@
"use client";

// eslint-disable-next-line no-restricted-imports
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
import {
Suspense,
type TransitionStartFunction,
useEffect,
useMemo,
useRef,
useState,
useSyncExternalStore,
useTransition,
} from "react";
import { createStore } from "./reactive";

// Using useDashboardRouter instead of useRouter gives us a nice progress bar on top of the page when navigating using router.push or router.replace

// using a store instead of context to avoid triggering re-renders on root component
const LoadingRouteHref = createStore<string | undefined>(undefined);
const RouteStartTransitionFn = createStore<TransitionStartFunction>((fn) =>
fn(),
);

export function useDashboardRouter() {
const startTransition = useSyncExternalStore(
RouteStartTransitionFn.subscribe,
RouteStartTransitionFn.getValue,
RouteStartTransitionFn.getValue,
);

const router = useRouter();
return useMemo(() => {
return {
...router,
replace(href: string, options?: { scroll?: boolean }) {
LoadingRouteHref.setValue(href);
router.replace(href, options);
startTransition(() => {
router.replace(href, options);
});
},
push(href: string, options?: { scroll?: boolean }) {
LoadingRouteHref.setValue(href);
router.push(href, options);
startTransition(() => {
router.push(href, options);
});
},
};
}, [router]);
}, [router, startTransition]);
}

function useRouterLoadingStatus() {
const loadingHref = useSyncExternalStore(
LoadingRouteHref.subscribe,
LoadingRouteHref.getValue,
LoadingRouteHref.getValue,
);
const pathname = usePathname();
const searchParams = useSearchParams();
const searchParamsStr = searchParams?.toString();

const routerHref = pathname + (searchParamsStr ? `?${searchParamsStr}` : "");
const isLoading = loadingHref && loadingHref !== routerHref;
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// reset loading on route load
export function DashboardRouterTopProgressBar() {
const [isRouteLoading, startTransition] = useTransition();
// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
if (!isLoading) {
LoadingRouteHref.setValue(undefined);
}
}, [isLoading]);

return isLoading;
}
RouteStartTransitionFn.setValue(startTransition);
}, []);

const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// Render this on root
function DashboardRouterTopProgressBarInner() {
const isLoading = useRouterLoadingStatus();
const [progress, setProgress] = useState(0);

const progressStartedRef = useRef(false);

// eslint-disable-next-line no-restricted-syntax
useEffect(() => {
if (!isLoading) {
if (!isRouteLoading) {
setProgress(0);
progressStartedRef.current = false;
return;
Expand All @@ -85,7 +74,7 @@ function DashboardRouterTopProgressBarInner() {

async function updateProgressBar(progress: number, delay: number) {
if (!isMounted) {
if (!isLoading) {
if (!isRouteLoading) {
setProgress(100);
}
return;
Expand All @@ -112,14 +101,14 @@ function DashboardRouterTopProgressBarInner() {
return () => {
isMounted = false;
};
}, [isLoading]);
}, [isRouteLoading]);

const width = isLoading ? progress : 100;
const width = isRouteLoading ? progress : 100;
return (
<span
className="fixed top-0 block h-[3px] bg-foreground"
style={{
opacity: isLoading ? "100" : "0",
opacity: isRouteLoading ? "100" : "0",
width: `${width}%`,
transition: width === 0 ? "none" : "width 0.2s ease, opacity 0.3s ease",
zIndex: "100000000",
Expand All @@ -128,12 +117,3 @@ function DashboardRouterTopProgressBarInner() {
/>
);
}

// need to wrap with suspense because of useSearchParams usage
export function DashboardRouterTopProgressBar() {
return (
<Suspense fallback={null}>
<DashboardRouterTopProgressBarInner />
</Suspense>
);
}
Loading