From 08be019bedea5a360df20332eab6184436041db4 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sat, 30 May 2026 22:35:03 -0500 Subject: [PATCH] Miscellaneous fixes (#23358) * improve visibility of blurred icon buttons * add motion search to history actions menu and mobile drawer * i18n * use pure css for motion search dialog video * defer profile restoration until subscribers are connected * change order of features in mobile review settings drawer --- frigate/app.py | 13 +++- web/public/locales/en/views/events.json | 2 +- .../components/button/BlurredIconButton.tsx | 4 +- .../components/overlay/ActionsDropdown.tsx | 14 ++++- .../overlay/MobileReviewSettingsDrawer.tsx | 62 +++++++++++------- web/src/pages/Events.tsx | 12 ++-- .../motion-search/MotionSearchDialog.tsx | 63 +------------------ web/src/views/recording/RecordingView.tsx | 8 +++ 8 files changed, 84 insertions(+), 94 deletions(-) diff --git a/frigate/app.py b/frigate/app.py index b2402d232..785dc2447 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -343,13 +343,21 @@ class FrigateApp: ) self.dispatcher.profile_manager = self.profile_manager + def restore_active_profile(self) -> None: + """Re-activate the persisted profile after subscribers are connected. + + ZMQ PUB/SUB drops messages with no subscribers, so activation must + run after every config_updater subscriber is up. + """ + if self.profile_manager is None: + return + persisted = ProfileManager.load_persisted_profile() if persisted and any( persisted in cam.profiles for cam in self.config.cameras.values() ): logger.info("Restoring persisted profile '%s'", persisted) - # don't clear runtime overrides here, restore_runtime_state() later - # in startup replays it on top of the activated profile + # runtime overrides are layered on top via restore_runtime_state() self.profile_manager.activate_profile( persisted, clear_runtime_overrides=False ) @@ -617,6 +625,7 @@ class FrigateApp: self.start_watchdog() # restore persisted runtime overrides on top of config + self.restore_active_profile() self.dispatcher.restore_runtime_state() self.init_auth() diff --git a/web/public/locales/en/views/events.json b/web/public/locales/en/views/events.json index 103d93b7e..f25e3022c 100644 --- a/web/public/locales/en/views/events.json +++ b/web/public/locales/en/views/events.json @@ -67,7 +67,7 @@ "needsReview": "Needs review", "securityConcern": "Security concern", "motionSearch": { - "menuItem": "Motion search", + "menuItem": "Motion Search", "openMenu": "Camera options" }, "motionPreviews": { diff --git a/web/src/components/button/BlurredIconButton.tsx b/web/src/components/button/BlurredIconButton.tsx index 8fe17f869..90ffa36ee 100644 --- a/web/src/components/button/BlurredIconButton.tsx +++ b/web/src/components/button/BlurredIconButton.tsx @@ -14,8 +14,8 @@ const BlurredIconButton = forwardRef( )} {...rest} > -
-
+
+
{children}
diff --git a/web/src/components/overlay/ActionsDropdown.tsx b/web/src/components/overlay/ActionsDropdown.tsx index 1bc750dd7..7f501a206 100644 --- a/web/src/components/overlay/ActionsDropdown.tsx +++ b/web/src/components/overlay/ActionsDropdown.tsx @@ -12,14 +12,21 @@ type ActionsDropdownProps = { onDebugReplayClick?: () => void; onExportClick: () => void; onShareTimestampClick: () => void; + onMotionSearchClick?: () => void; }; export default function ActionsDropdown({ onDebugReplayClick, onExportClick, onShareTimestampClick, + onMotionSearchClick, }: Readonly) { - const { t } = useTranslation(["components/dialog", "views/replay", "common"]); + const { t } = useTranslation([ + "components/dialog", + "views/replay", + "views/events", + "common", + ]); return ( @@ -42,6 +49,11 @@ export default function ActionsDropdown({ {t("recording.shareTimestamp.label", { ns: "components/dialog" })} + {onMotionSearchClick && ( + + {t("motionSearch.menuItem", { ns: "views/events" })} + + )} {onDebugReplayClick && ( {t("title", { ns: "views/replay" })} diff --git a/web/src/components/overlay/MobileReviewSettingsDrawer.tsx b/web/src/components/overlay/MobileReviewSettingsDrawer.tsx index c2daf6d05..273cb5946 100644 --- a/web/src/components/overlay/MobileReviewSettingsDrawer.tsx +++ b/web/src/components/overlay/MobileReviewSettingsDrawer.tsx @@ -3,7 +3,7 @@ import { baseUrl } from "@/api/baseUrl"; import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer"; import { Button } from "../ui/button"; import { FaArrowDown, FaCalendarAlt, FaCog, FaFilter } from "react-icons/fa"; -import { LuBug, LuShare2 } from "react-icons/lu"; +import { LuBug, LuSearch, LuShare2 } from "react-icons/lu"; import { TimeRange } from "@/types/timeline"; import { ExportContent, ExportPreviewDialog, ExportTab } from "./ExportDialog"; import { @@ -46,6 +46,7 @@ const DRAWER_FEATURES = [ "filter", "debug-replay", "share-timestamp", + "motion-search", ] as const; export type DrawerFeatures = (typeof DRAWER_FEATURES)[number]; const DEFAULT_DRAWER_FEATURES: DrawerFeatures[] = [ @@ -54,6 +55,7 @@ const DEFAULT_DRAWER_FEATURES: DrawerFeatures[] = [ "filter", "debug-replay", "share-timestamp", + "motion-search", ]; type MobileReviewSettingsDrawerProps = { @@ -75,6 +77,7 @@ type MobileReviewSettingsDrawerProps = { setDebugReplayMode?: (mode: ExportMode) => void; setDebugReplayRange?: (range: TimeRange | undefined) => void; onShareTimestamp?: (timestamp: number) => void; + onMotionSearch?: () => void; onUpdateFilter: (filter: ReviewFilter) => void; setRange: (range: TimeRange | undefined) => void; setMode: (mode: ExportMode) => void; @@ -99,6 +102,7 @@ export default function MobileReviewSettingsDrawer({ setDebugReplayMode = () => {}, setDebugReplayRange = () => {}, onShareTimestamp = () => {}, + onMotionSearch, onUpdateFilter, setRange, setMode, @@ -108,6 +112,7 @@ export default function MobileReviewSettingsDrawer({ "views/recording", "components/dialog", "views/replay", + "views/events", "common", ]); const isAdmin = useIsAdmin(); @@ -343,27 +348,6 @@ export default function MobileReviewSettingsDrawer({ {t("export")} )} - {features.includes("share-timestamp") && ( - - )} {features.includes("calendar") && ( + )} + {features.includes("motion-search") && onMotionSearch && ( + + )} {isAdmin && features.includes("debug-replay") && (