import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { ReactNode, useCallback, useContext, useEffect } from "react"; import { Toaster, toast } from "sonner"; import { Button } from "../../components/ui/button"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import { useUserPersistence, deleteUserNamespacedKey, } from "@/hooks/use-user-persistence"; import { isSafari } from "react-device-detect"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, } from "../../components/ui/select"; import { useTranslation } from "react-i18next"; import { AuthContext } from "@/context/auth-context"; import { SettingsGroupCard, SPLIT_ROW_CLASS_NAME, DESCRIPTION_CLASS_NAME, CONTROL_COLUMN_CLASS_NAME, } from "@/components/card/SettingsGroupCard"; import Heading from "@/components/ui/heading"; const WEEK_STARTS_ON = ["Sunday", "Monday"]; type SwitchSettingRowProps = { id: string; label: string; description: string; checked: boolean | undefined; onCheckedChange: (checked: boolean | undefined) => void; }; function SwitchSettingRow({ id, label, description, checked, onCheckedChange, }: SwitchSettingRowProps) { return (

{description}

); } type ValueSettingRowProps = { id: string; label: string; description: string; control: ReactNode; }; function ValueSettingRow({ id, label, description, control, }: ValueSettingRowProps) { return (

{description}

{control}

{description}

); } export default function UiSettingsView() { const { data: config } = useSWR("config"); const { t } = useTranslation("views/settings"); const { auth } = useContext(AuthContext); const username = auth?.user?.username; const PLAYBACK_RATE_DEFAULT = isSafari ? [0.5, 1, 2] : [0.5, 1, 2, 4, 8, 16]; const clearStoredLayouts = useCallback(() => { if (!config) { return []; } Object.entries(config.camera_groups).forEach(async ([cameraName]) => { await deleteUserNamespacedKey(`${cameraName}-draggable-layout`, username) .then(() => { toast.success( t("general.toast.success.clearStoredLayout", { cameraName }), { position: "top-center", }, ); }) .catch((error) => { const errorMessage = error.response?.data?.message || error.response?.data?.detail || "Unknown error"; toast.error( t("general.toast.error.clearStoredLayoutFailed", { errorMessage }), { position: "top-center", }, ); }); }); }, [config, t, username]); const clearStreamingSettings = useCallback(async () => { if (!config) { return []; } await deleteUserNamespacedKey("streaming-settings", username) .then(() => { toast.success(t("general.toast.success.clearStreamingSettings"), { position: "top-center", }); }) .catch((error) => { const errorMessage = error.response?.data?.message || error.response?.data?.detail || "Unknown error"; toast.error( t("general.toast.error.clearStreamingSettingsFailed", { errorMessage, }), { position: "top-center", }, ); }); }, [config, t, username]); useEffect(() => { document.title = t("documentTitle.general"); }, [t]); const [autoLive, setAutoLive] = useUserPersistence("autoLiveView", true); const [cameraNames, setCameraName] = useUserPersistence( "displayCameraNames", false, ); const [playbackRate, setPlaybackRate] = useUserPersistence("playbackRate", 1); const [weekStartsOn, setWeekStartsOn] = useUserPersistence("weekStartsOn", 0); const [alertVideos, setAlertVideos] = useUserPersistence("alertVideos", true); const [fallbackTimeout, setFallbackTimeout] = useUserPersistence( "liveFallbackTimeout", 3, ); const liveDashboardSwitchRows = [ { id: "auto-live", label: t("general.liveDashboard.automaticLiveView.label"), description: t("general.liveDashboard.automaticLiveView.desc"), checked: autoLive, onCheckedChange: setAutoLive, }, { id: "images-only", label: t("general.liveDashboard.playAlertVideos.label"), description: t("general.liveDashboard.playAlertVideos.desc"), checked: alertVideos, onCheckedChange: setAlertVideos, }, { id: "camera-names", label: t("general.liveDashboard.displayCameraNames.label"), description: t("general.liveDashboard.displayCameraNames.desc"), checked: cameraNames, onCheckedChange: setCameraName, }, ]; return (
{t("general.title")}
{liveDashboardSwitchRows.map((row) => ( ))} setFallbackTimeout(parseInt(value, 10)) } > {t("time.second", { ns: "common", time: fallbackTimeout, count: fallbackTimeout, })} {[1, 2, 3, 5, 8, 10, 12, 15].map((timeout) => ( {t("time.second", { ns: "common", time: timeout, count: timeout, })} ))} } /> {t("general.storedLayouts.clearAll")} } /> {t("general.cameraGroupStreaming.clearAll")} } />
setPlaybackRate(parseFloat(value))} > {`${playbackRate}x`} {PLAYBACK_RATE_DEFAULT.map((rate) => ( {rate}x ))} } /> setWeekStartsOn(parseInt(value, 10)) } > {t( "general.calendar.firstWeekday." + WEEK_STARTS_ON[weekStartsOn ?? 0].toLowerCase(), )} {WEEK_STARTS_ON.map((day, index) => ( {t( "general.calendar.firstWeekday." + day.toLowerCase(), )} ))} } />
); }