mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-12 22:25:24 +03:00
Implement push notification handling
This commit is contained in:
parent
f946690520
commit
6fbab846af
@ -44,7 +44,9 @@ def register_notifications():
|
||||
sub = json.get("sub")
|
||||
|
||||
if not sub:
|
||||
return jsonify({"success": False, "message": "Subscription must be provided."}), 400
|
||||
return jsonify(
|
||||
{"success": False, "message": "Subscription must be provided."}
|
||||
), 400
|
||||
|
||||
try:
|
||||
User.update(notification_tokens=User.notification_tokens.append(sub)).where(
|
||||
|
||||
@ -85,19 +85,24 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
||||
|
||||
title = f"{', '.join(sorted_objects).replace('_', ' ').title()}{' was' if state == 'end' else ''} detected in {', '.join(payload['after']['data']['zones']).replace('_', ' ').title()}"
|
||||
message = f"Detected on {payload['after']['camera'].replace('_', ' ').title()}"
|
||||
direct_url = f"{self.config.notifications.base_url}/review?id={reviewId}"
|
||||
image = f'{self.config.notifications.base_url}{payload["after"]["thumb_path"].replace("/media/frigate", "")}'
|
||||
direct_url = f"/review?id={reviewId}"
|
||||
image = f'{payload["after"]["thumb_path"].replace("/media/frigate", "")}'
|
||||
|
||||
logger.info(f"the image for testing is {image}")
|
||||
|
||||
for pusher in self.web_pushers:
|
||||
pusher.send(
|
||||
headers=self.claim_headers,
|
||||
ttl=0,
|
||||
data=json.dumps({
|
||||
"title": title,
|
||||
"message": message,
|
||||
"direct_url": direct_url,
|
||||
"image": image,
|
||||
}),
|
||||
data=json.dumps(
|
||||
{
|
||||
"title": title,
|
||||
"message": message,
|
||||
"direct_url": direct_url,
|
||||
"image": image,
|
||||
"id": reviewId,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def stop(self) -> None:
|
||||
|
||||
@ -1,9 +1,58 @@
|
||||
// Notifications Worker
|
||||
|
||||
self.addEventListener("push", function (event) {
|
||||
// @ts-expect-error we know this exists
|
||||
if (event.data) {
|
||||
console.log("This push event has data: ", event.data.text());
|
||||
// @ts-expect-error we know this exists
|
||||
const data = event.data.json();
|
||||
// @ts-expect-error we know this exists
|
||||
self.registration.showNotification(data.title, {
|
||||
body: data.message,
|
||||
icon: data.image,
|
||||
image: data.image,
|
||||
tag: data.id,
|
||||
data: { id: data.id, link: data.direct_url },
|
||||
actions: [
|
||||
{
|
||||
action: `view-${data.id}`,
|
||||
title: "View",
|
||||
},
|
||||
],
|
||||
});
|
||||
} else {
|
||||
console.log("This push event has no data.");
|
||||
// pass
|
||||
// This push event has no data
|
||||
}
|
||||
});
|
||||
|
||||
self.addEventListener("notificationclick", (event) => {
|
||||
// @ts-expect-error we know this exists
|
||||
if (event.notification) {
|
||||
// @ts-expect-error we know this exists
|
||||
event.notification.close();
|
||||
|
||||
// @ts-expect-error we know this exists
|
||||
if (event.notification.data) {
|
||||
// @ts-expect-error we know this exists
|
||||
const url = event.notification.data.link;
|
||||
|
||||
// @ts-expect-error we know this exists
|
||||
clients.matchAll({ type: "window" }).then((windowClients) => {
|
||||
// Check if there is already a window/tab open with the target URL
|
||||
for (let i = 0; i < windowClients.length; i++) {
|
||||
const client = windowClients[i];
|
||||
// If so, just focus it.
|
||||
if (client.url === url && "focus" in client) {
|
||||
return client.focus();
|
||||
}
|
||||
}
|
||||
// If not, then open the target URL in a new window/tab.
|
||||
// @ts-expect-error we know this exists
|
||||
if (clients.openWindow) {
|
||||
// @ts-expect-error we know this exists
|
||||
return clients.openWindow(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -5,30 +5,58 @@ import { Toaster } from "@/components/ui/sonner";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
const NOTIFICATION_SERVICE_WORKER = "notifications-worker.ts";
|
||||
|
||||
export default function NotificationView() {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const { data: config } = useSWR<FrigateConfig>("config", {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
|
||||
// notification key handling
|
||||
|
||||
const { data: publicKey } = useSWR(
|
||||
config?.notifications?.enabled ? "notifications/pubkey" : null,
|
||||
{ revalidateOnFocus: false },
|
||||
);
|
||||
|
||||
const subscribeToNotifications = useCallback(
|
||||
(registration: ServiceWorkerRegistration) => {
|
||||
if (registration) {
|
||||
registration.pushManager
|
||||
.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: publicKey,
|
||||
})
|
||||
.then((pushSubscription) => {
|
||||
axios.post("notifications/register", {
|
||||
sub: pushSubscription,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
[publicKey],
|
||||
);
|
||||
|
||||
// notification state
|
||||
|
||||
const [notificationsSubscribed, setNotificationsSubscribed] =
|
||||
useState<boolean>();
|
||||
const [registration, setRegistration] =
|
||||
useState<ServiceWorkerRegistration | null>();
|
||||
|
||||
useEffect(() => {
|
||||
navigator.serviceWorker
|
||||
.getRegistration(NOTIFICATION_SERVICE_WORKER)
|
||||
.then((worker) => {
|
||||
setNotificationsSubscribed(worker != null);
|
||||
if (worker) {
|
||||
setRegistration(worker);
|
||||
} else {
|
||||
setRegistration(null);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setRegistration(null);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@ -70,34 +98,34 @@ export default function NotificationView() {
|
||||
// TODO make the notifications button show enable / disable depending on current state
|
||||
}
|
||||
<Button
|
||||
disabled={
|
||||
notificationsSubscribed == undefined ||
|
||||
publicKey == undefined
|
||||
}
|
||||
disabled={publicKey == undefined}
|
||||
onClick={() => {
|
||||
Notification.requestPermission().then((permission) => {
|
||||
console.log("notification permissions are ", permission);
|
||||
if (permission === "granted") {
|
||||
navigator.serviceWorker
|
||||
.register(NOTIFICATION_SERVICE_WORKER)
|
||||
.then((registration) => {
|
||||
registration.pushManager
|
||||
.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: publicKey,
|
||||
})
|
||||
.then((pushSubscription) => {
|
||||
console.log(pushSubscription.endpoint);
|
||||
axios.post("notifications/register", {
|
||||
sub: pushSubscription,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
if (registration == null) {
|
||||
Notification.requestPermission().then((permission) => {
|
||||
if (permission === "granted") {
|
||||
navigator.serviceWorker
|
||||
.register(NOTIFICATION_SERVICE_WORKER)
|
||||
.then((registration) => {
|
||||
setRegistration(registration);
|
||||
|
||||
if (registration.active) {
|
||||
subscribeToNotifications(registration);
|
||||
} else {
|
||||
setTimeout(
|
||||
() => subscribeToNotifications(registration),
|
||||
1000,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
registration.unregister();
|
||||
setRegistration(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{`${notificationsSubscribed ? "Disable" : "Enable"} Notifications`}
|
||||
{`${registration != null ? "Unregister" : "Register"} for Notifications`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user