add motion search to history actions menu and mobile drawer

This commit is contained in:
Josh Hawkins 2026-05-30 19:24:35 -05:00
parent 196195ccb0
commit f7e1ff690b
4 changed files with 47 additions and 7 deletions

View File

@ -12,14 +12,21 @@ type ActionsDropdownProps = {
onDebugReplayClick?: () => void;
onExportClick: () => void;
onShareTimestampClick: () => void;
onMotionSearchClick?: () => void;
};
export default function ActionsDropdown({
onDebugReplayClick,
onExportClick,
onShareTimestampClick,
onMotionSearchClick,
}: Readonly<ActionsDropdownProps>) {
const { t } = useTranslation(["components/dialog", "views/replay", "common"]);
const { t } = useTranslation([
"components/dialog",
"views/replay",
"views/events",
"common",
]);
return (
<DropdownMenu>
@ -42,6 +49,11 @@ export default function ActionsDropdown({
<DropdownMenuItem onClick={onShareTimestampClick}>
{t("recording.shareTimestamp.label", { ns: "components/dialog" })}
</DropdownMenuItem>
{onMotionSearchClick && (
<DropdownMenuItem onClick={onMotionSearchClick}>
{t("motionSearch.menuItem", { ns: "views/events" })}
</DropdownMenuItem>
)}
{onDebugReplayClick && (
<DropdownMenuItem onClick={onDebugReplayClick}>
{t("title", { ns: "views/replay" })}

View File

@ -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();
@ -364,6 +369,19 @@ export default function MobileReviewSettingsDrawer({
})}
</Button>
)}
{features.includes("motion-search") && onMotionSearch && (
<Button
className="flex w-full items-center justify-center gap-2"
aria-label={t("motionSearch.menuItem", { ns: "views/events" })}
onClick={() => {
onMotionSearch();
setDrawerMode("none");
}}
>
<LuSearch className="size-5 rounded-md bg-secondary-foreground stroke-secondary p-1" />
{t("motionSearch.menuItem", { ns: "views/events" })}
</Button>
)}
{features.includes("calendar") && (
<Button
className="flex w-full items-center justify-center gap-2"

View File

@ -56,11 +56,9 @@ export default function Events() {
false,
);
const [recording, setRecording] = useOverlayState<RecordingStartingPoint>(
"recording",
undefined,
false,
);
const [recording, setRecording] = useOverlayState<
RecordingStartingPoint | undefined
>("recording", undefined, false);
const [motionPreviewsCamera, setMotionPreviewsCamera] = useOverlayState<
string | undefined
>("motionPreviewsCamera", undefined);
@ -668,6 +666,10 @@ export default function Events() {
filter={reviewFilter}
updateFilter={onUpdateFilter}
refreshData={reloadData}
onMotionSearch={(camera) => {
setMotionSearchCamera(camera);
setRecording(undefined);
}}
/>
);
}

View File

@ -95,6 +95,7 @@ type RecordingViewProps = {
filter?: ReviewFilter;
updateFilter: (newFilter: ReviewFilter) => void;
refreshData?: () => void;
onMotionSearch?: (camera: string) => void;
};
export function RecordingView({
startCamera,
@ -107,6 +108,7 @@ export function RecordingView({
filter,
updateFilter,
refreshData,
onMotionSearch,
}: RecordingViewProps) {
const { t } = useTranslation(["views/events", "components/dialog"]);
const { data: config } = useSWR<FrigateConfig>("config");
@ -725,6 +727,9 @@ export function RecordingView({
setCustomShareTimestamp(initialTimestamp);
setShareTimestampOpen(true);
}}
onMotionSearchClick={
onMotionSearch ? () => onMotionSearch(mainCamera) : undefined
}
onDebugReplayClick={
isAdmin
? () => {
@ -807,6 +812,9 @@ export function RecordingView({
}
}}
onShareTimestamp={onShareReviewLink}
onMotionSearch={
onMotionSearch ? () => onMotionSearch(mainCamera) : undefined
}
onUpdateFilter={updateFilter}
setRange={setExportRange}
setMode={setExportMode}