This commit is contained in:
Josh Hawkins 2024-12-16 12:23:55 -06:00
parent 642cadbb77
commit fe5f94aaeb
2 changed files with 69 additions and 3 deletions

View File

@ -53,13 +53,16 @@ function useValue(): useValueReturn {
const cameraStates: WsState = {};
Object.entries(cameraActivity).forEach(([name, state]) => {
const { record, detect, snapshots, audio, autotracking } =
const { record, detect, snapshots, audio, notifications, autotracking } =
// @ts-expect-error we know this is correct
state["config"];
cameraStates[`${name}/recordings/state`] = record ? "ON" : "OFF";
cameraStates[`${name}/detect/state`] = detect ? "ON" : "OFF";
cameraStates[`${name}/snapshots/state`] = snapshots ? "ON" : "OFF";
cameraStates[`${name}/audio/state`] = audio ? "ON" : "OFF";
cameraStates[`${name}/notifications/state`] = notifications
? "ON"
: "OFF";
cameraStates[`${name}/ptz_autotracker/state`] = autotracking
? "ON"
: "OFF";
@ -414,6 +417,17 @@ export function useTrackedObjectUpdate(): { payload: string } {
return useDeepMemo(JSON.parse(payload as string));
}
export function useNotifications(camera: string): {
payload: ToggleableSetting;
send: (payload: string, retain?: boolean) => void;
} {
const {
value: { payload },
send,
} = useWs(`${camera}/notifications/state`, `${camera}/notifications/set`);
return { payload: payload as ToggleableSetting, send };
}
export function useNotificationTest(): {
payload: string;
send: (payload: string, retain?: boolean) => void;

View File

@ -1,4 +1,3 @@
import { useNotificationTest } from "@/api/ws";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import { Button } from "@/components/ui/button";
import {
@ -20,13 +19,15 @@ import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { FrigateConfig } from "@/types/frigateConfig";
import { zodResolver } from "@hookform/resolvers/zod";
import axios from "axios";
import { useCallback, useContext, useEffect, useState } from "react";
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import { LuExternalLink } from "react-icons/lu";
import { Link } from "react-router-dom";
import { toast } from "sonner";
import useSWR from "swr";
import { z } from "zod";
import { useNotifications, useNotificationTest } from "@/api/ws";
import FilterSwitch from "@/components/filter/FilterSwitch";
const NOTIFICATION_SERVICE_WORKER = "notifications-worker.js";
@ -48,6 +49,16 @@ export default function NotificationView({
},
);
const cameras = useMemo(() => {
if (!config) {
return [];
}
return Object.values(config.cameras)
.filter((conf) => conf.ui.dashboard && conf.enabled)
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
}, [config]);
const { send: sendTestNotification } = useNotificationTest();
// status bar
@ -351,8 +362,49 @@ export default function NotificationView({
)}
</div>
</div>
{registration != null && (
<div className="mt-4 gap-2 space-y-6">
<div className="space-y-3">
<Separator className="my-2 flex bg-secondary" />
<Heading as="h4" className="my-2">
Cameras
</Heading>
<div className="max-w-6xl">
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant">
<p>Enable / disable notifications for specific cameras.</p>
</div>
</div>
<div className="flex max-w-72 flex-col gap-2.5">
{cameras.map((item) => (
<CameraNotificationSwitch camera={item.name} />
))}
</div>
</div>
</div>
)}
</div>
</div>
</>
);
}
type CameraNotificationSwitchProps = {
camera: string;
};
function CameraNotificationSwitch({ camera }: CameraNotificationSwitchProps) {
const { payload: notificationState, send: sendNotification } =
useNotifications(camera);
return (
<FilterSwitch
key={camera}
isChecked={notificationState == "ON"}
label={camera.replaceAll("_", " ")}
onCheckedChange={(isChecked) => {
sendNotification(isChecked ? "ON" : "OFF");
}}
/>
);
}