82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
import {
|
|
Outlet,
|
|
createFileRoute,
|
|
redirect,
|
|
useLocation,
|
|
} from "@tanstack/react-router";
|
|
import { useEffect } from "react";
|
|
import { setLastProjectId } from "@/client/lib/active-project";
|
|
import { useHostedAuthRouteGuard } from "@/client/features/auth/useHostedAuthRouteGuard";
|
|
import { FreePlanBanner } from "@/client/features/billing/FreePlanBanner";
|
|
import { useSubscribeRedirect } from "@/client/features/billing/useSubscribeRedirect";
|
|
import { useOnboardingRedirect } from "@/client/features/onboarding/useOnboardingRedirect";
|
|
import { getErrorCode } from "@/client/lib/error-messages";
|
|
import { AuthenticatedAppLayout } from "@/client/layout/AppShell";
|
|
import {
|
|
getCurrentAuthRedirectFromHref,
|
|
getSignInSearch,
|
|
} from "@/lib/auth-redirect";
|
|
import { getProjectAccess } from "@/serverFunctions/projects";
|
|
|
|
export const Route = createFileRoute("/_project/p/$projectId")({
|
|
beforeLoad: async ({ location, params }) => {
|
|
try {
|
|
await getProjectAccess({ data: { projectId: params.projectId } });
|
|
} catch (error) {
|
|
if (getErrorCode(error) === "UNAUTHENTICATED") {
|
|
throw redirect({
|
|
to: "/sign-in",
|
|
search: getSignInSearch(
|
|
getCurrentAuthRedirectFromHref(location.href),
|
|
),
|
|
replace: true,
|
|
});
|
|
}
|
|
|
|
throw redirect({ to: "/", replace: true });
|
|
}
|
|
},
|
|
pendingComponent: ProjectRoutePending,
|
|
component: ProjectLayout,
|
|
});
|
|
|
|
function ProjectLayout() {
|
|
const { projectId } = Route.useParams();
|
|
const authGate = useHostedAuthRouteGuard();
|
|
useOnboardingRedirect();
|
|
const subscribeGate = useSubscribeRedirect();
|
|
|
|
// Remember this as the last-visited project for the landing redirect.
|
|
// Settings is excluded: editing another project's settings is
|
|
// administration, not a context switch, so it shouldn't change which
|
|
// project the app opens next time.
|
|
const isSettingsPage = useLocation({
|
|
select: (l) => l.pathname.endsWith("/settings"),
|
|
});
|
|
useEffect(() => {
|
|
if (isSettingsPage) return;
|
|
setLastProjectId(projectId);
|
|
}, [projectId, isSettingsPage]);
|
|
|
|
if (!authGate.canRenderAuthenticatedContent || subscribeGate.isBlocking) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<AuthenticatedAppLayout
|
|
projectId={projectId}
|
|
banner={authGate.isHostedMode ? <FreePlanBanner /> : undefined}
|
|
>
|
|
<Outlet />
|
|
</AuthenticatedAppLayout>
|
|
);
|
|
}
|
|
|
|
function ProjectRoutePending() {
|
|
return (
|
|
<div className="flex h-full items-center justify-center">
|
|
<span className="loading loading-spinner loading-md" />
|
|
</div>
|
|
);
|
|
}
|