Skip to content
Merged
Show file tree
Hide file tree
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
20 changes: 10 additions & 10 deletions app/catchall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import Section from "@packages/components/Section";
import { LocalizedText } from "@packages/locale/context";

function PageNotFound() {
return (
<Section className="px-0 py-56 h-[100vh]">
<div className="flex flex-col items-center space-y-2 text-center md:space-y-4">
<h1 className="font-semibold text-3xl leading-tight sm:text-4xl">
<LocalizedText token="not-found.title" />
</h1>
<p className="text-purple-400">(╯°□°)╯︵ ┻━┻</p>
</div>
</Section>
);
return (
<Section className="px-0 py-56 h-[100vh]">
<div className="flex flex-col items-center space-y-2 text-center md:space-y-4">
<h1 className="font-semibold text-3xl leading-tight sm:text-4xl">
<LocalizedText token="not-found.title" />
</h1>
<p className="text-purple-400">(╯°□°)╯︵ ┻━┻</p>
</div>
</Section>
);
}

export default PageNotFound;
4 changes: 2 additions & 2 deletions app/cookies.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { MAX_COOKIE_AGE } from "@packages/contants";
import { createCookie } from "react-router";

export const selectedTheme = createCookie("selected-theme", {
path: "/",
maxAge: MAX_COOKIE_AGE,
path: "/",
maxAge: MAX_COOKIE_AGE,
});
62 changes: 31 additions & 31 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
import { isbot } from "isbot";
import { renderToReadableStream } from "react-dom/server";
import {
type AppLoadContext,
type EntryContext,
ServerRouter,
type AppLoadContext,
type EntryContext,
ServerRouter,
} from "react-router";

const ABORT_DELAY = 5000;

export default async function handleRequest(
request: Request,
status: number,
headers: Headers,
routerContext: EntryContext,
// This is ignored so we can keep it in the template for visibility. Feel
// free to delete this parameter in your app if you're not using it!
_loadContext: AppLoadContext,
request: Request,
status: number,
headers: Headers,
routerContext: EntryContext,
// This is ignored so we can keep it in the template for visibility. Feel
// free to delete this parameter in your app if you're not using it!
_loadContext: AppLoadContext,
) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ABORT_DELAY);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ABORT_DELAY);

const body = await renderToReadableStream(
<ServerRouter context={routerContext} url={request.url} />,
{
signal: controller.signal,
onError(error: unknown) {
if (!controller.signal.aborted) {
// Log streaming rendering errors from inside the shell
console.error(error);
}
status = 500;
},
},
);
const body = await renderToReadableStream(
<ServerRouter context={routerContext} url={request.url} />,
{
signal: controller.signal,
onError(error: unknown) {
if (!controller.signal.aborted) {
// Log streaming rendering errors from inside the shell
console.error(error);
}
status = 500;
},
},
);

body.allReady.then(() => clearTimeout(timeoutId));
body.allReady.then(() => clearTimeout(timeoutId));

if (isbot(request.headers.get("user-agent") || "")) {
await body.allReady;
}
if (isbot(request.headers.get("user-agent") || "")) {
await body.allReady;
}

headers.set("Content-Type", "text/html");
return new Response(body, { headers, status });
headers.set("Content-Type", "text/html");
return new Response(body, { headers, status });
}
152 changes: 76 additions & 76 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { Theme } from "@packages/utils/theme";
import {
type ActionFunctionArgs,
Links,
type LinksFunction,
type LoaderFunctionArgs,
Meta,
Outlet,
Scripts,
ScrollRestoration,
isRouteErrorResponse,
redirect,
useLoaderData,
useRouteError,
type ActionFunctionArgs,
Links,
type LinksFunction,
type LoaderFunctionArgs,
Meta,
Outlet,
Scripts,
ScrollRestoration,
isRouteErrorResponse,
redirect,
useLoaderData,
useRouteError,
} from "react-router";

import "./tailwind.css";
Expand All @@ -20,91 +20,91 @@ import Providers from "@packages/components/Providers";
import { selectedTheme } from "./cookies.server";

export const links: LinksFunction = () => [
// NOTE: Example of blocking scripts
//
//{ rel: "preconnect", href: "https://fonts.googleapis.com" },
//{
// rel: "preconnect",
// href: "https://fonts.gstatic.com",
// crossOrigin: "anonymous",
//},
//{
// rel: "stylesheet",
// href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
//},
// NOTE: Example of blocking scripts
//
//{ rel: "preconnect", href: "https://fonts.googleapis.com" },
//{
// rel: "preconnect",
// href: "https://fonts.gstatic.com",
// crossOrigin: "anonymous",
//},
//{
// rel: "stylesheet",
// href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
//},
];

export async function loader({ request }: LoaderFunctionArgs) {
const cookieHeader = request.headers.get("Cookie");
const rawTheme = await selectedTheme.parse(cookieHeader);
const theme: Theme = rawTheme ?? "system";
const cookieHeader = request.headers.get("Cookie");
const rawTheme = await selectedTheme.parse(cookieHeader);
const theme: Theme = rawTheme ?? "system";

return { theme };
return { theme };
}

export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const theme = (formData.get("theme") ?? "system") as Theme;
const formData = await request.formData();
const theme = (formData.get("theme") ?? "system") as Theme;

const redirectUrl = request.headers.get("referer") ?? "/";
const redirectUrl = request.headers.get("referer") ?? "/";

return redirect(redirectUrl, {
headers: {
"Set-Cookie": await selectedTheme.serialize(theme),
},
});
return redirect(redirectUrl, {
headers: {
"Set-Cookie": await selectedTheme.serialize(theme),
},
});
}

export function Layout({ children }: React.PropsWithChildren) {
const { theme } = useLoaderData<typeof loader>();
const { theme } = useLoaderData<typeof loader>();

return (
<html lang="en" data-theme={theme} className={theme}>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
return (
<html lang="en" data-theme={theme} className={theme}>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />

<Metadata />
</head>
<body>
<Providers>{children}</Providers>
<ScrollRestoration />
<Scripts />
</body>
</html>
);
<Metadata />
</head>
<body>
<Providers>{children}</Providers>
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export default function App() {
return <Outlet />;
return <Outlet />;
}

export function ErrorBoundary() {
const error = useRouteError();
const error = useRouteError();

if (isRouteErrorResponse(error)) {
return (
<div>
<h1>
{error.status} {error.statusText}
</h1>
<p>{error.data}</p>
</div>
);
}
if (isRouteErrorResponse(error)) {
return (
<div>
<h1>
{error.status} {error.statusText}
</h1>
<p>{error.data}</p>
</div>
);
}

if (error instanceof Error) {
return (
<div>
<h1>Error</h1>
<p>{error.message}</p>
<p>The stack trace is:</p>
<pre>{error.stack}</pre>
</div>
);
}
if (error instanceof Error) {
return (
<div>
<h1>Error</h1>
<p>{error.message}</p>
<p>The stack trace is:</p>
<pre>{error.stack}</pre>
</div>
);
}

return <h1>Unknown Error</h1>;
return <h1>Unknown Error</h1>;
}
7 changes: 6 additions & 1 deletion app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ import { flatRoutes } from "@react-router/fs-routes";

const routes = await flatRoutes();

export default [...routes, route("/*", "./catchall.tsx")] satisfies RouteConfig;
const allRoutes = [
...routes,
route("/*", "./catchall.tsx"),
] satisfies RouteConfig;

export default allRoutes;
12 changes: 6 additions & 6 deletions app/routes/[robots.txt].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ Sitemap: ${SITEMAP_URL}
`;

export function loader() {
return new Response(ROBOT_CONTENT, {
status: 200,
headers: {
"Content-Type": "text/plain",
},
});
return new Response(ROBOT_CONTENT, {
status: 200,
headers: {
"Content-Type": "text/plain",
},
});
}
18 changes: 9 additions & 9 deletions app/routes/[sitemap.xml].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const SITEMAP_CONTENT = `
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
${SITEMAP_URLS.map(
(url) => `
(url) => `
<loc>${url.loc}</loc>
<lastmod>${url.lastmod}</lastmod>
<priority>${url.priority}</priority>
Expand All @@ -15,12 +15,12 @@ ${SITEMAP_URLS.map(
`;

export function loader() {
return new Response(SITEMAP_CONTENT, {
status: 200,
headers: {
"Content-Type": "application/xml",
"xml-version": "1.0",
encoding: "UTF-8",
},
});
return new Response(SITEMAP_CONTENT, {
status: 200,
headers: {
"Content-Type": "application/xml",
"xml-version": "1.0",
encoding: "UTF-8",
},
});
}
Loading
Loading