import ActivityIndicator from "@/components/indicators/activity-indicator"; import { Button } from "@/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import Heading from "@/components/ui/heading"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Toaster } from "@/components/ui/sonner"; import { Switch } from "@/components/ui/switch"; 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 { 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"; const NOTIFICATION_SERVICE_WORKER = "notifications-worker.js"; type NotificationSettingsValueType = { enabled: boolean; email?: string; }; type NotificationsSettingsViewProps = { setUnsavedChanges: React.Dispatch>; }; export default function NotificationView({ setUnsavedChanges, }: NotificationsSettingsViewProps) { const { data: config, mutate: updateConfig } = useSWR( "config", { revalidateOnFocus: false, }, ); // status bar const { addMessage, removeMessage } = useContext(StatusBarMessagesContext)!; // notification key handling const { data: publicKey } = useSWR( config?.notifications?.enabled ? "notifications/pubkey" : null, { revalidateOnFocus: false }, ); const subscribeToNotifications = useCallback( (registration: ServiceWorkerRegistration) => { if (registration) { addMessage( "notification_settings", "Unsaved Notification Registrations", undefined, "registration", ); registration.pushManager .subscribe({ userVisibleOnly: true, applicationServerKey: publicKey, }) .then((pushSubscription) => { axios .post("notifications/register", { sub: pushSubscription, }) .catch(() => { toast.error("Failed to save notification registration.", { position: "top-center", }); pushSubscription.unsubscribe(); registration.unregister(); setRegistration(null); }); toast.success( "Successfully registered for notifications. Restart to start receiving notifications.", { position: "top-center", }, ); }); } }, [publicKey, addMessage], ); // notification state const [registration, setRegistration] = useState(); useEffect(() => { navigator.serviceWorker .getRegistration(NOTIFICATION_SERVICE_WORKER) .then((worker) => { if (worker) { setRegistration(worker); } else { setRegistration(null); } }) .catch(() => { setRegistration(null); }); }, []); // form const [isLoading, setIsLoading] = useState(false); const formSchema = z.object({ enabled: z.boolean(), email: z.string(), }); const form = useForm>({ resolver: zodResolver(formSchema), mode: "onChange", defaultValues: { enabled: config?.notifications.enabled, email: config?.notifications.email, }, }); const onCancel = useCallback(() => { if (!config) { return; } setUnsavedChanges(false); form.reset({ enabled: config.notifications.enabled, email: config.notifications.email || "", }); // we know that these deps are correct // eslint-disable-next-line react-hooks/exhaustive-deps }, [config, removeMessage, setUnsavedChanges]); const saveToConfig = useCallback( async ( { enabled, email }: NotificationSettingsValueType, // values submitted via the form ) => { axios .put( `config/set?notifications.enabled=${enabled}¬ifications.email=${email}`, { requires_restart: 0, }, ) .then((res) => { if (res.status === 200) { toast.success("Notification settings have been saved.", { position: "top-center", }); updateConfig(); } else { toast.error(`Failed to save config changes: ${res.statusText}`, { position: "top-center", }); } }) .catch((error) => { toast.error( `Failed to save config changes: ${error.response.data.message}`, { position: "top-center" }, ); }) .finally(() => { setIsLoading(false); }); }, [updateConfig, setIsLoading], ); function onSubmit(values: z.infer) { setIsLoading(true); saveToConfig(values as NotificationSettingsValueType); } return ( <>
Notification Settings

Frigate can natively send push notifications to your device when it is running in the browser or installed as a PWA.

Read the Documentation{" "}
(
{ return field.onChange(checked); }} />
)} /> ( Email Entering a valid email is required, as this is used by the push server in case problems occur. )} />
); }