;
+const searchSchema = z
+ .object({
+ inactive: z.literal(true).optional(),
+ })
+ .and(anyPaginationSchema);
export const Route = createFileRoute("/_account/sessions/browsers")({
- // We paginate backwards, so we need to validate the `last` parameter by default
- validateSearch: paginationSchema.catch(DEFAULT_PAGE).and(searchSchema),
+ validateSearch: zodSearchValidator(searchSchema),
- loaderDeps: ({ search }): Pagination & Search =>
- paginationSchema.and(searchSchema).parse(search),
+ loaderDeps: ({ search: { inactive, ...pagination } }) => ({
+ inactive,
+ pagination: normalizePagination(pagination, PAGE_SIZE, "backward"),
+ }),
async loader({
context,
- deps: { inactive, ...pagination },
+ deps: { inactive, pagination },
abortController: { signal },
}) {
const variables = {
@@ -95,6 +92,4 @@ export const Route = createFileRoute("/_account/sessions/browsers")({
if (result.data?.viewerSession?.__typename !== "BrowserSession")
throw notFound();
},
-
- component: () => Hello /_account/sessions/browsers!
,
});
diff --git a/frontend/src/routes/_account.sessions.index.lazy.tsx b/frontend/src/routes/_account.sessions.index.lazy.tsx
index 7f72aa377..19e37a028 100644
--- a/frontend/src/routes/_account.sessions.index.lazy.tsx
+++ b/frontend/src/routes/_account.sessions.index.lazy.tsx
@@ -16,13 +16,12 @@ import EmptyState from "../components/EmptyState";
import Filter from "../components/Filter";
import OAuth2Session from "../components/OAuth2Session";
import BrowserSessionsOverview from "../components/UserSessionsOverview/BrowserSessionsOverview";
-import { type BackwardPagination, usePages } from "../pagination";
+import { usePages } from "../pagination";
import { getNinetyDaysAgo } from "../utils/dates";
import { QUERY, LIST_QUERY } from "./_account.sessions.index";
const PAGE_SIZE = 6;
-const DEFAULT_PAGE: BackwardPagination = { last: PAGE_SIZE };
// A type-safe way to ensure we've handled all session types
const unknownSessionType = (type: never): never => {
@@ -35,7 +34,7 @@ export const Route = createLazyFileRoute("/_account/sessions/")({
function Sessions(): React.ReactElement {
const { t } = useTranslation();
- const { inactive, ...pagination } = Route.useLoaderDeps();
+ const { inactive, pagination } = Route.useLoaderDeps();
const [overview] = useQuery({ query: QUERY });
if (overview.error) throw overview.error;
const user =
@@ -73,7 +72,7 @@ function Sessions(): React.ReactElement {
{t("frontend.last_active.inactive_90_days")}
diff --git a/frontend/src/routes/_account.sessions.index.tsx b/frontend/src/routes/_account.sessions.index.tsx
index 79bf8e820..b3f76f817 100644
--- a/frontend/src/routes/_account.sessions.index.tsx
+++ b/frontend/src/routes/_account.sessions.index.tsx
@@ -5,18 +5,14 @@
// Please see LICENSE in the repository root for full details.
import { createFileRoute, notFound } from "@tanstack/react-router";
+import { zodSearchValidator } from "@tanstack/router-zod-adapter";
import * as z from "zod";
import { graphql } from "../gql";
-import {
- type BackwardPagination,
- type Pagination,
- paginationSchema,
-} from "../pagination";
+import { anyPaginationSchema, normalizePagination } from "../pagination";
import { getNinetyDaysAgo } from "../utils/dates";
const PAGE_SIZE = 6;
-const DEFAULT_PAGE: BackwardPagination = { last: PAGE_SIZE };
export const QUERY = graphql(/* GraphQL */ `
query SessionsOverviewQuery {
@@ -74,22 +70,23 @@ export const LIST_QUERY = graphql(/* GraphQL */ `
}
`);
-const searchSchema = z.object({
- inactive: z.literal(true).optional().catch(undefined),
-});
-
-type Search = z.infer;
+const searchSchema = z
+ .object({
+ inactive: z.literal(true).optional(),
+ })
+ .and(anyPaginationSchema);
export const Route = createFileRoute("/_account/sessions/")({
- // We paginate backwards, so we need to validate the `last` parameter by default
- validateSearch: paginationSchema.catch(DEFAULT_PAGE).and(searchSchema),
+ validateSearch: zodSearchValidator(searchSchema),
- loaderDeps: ({ search }): Pagination & Search =>
- paginationSchema.and(searchSchema).parse(search),
+ loaderDeps: ({ search: { inactive, ...pagination } }) => ({
+ inactive,
+ pagination: normalizePagination(pagination, PAGE_SIZE, "backward"),
+ }),
async loader({
context,
- deps: { inactive, ...pagination },
+ deps: { inactive, pagination },
abortController: { signal },
}) {
const variables = {
diff --git a/frontend/src/routes/devices.$.tsx b/frontend/src/routes/devices.$.tsx
index f8ec3abd7..693a5639b 100644
--- a/frontend/src/routes/devices.$.tsx
+++ b/frontend/src/routes/devices.$.tsx
@@ -78,9 +78,7 @@ function NotFound(): React.ReactElement {
title={t("frontend.session_detail.alert.title", { deviceId })}
>
{t("frontend.session_detail.alert.text")}
-
- {t("frontend.session_detail.alert.button")}
-
+ {t("frontend.session_detail.alert.button")}
);
diff --git a/frontend/src/routes/password.recovery.index.tsx b/frontend/src/routes/password.recovery.index.tsx
index 9ea479075..dd72a3a69 100644
--- a/frontend/src/routes/password.recovery.index.tsx
+++ b/frontend/src/routes/password.recovery.index.tsx
@@ -5,6 +5,8 @@
// Please see LICENSE in the repository root for full details.
import { createFileRoute } from "@tanstack/react-router";
+import { zodSearchValidator } from "@tanstack/router-zod-adapter";
+import * as z from "zod";
import { graphql } from "../gql";
@@ -17,11 +19,13 @@ export const QUERY = graphql(/* GraphQL */ `
}
`);
+const schema = z.object({
+ ticket: z.string(),
+});
+
export const Route = createFileRoute("/password/recovery/")({
- validateSearch: (search) =>
- search as {
- ticket: string;
- },
+ validateSearch: zodSearchValidator(schema),
+
async loader({ context, abortController: { signal } }) {
const queryResult = await context.client.query(
QUERY,
diff --git a/frontend/src/routes/reset-cross-signing.tsx b/frontend/src/routes/reset-cross-signing.tsx
index 78d624ee1..9bd192822 100644
--- a/frontend/src/routes/reset-cross-signing.tsx
+++ b/frontend/src/routes/reset-cross-signing.tsx
@@ -5,6 +5,7 @@
// Please see LICENSE in the repository root for full details.
import { notFound, createFileRoute } from "@tanstack/react-router";
+import { zodSearchValidator } from "@tanstack/router-zod-adapter";
import * as z from "zod";
import { graphql } from "../gql";
@@ -35,5 +36,5 @@ export const Route = createFileRoute("/reset-cross-signing")({
if (viewer.data?.viewer.__typename !== "User") throw notFound();
},
- validateSearch: searchSchema,
+ validateSearch: zodSearchValidator(searchSchema),
});