mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-18 09:04:28 +03:00
frontend
This commit is contained in:
parent
642cadbb77
commit
fe5f94aaeb
@ -53,13 +53,16 @@ function useValue(): useValueReturn {
|
|||||||
const cameraStates: WsState = {};
|
const cameraStates: WsState = {};
|
||||||
|
|
||||||
Object.entries(cameraActivity).forEach(([name, state]) => {
|
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
|
// @ts-expect-error we know this is correct
|
||||||
state["config"];
|
state["config"];
|
||||||
cameraStates[`${name}/recordings/state`] = record ? "ON" : "OFF";
|
cameraStates[`${name}/recordings/state`] = record ? "ON" : "OFF";
|
||||||
cameraStates[`${name}/detect/state`] = detect ? "ON" : "OFF";
|
cameraStates[`${name}/detect/state`] = detect ? "ON" : "OFF";
|
||||||
cameraStates[`${name}/snapshots/state`] = snapshots ? "ON" : "OFF";
|
cameraStates[`${name}/snapshots/state`] = snapshots ? "ON" : "OFF";
|
||||||
cameraStates[`${name}/audio/state`] = audio ? "ON" : "OFF";
|
cameraStates[`${name}/audio/state`] = audio ? "ON" : "OFF";
|
||||||
|
cameraStates[`${name}/notifications/state`] = notifications
|
||||||
|
? "ON"
|
||||||
|
: "OFF";
|
||||||
cameraStates[`${name}/ptz_autotracker/state`] = autotracking
|
cameraStates[`${name}/ptz_autotracker/state`] = autotracking
|
||||||
? "ON"
|
? "ON"
|
||||||
: "OFF";
|
: "OFF";
|
||||||
@ -414,6 +417,17 @@ export function useTrackedObjectUpdate(): { payload: string } {
|
|||||||
return useDeepMemo(JSON.parse(payload as 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(): {
|
export function useNotificationTest(): {
|
||||||
payload: string;
|
payload: string;
|
||||||
send: (payload: string, retain?: boolean) => void;
|
send: (payload: string, retain?: boolean) => void;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useNotificationTest } from "@/api/ws";
|
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@ -20,13 +19,15 @@ import { StatusBarMessagesContext } from "@/context/statusbar-provider";
|
|||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import axios from "axios";
|
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 { useForm } from "react-hook-form";
|
||||||
import { LuExternalLink } from "react-icons/lu";
|
import { LuExternalLink } from "react-icons/lu";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { useNotifications, useNotificationTest } from "@/api/ws";
|
||||||
|
import FilterSwitch from "@/components/filter/FilterSwitch";
|
||||||
|
|
||||||
const NOTIFICATION_SERVICE_WORKER = "notifications-worker.js";
|
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();
|
const { send: sendTestNotification } = useNotificationTest();
|
||||||
|
|
||||||
// status bar
|
// status bar
|
||||||
@ -351,8 +362,49 @@ export default function NotificationView({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
</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");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user