Miscellaneous Fixes (#22890)

* only link to profile settings in status bar for admin users

* use hasFullCameraAccess for group filtering

* add custom export args to record docs

* update recordings docs

* prevent review WS handler from poisoning SWR cache before initial fetch completes
This commit is contained in:
Josh Hawkins
2026-04-16 10:10:03 -05:00
committed by GitHub
parent d830d47c9b
commit a94d1b5d9e
5 changed files with 145 additions and 23 deletions

View File

@@ -7,6 +7,7 @@ import useStats, { useAutoFrigateStats } from "@/hooks/use-stats";
import { cn } from "@/lib/utils";
import type { ProfilesApiResponse } from "@/types/profile";
import { getProfileColor } from "@/utils/profileColors";
import { useIsAdmin } from "@/hooks/use-is-admin";
import { useContext, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import useSWR from "swr";
@@ -18,6 +19,7 @@ import { Link } from "react-router-dom";
export default function Statusbar() {
const { t } = useTranslation(["views/system"]);
const isAdmin = useIsAdmin();
const { messages, addMessage, clearMessages } = useContext(
StatusBarMessagesContext,
@@ -154,9 +156,23 @@ export default function Statusbar() {
</Link>
);
})}
{activeProfile && (
<Link to="/settings?page=profiles">
<div className="flex cursor-pointer items-center gap-2 text-sm hover:underline">
{activeProfile &&
(isAdmin ? (
<Link to="/settings?page=profiles">
<div className="flex cursor-pointer items-center gap-2 text-sm hover:underline">
<span
className={cn(
"size-2 shrink-0 rounded-full",
activeProfile.color.dot,
)}
/>
<span className="max-w-[150px] truncate">
{activeProfile.friendlyName}
</span>
</div>
</Link>
) : (
<div className="flex items-center gap-2 text-sm">
<span
className={cn(
"size-2 shrink-0 rounded-full",
@@ -167,8 +183,7 @@ export default function Statusbar() {
{activeProfile.friendlyName}
</span>
</div>
</Link>
)}
))}
</div>
<div className="no-scrollbar flex h-full max-w-[50%] items-center gap-2 overflow-x-auto">
{Object.entries(messages).length === 0 ? (

View File

@@ -89,6 +89,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
const { t } = useTranslation(["components/camera"]);
const { data: config } = useSWR<FrigateConfig>("config");
const allowedCameras = useAllowedCameras();
const hasFullCameraAccess = useHasFullCameraAccess();
const isAdmin = useIsAdmin();
// tooltip
@@ -125,7 +126,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
const allGroups = Object.entries(config.camera_groups);
// If custom role, filter out groups where user has no accessible cameras
if (!isAdmin) {
if (!hasFullCameraAccess) {
return allGroups
.filter(([, groupConfig]) => {
// Check if user has access to at least one camera in this group
@@ -137,7 +138,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
}
return allGroups.sort((a, b) => a[1].order - b[1].order);
}, [config, allowedCameras, isAdmin]);
}, [config, allowedCameras, hasFullCameraAccess]);
// add group

View File

@@ -334,7 +334,7 @@ export default function Events() {
);
useEffect(() => {
if (reviewUpdate?.type === "end") {
if (reviewUpdate?.type === "end" && reviews) {
updateSegments(
(data) => {
if (!data) return data;
@@ -348,6 +348,9 @@ export default function Events() {
new Map(prev).set(reviewUpdate.after.id, reviewUpdate.after),
);
}
// reviews is intentionally excluded - only used to guard against
// updating the SWR cache before data has loaded
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [reviewUpdate, updateSegments]);
const currentItems = useMemo(() => {