From ff270d995cc4c62db3cedb364ec2147caaae2222 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:23:55 -0600 Subject: [PATCH] frontend --- web/src/api/ws.tsx | 26 ++++- .../settings/NotificationsSettingsView.tsx | 98 ++++++++++++++++--- 2 files changed, 112 insertions(+), 12 deletions(-) diff --git a/web/src/api/ws.tsx b/web/src/api/ws.tsx index ac633b08b..f27e0c7ab 100644 --- a/web/src/api/ws.tsx +++ b/web/src/api/ws.tsx @@ -53,7 +53,15 @@ function useValue(): useValueReturn { const cameraStates: WsState = {}; Object.entries(cameraActivity).forEach(([name, state]) => { - const { record, detect, snapshots, audio, notifications, autotracking } = + const { + record, + detect, + snapshots, + audio, + notifications, + notifications_suspended, + autotracking, + } = // @ts-expect-error we know this is correct state["config"]; cameraStates[`${name}/recordings/state`] = record ? "ON" : "OFF"; @@ -63,6 +71,8 @@ function useValue(): useValueReturn { cameraStates[`${name}/notifications/state`] = notifications ? "ON" : "OFF"; + cameraStates[`${name}/notifications/suspended`] = + notifications_suspended || 0; cameraStates[`${name}/ptz_autotracker/state`] = autotracking ? "ON" : "OFF"; @@ -428,6 +438,20 @@ export function useNotifications(camera: string): { return { payload: payload as ToggleableSetting, send }; } +export function useNotificationSuspend(camera: string): { + payload: string; + send: (payload: string, retain?: boolean) => void; +} { + const { + value: { payload }, + send, + } = useWs( + `${camera}/notifications/suspended`, + `${camera}/notifications/suspend`, + ); + return { payload: payload as string, send }; +} + export function useNotificationTest(): { payload: string; send: (payload: string, retain?: boolean) => void; diff --git a/web/src/views/settings/NotificationsSettingsView.tsx b/web/src/views/settings/NotificationsSettingsView.tsx index 75c56b567..e6dd9d346 100644 --- a/web/src/views/settings/NotificationsSettingsView.tsx +++ b/web/src/views/settings/NotificationsSettingsView.tsx @@ -26,8 +26,19 @@ 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 { + useNotifications, + useNotificationSuspend, + useNotificationTest, +} from "@/api/ws"; import FilterSwitch from "@/components/filter/FilterSwitch"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectItem, +} from "@/components/ui/select"; const NOTIFICATION_SERVICE_WORKER = "notifications-worker.js"; @@ -362,7 +373,7 @@ export default function NotificationView({ )} - {registration != null && ( + {cameras && (
@@ -393,18 +404,83 @@ type CameraNotificationSwitchProps = { camera: string; }; -function CameraNotificationSwitch({ camera }: CameraNotificationSwitchProps) { +export function CameraNotificationSwitch({ + camera, +}: CameraNotificationSwitchProps) { const { payload: notificationState, send: sendNotification } = useNotifications(camera); + const { payload: notificationSuspendUntil, send: sendNotificationSuspend } = + useNotificationSuspend(camera); + const [isSuspended, setIsSuspended] = useState(false); + + useEffect(() => { + if (notificationSuspendUntil) { + setIsSuspended(notificationSuspendUntil != "0"); + } + }, [notificationSuspendUntil]); + + const handleSuspend = (duration: string) => { + sendNotificationSuspend(duration); + }; + + const handleCancelSuspension = () => { + sendNotificationSuspend("0"); // Assuming sending "0" cancels the suspension + }; + + const formatSuspendedUntil = (timestamp: string) => { + const date = new Date(parseInt(timestamp) * 1000); + return date.toLocaleString(undefined, { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); + }; + + const isOn = notificationState === "ON" || isSuspended; return ( - { - sendNotification(isChecked ? "ON" : "OFF"); - }} - /> +
+
+ { + sendNotification(isChecked ? "ON" : "OFF"); + }} + /> + {!isSuspended && isOn && ( + + )} + {isSuspended && ( + + )} +
+ {isSuspended && notificationSuspendUntil && ( +
+ Suspended until {formatSuspendedUntil(notificationSuspendUntil)} +
+ )} +
); }