49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { Navigate, Outlet } from "react-router-dom";
|
|
import { match } from "ts-pattern";
|
|
import { useMaybeUser } from "../providers/UserStoreProvider";
|
|
import { LoadingSpinner } from "./LoadingSpinner";
|
|
|
|
interface ProtectedRouteProps {
|
|
fallback?: string;
|
|
shouldRedirectToCurrentPage?: boolean;
|
|
}
|
|
|
|
export const ProtectedRoute = ({ fallback, shouldRedirectToCurrentPage }: ProtectedRouteProps) => {
|
|
const user = useMaybeUser();
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(() => {
|
|
setIsLoading(false);
|
|
}, 500);
|
|
return () => clearTimeout(timer);
|
|
}, [user, fallback]);
|
|
|
|
let status: "loading" | "should-land-user" | "should-redirect" | "should-pass" = "loading";
|
|
|
|
const isFirstTimeUser = localStorage.getItem("xtablo-has-seen-landing-page") === null;
|
|
|
|
if (isLoading) {
|
|
status = "loading";
|
|
} else if (!user && isFirstTimeUser) {
|
|
status = "should-land-user";
|
|
} else if (!user) {
|
|
status = "should-redirect";
|
|
} else {
|
|
status = "should-pass";
|
|
}
|
|
|
|
const redirectUrl = shouldRedirectToCurrentPage
|
|
? `${fallback ?? "/login"}?redirect=${encodeURIComponent(
|
|
`${window.location.pathname}${window.location.search}`
|
|
)}`
|
|
: (fallback ?? "/login");
|
|
|
|
return match(status)
|
|
.with("loading", () => <LoadingSpinner />)
|
|
.with("should-land-user", () => <Navigate to="/landing" replace />)
|
|
.with("should-redirect", () => <Navigate to={redirectUrl} replace />)
|
|
.with("should-pass", () => <Outlet />)
|
|
.exhaustive();
|
|
};
|