mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-07-14 18:15:56 -04:00
feat(web): add page titles, staleTime, and lift auth redirect into layout
- Add `head()` with document titles to dashboard, explore, settings, setup, login, register, and person detail routes - Return `personName` from the person detail loader so the title can be set server-side instead of after data loads - Add `staleTime` to all loader routes (30s for dashboard/settings, 60s for explore/titles/people) - Move the "redirect if already authenticated" `beforeLoad` guard from individual login/register routes into the shared `_auth` layout so it runs once for all unauthenticated routes - Add a Cloud deploy section to the README with a one-click Railway button
This commit is contained in:
@@ -12,6 +12,8 @@ import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { orpc } from "@/lib/orpc/client";
|
||||
|
||||
export const Route = createFileRoute("/_app/dashboard")({
|
||||
staleTime: 30_000,
|
||||
head: () => ({ meta: [{ title: "Dashboard — Sofa" }] }),
|
||||
loader: async ({ context }) => {
|
||||
await Promise.all([
|
||||
context.queryClient.ensureQueryData(orpc.dashboard.stats.queryOptions()),
|
||||
|
||||
@@ -11,6 +11,8 @@ import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { orpc } from "@/lib/orpc/client";
|
||||
|
||||
export const Route = createFileRoute("/_app/explore")({
|
||||
staleTime: 60_000,
|
||||
head: () => ({ meta: [{ title: "Explore — Sofa" }] }),
|
||||
loader: async ({ context }) => {
|
||||
await Promise.all([
|
||||
context.queryClient.ensureQueryData(
|
||||
|
||||
@@ -5,8 +5,9 @@ import { PersonDetailClient, PersonDetailSkeleton } from "@/components/people/pe
|
||||
import { orpc } from "@/lib/orpc/client";
|
||||
|
||||
export const Route = createFileRoute("/_app/people/$id")({
|
||||
staleTime: 60_000,
|
||||
loader: async ({ params, context }) => {
|
||||
await context.queryClient.ensureInfiniteQueryData(
|
||||
const data = await context.queryClient.ensureInfiniteQueryData(
|
||||
orpc.people.detail.infiniteOptions({
|
||||
input: (pageParam: number) => ({
|
||||
id: params.id,
|
||||
@@ -18,10 +19,11 @@ export const Route = createFileRoute("/_app/people/$id")({
|
||||
lastPage.page < lastPage.totalPages ? lastPage.page + 1 : undefined,
|
||||
}),
|
||||
);
|
||||
return { personName: data.pages[0]?.person.name };
|
||||
},
|
||||
head: ({ loaderData: _loaderData, params: _params }) => {
|
||||
// Title is set by the component after data loads
|
||||
return {};
|
||||
head: ({ loaderData }) => {
|
||||
if (!loaderData?.personName) return {};
|
||||
return { meta: [{ title: `${loaderData.personName} — Sofa` }] };
|
||||
},
|
||||
pendingComponent: () => <PersonDetailSkeleton />,
|
||||
notFoundComponent: PersonNotFound,
|
||||
|
||||
@@ -28,6 +28,8 @@ import { orpc } from "@/lib/orpc/client";
|
||||
const GITHUB_REPO = "jakejarvis/sofa";
|
||||
|
||||
export const Route = createFileRoute("/_app/settings")({
|
||||
staleTime: 30_000,
|
||||
head: () => ({ meta: [{ title: "Settings — Sofa" }] }),
|
||||
loader: async ({ context }) => {
|
||||
const promises: Promise<unknown>[] = [
|
||||
context.queryClient.ensureQueryData(orpc.integrations.list.queryOptions()),
|
||||
|
||||
@@ -15,6 +15,7 @@ import { orpc } from "@/lib/orpc/client";
|
||||
import { getThemeCssProperties } from "@/lib/theme";
|
||||
|
||||
export const Route = createFileRoute("/_app/titles/$id")({
|
||||
staleTime: 60_000,
|
||||
loader: async ({ params, context }) => {
|
||||
const [titleResult, userInfo] = await Promise.all([
|
||||
context.queryClient.ensureQueryData(
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
||||
import { createFileRoute, redirect, Outlet } from "@tanstack/react-router";
|
||||
|
||||
import { authClient } from "@/lib/auth/client";
|
||||
|
||||
export const Route = createFileRoute("/_auth")({
|
||||
beforeLoad: async () => {
|
||||
const { data: session } = await authClient.getSession();
|
||||
if (session) throw redirect({ to: "/dashboard" });
|
||||
},
|
||||
component: AuthLayout,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
import { AuthForm } from "@/components/auth-form";
|
||||
import { authClient } from "@/lib/auth/client";
|
||||
import { client } from "@/lib/orpc/client";
|
||||
|
||||
export const Route = createFileRoute("/_auth/login")({
|
||||
beforeLoad: async () => {
|
||||
const { data: session } = await authClient.getSession();
|
||||
if (session) throw redirect({ to: "/dashboard" });
|
||||
|
||||
const authConfig = await client.system.authConfig({});
|
||||
if (authConfig.userCount === 0) throw redirect({ to: "/register" });
|
||||
|
||||
return { authConfig };
|
||||
},
|
||||
head: () => ({ meta: [{ title: "Sign in — Sofa" }] }),
|
||||
component: LoginPage,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
import { IconLock } from "@tabler/icons-react";
|
||||
import { createFileRoute, Link, redirect } from "@tanstack/react-router";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
|
||||
import { AuthForm } from "@/components/auth-form";
|
||||
import { authClient } from "@/lib/auth/client";
|
||||
import { client } from "@/lib/orpc/client";
|
||||
|
||||
export const Route = createFileRoute("/_auth/register")({
|
||||
beforeLoad: async () => {
|
||||
const { data: session } = await authClient.getSession();
|
||||
if (session) throw redirect({ to: "/dashboard" });
|
||||
|
||||
const authConfig = await client.system.authConfig({});
|
||||
return { authConfig };
|
||||
},
|
||||
head: () => ({ meta: [{ title: "Create account — Sofa" }] }),
|
||||
component: RegisterPage,
|
||||
});
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ const envSnippets = [
|
||||
];
|
||||
|
||||
export const Route = createFileRoute("/setup")({
|
||||
head: () => ({ meta: [{ title: "Setup — Sofa" }] }),
|
||||
beforeLoad: async () => {
|
||||
const info = await client.system.publicInfo({});
|
||||
if (info.tmdbConfigured) throw redirect({ to: "/" });
|
||||
|
||||
Reference in New Issue
Block a user