mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-10 02:29:19 +03:00
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
This commit is contained in:
parent
dda9f7bfed
commit
889dfca36c
@ -11,7 +11,6 @@ import { Redirect } from "./components/navigation/Redirect";
|
|||||||
import { cn } from "./lib/utils";
|
import { cn } from "./lib/utils";
|
||||||
import { isPWA } from "./utils/isPWA";
|
import { isPWA } from "./utils/isPWA";
|
||||||
import ProtectedRoute from "@/components/auth/ProtectedRoute";
|
import ProtectedRoute from "@/components/auth/ProtectedRoute";
|
||||||
import { AuthProvider } from "@/context/auth-context";
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { FrigateConfig } from "./types/frigateConfig";
|
import { FrigateConfig } from "./types/frigateConfig";
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
@ -39,13 +38,11 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Providers>
|
<Providers>
|
||||||
<AuthProvider>
|
|
||||||
<BrowserRouter basename={window.baseUrl}>
|
<BrowserRouter basename={window.baseUrl}>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{config?.safe_mode ? <SafeAppView /> : <DefaultAppView />}
|
{config?.safe_mode ? <SafeAppView /> : <DefaultAppView />}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</AuthProvider>
|
|
||||||
</Providers>
|
</Providers>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -85,17 +82,13 @@ function DefaultAppView() {
|
|||||||
: "bottom-8 left-[52px]",
|
: "bottom-8 left-[52px]",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Suspense>
|
<Suspense
|
||||||
<Routes>
|
fallback={
|
||||||
<Route
|
|
||||||
element={
|
|
||||||
mainRouteRoles ? (
|
|
||||||
<ProtectedRoute requiredRoles={mainRouteRoles} />
|
|
||||||
) : (
|
|
||||||
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<Routes>
|
||||||
|
<Route element={<ProtectedRoute requiredRoles={mainRouteRoles} />}>
|
||||||
<Route index element={<Live />} />
|
<Route index element={<Live />} />
|
||||||
<Route path="/review" element={<Events />} />
|
<Route path="/review" element={<Events />} />
|
||||||
<Route path="/explore" element={<Explore />} />
|
<Route path="/explore" element={<Explore />} />
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
export default function ProtectedRoute({
|
export default function ProtectedRoute({
|
||||||
requiredRoles,
|
requiredRoles,
|
||||||
}: {
|
}: {
|
||||||
requiredRoles: string[];
|
requiredRoles?: string[];
|
||||||
}) {
|
}) {
|
||||||
const { auth } = useContext(AuthContext);
|
const { auth } = useContext(AuthContext);
|
||||||
|
|
||||||
@ -36,6 +36,13 @@ export default function ProtectedRoute({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for config to provide required roles
|
||||||
|
if (!requiredRoles) {
|
||||||
|
return (
|
||||||
|
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (auth.isLoading) {
|
if (auth.isLoading) {
|
||||||
return (
|
return (
|
||||||
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export default function ActionsDropdown({
|
|||||||
const { t } = useTranslation(["components/dialog", "views/replay", "common"]);
|
const { t } = useTranslation(["components/dialog", "views/replay", "common"]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu modal={false}>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
|
|||||||
@ -116,6 +116,11 @@ export function useUserPersistence<S>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip reload if we're already loaded for this key
|
||||||
|
if (loadedKeyRef.current === namespacedKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset state when key changes - this prevents stale writes
|
// Reset state when key changes - this prevents stale writes
|
||||||
loadedKeyRef.current = null;
|
loadedKeyRef.current = null;
|
||||||
migrationAttemptedRef.current = false;
|
migrationAttemptedRef.current = false;
|
||||||
|
|||||||
@ -40,23 +40,31 @@ i18n
|
|||||||
"common",
|
"common",
|
||||||
"objects",
|
"objects",
|
||||||
"audio",
|
"audio",
|
||||||
|
"components/auth",
|
||||||
"components/camera",
|
"components/camera",
|
||||||
"components/dialog",
|
"components/dialog",
|
||||||
"components/filter",
|
"components/filter",
|
||||||
"components/icons",
|
"components/icons",
|
||||||
|
"components/input",
|
||||||
"components/player",
|
"components/player",
|
||||||
"views/events",
|
|
||||||
"views/chat",
|
"views/chat",
|
||||||
|
"views/classificationModel",
|
||||||
|
"views/configEditor",
|
||||||
|
"views/events",
|
||||||
"views/explore",
|
"views/explore",
|
||||||
|
"views/exports",
|
||||||
|
"views/faceLibrary",
|
||||||
"views/live",
|
"views/live",
|
||||||
|
"views/motionSearch",
|
||||||
|
"views/recording",
|
||||||
|
"views/replay",
|
||||||
|
"views/search",
|
||||||
"views/settings",
|
"views/settings",
|
||||||
"views/system",
|
"views/system",
|
||||||
"views/exports",
|
|
||||||
"views/explore",
|
|
||||||
"config/global",
|
|
||||||
"config/cameras",
|
"config/cameras",
|
||||||
"config/validation",
|
"config/global",
|
||||||
"config/groups",
|
"config/groups",
|
||||||
|
"config/validation",
|
||||||
],
|
],
|
||||||
defaultNS: "common",
|
defaultNS: "common",
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user