From 889dfca36cd739c2a02241386ef46402b3e5ed4c Mon Sep 17 00:00:00 2001
From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
Date: Sat, 7 Mar 2026 07:43:00 -0600
Subject: [PATCH] Frontend fixes (#22309)
* prevent unnecessary reloads in useUserPersistence hook
* always render ProtectedRoute (handling undefined roles internally) and add Suspense fallback
* add missing i18n namespaces
react 19 enforces Suspense more strictly, so components using useTranslation() with unloaded namespaces would suspend, blanking the content behind the empty Suspense fallback
* add missing namespace
* remove unneeded
* remove modal from actions dropdown
---
web/src/App.tsx | 29 +++++++------------
web/src/components/auth/ProtectedRoute.tsx | 9 +++++-
.../components/overlay/ActionsDropdown.tsx | 2 +-
web/src/hooks/use-user-persistence.ts | 5 ++++
web/src/utils/i18n.ts | 18 ++++++++----
5 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/web/src/App.tsx b/web/src/App.tsx
index 21babc2b9..01c415de4 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -11,7 +11,6 @@ import { Redirect } from "./components/navigation/Redirect";
import { cn } from "./lib/utils";
import { isPWA } from "./utils/isPWA";
import ProtectedRoute from "@/components/auth/ProtectedRoute";
-import { AuthProvider } from "@/context/auth-context";
import useSWR from "swr";
import { FrigateConfig } from "./types/frigateConfig";
import ActivityIndicator from "@/components/indicators/activity-indicator";
@@ -39,13 +38,11 @@ function App() {
return (
-
-
-
- {config?.safe_mode ? : }
-
-
-
+
+
+ {config?.safe_mode ? : }
+
+
);
}
@@ -85,17 +82,13 @@ function DefaultAppView() {
: "bottom-8 left-[52px]",
)}
>
-
+
+ }
+ >
-
- ) : (
-
- )
- }
- >
+ }>
} />
} />
} />
diff --git a/web/src/components/auth/ProtectedRoute.tsx b/web/src/components/auth/ProtectedRoute.tsx
index a7d1b3596..bcfa8fdf3 100644
--- a/web/src/components/auth/ProtectedRoute.tsx
+++ b/web/src/components/auth/ProtectedRoute.tsx
@@ -10,7 +10,7 @@ import {
export default function ProtectedRoute({
requiredRoles,
}: {
- requiredRoles: string[];
+ requiredRoles?: string[];
}) {
const { auth } = useContext(AuthContext);
@@ -36,6 +36,13 @@ export default function ProtectedRoute({
);
}
+ // Wait for config to provide required roles
+ if (!requiredRoles) {
+ return (
+
+ );
+ }
+
if (auth.isLoading) {
return (
diff --git a/web/src/components/overlay/ActionsDropdown.tsx b/web/src/components/overlay/ActionsDropdown.tsx
index d73ae71e3..9ddb0bd35 100644
--- a/web/src/components/overlay/ActionsDropdown.tsx
+++ b/web/src/components/overlay/ActionsDropdown.tsx
@@ -20,7 +20,7 @@ export default function ActionsDropdown({
const { t } = useTranslation(["components/dialog", "views/replay", "common"]);
return (
-
+