import { Event } from "@/types/event"; import { FrigateConfig } from "@/types/frigateConfig"; import axios from "axios"; import { useCallback, useState } from "react"; import { LuExternalLink, LuMinus, LuPlus } from "react-icons/lu"; import { Link } from "react-router-dom"; import { toast } from "sonner"; import useSWR from "swr"; import { Button } from "@/components/ui/button"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { Separator } from "@/components/ui/separator"; import { Slider } from "@/components/ui/slider"; import { Trans, useTranslation } from "react-i18next"; import { useDocDomain } from "@/hooks/use-doc-domain"; import { useIsAdmin } from "@/hooks/use-is-admin"; const OFFSET_MIN = -2500; const OFFSET_MAX = 2500; const OFFSET_STEP = 50; type AnnotationSettingsPaneProps = { event: Event; annotationOffset: number; setAnnotationOffset: React.Dispatch>; }; export function AnnotationSettingsPane({ event, annotationOffset, setAnnotationOffset, }: AnnotationSettingsPaneProps) { const { t } = useTranslation(["views/explore"]); const isAdmin = useIsAdmin(); const { getLocaleDocUrl } = useDocDomain(); const { data: config, mutate: updateConfig } = useSWR("config"); const [isLoading, setIsLoading] = useState(false); const handleSliderChange = useCallback( (values: number[]) => { if (!values || values.length === 0) return; setAnnotationOffset(values[0]); }, [setAnnotationOffset], ); const stepOffset = useCallback( (delta: number) => { setAnnotationOffset((prev) => { const next = prev + delta; return Math.max(OFFSET_MIN, Math.min(OFFSET_MAX, next)); }); }, [setAnnotationOffset], ); const reset = useCallback(() => { setAnnotationOffset(0); }, [setAnnotationOffset]); const saveToConfig = useCallback(async () => { if (!config || !event) return; setIsLoading(true); try { const res = await axios.put( `config/set?cameras.${event.camera}.detect.annotation_offset=${annotationOffset}`, { requires_restart: 0 }, ); if (res.status === 200) { toast.success( t("trackingDetails.annotationSettings.offset.toast.success", { camera: event.camera, }), { position: "top-center" }, ); updateConfig(); } else { toast.error( t("toast.save.error.title", { errorMessage: res.statusText, ns: "common", }), { position: "top-center" }, ); } } catch (error: unknown) { const err = error as { response?: { data?: { message?: string; detail?: string } }; }; const errorMessage = err?.response?.data?.message || err?.response?.data?.detail || "Unknown error"; toast.error(t("toast.save.error.title", { errorMessage, ns: "common" }), { position: "top-center", }); } finally { setIsLoading(false); } }, [annotationOffset, config, event, updateConfig, t]); return (
{t("trackingDetails.annotationSettings.title")}
{t("trackingDetails.annotationSettings.offset.label")}
trackingDetails.annotationSettings.offset.millisecondsToOffset
{annotationOffset > 0 ? "+" : ""} {annotationOffset}ms
{t("trackingDetails.annotationSettings.offset.tips")}
{t("readTheDocumentation", { ns: "common" })}
{isAdmin && ( <> )}
); }