feat(chat): added desktop notifications
Build and Push Docker image / build-and-push (push) Failing after 4m10s
Details
Build and Push Docker image / build-and-push (push) Failing after 4m10s
Details
This commit is contained in:
parent
390edb38bd
commit
d6df0b1d43
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -2,37 +2,76 @@ import {ActionIcon, Indicator} from "@mantine/core";
|
||||||
import {Link, useMatch} from "react-router-dom";
|
import {Link, useMatch} from "react-router-dom";
|
||||||
import {IconMessageCircle} from "@tabler/icons-react";
|
import {IconMessageCircle} from "@tabler/icons-react";
|
||||||
import {usePB} from "@/lib/pocketbase.tsx";
|
import {usePB} from "@/lib/pocketbase.tsx";
|
||||||
import {useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {useTimeout} from "@mantine/hooks";
|
import {useTimeout} from "@mantine/hooks";
|
||||||
import {MessagesModel} from "@/models/MessageTypes.ts";
|
import {MessagesModel} from "@/models/MessageTypes.ts";
|
||||||
|
import {APP_NAME, APP_URL} from "../../../../config.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse HTML string and return text content
|
||||||
|
* @param html - HTML string
|
||||||
|
*/
|
||||||
|
const parseHtmlString = (html: string): string => {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(html, 'text/html');
|
||||||
|
return doc.body.textContent || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendDesktopNotification = (body: string, url: string) => {
|
||||||
|
if ("Notification" in window && Notification.permission === 'granted') {
|
||||||
|
const notification = new Notification(APP_NAME, {
|
||||||
|
body: body,
|
||||||
|
icon: `${APP_URL}/stuve-logo.svg`,
|
||||||
|
vibrate: [200, 100, 200],
|
||||||
|
data: {url: url}
|
||||||
|
})
|
||||||
|
notification.onclick = () => {
|
||||||
|
window.focus()
|
||||||
|
window.open(notification.data.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function ChatNavIcon() {
|
export default function ChatNavIcon() {
|
||||||
|
|
||||||
const [newMessage, setNewMessage] = useState<string | null>(null);
|
const [notificationUrl, setNotificationUrl] = useState<string | null>(null);
|
||||||
const {start} = useTimeout(() => setNewMessage(null), 5000);
|
const {start} = useTimeout(() => setNotificationUrl(null), 5000);
|
||||||
|
|
||||||
const {user, useSubscription} = usePB()
|
const {user, useSubscription} = usePB()
|
||||||
|
|
||||||
const match = useMatch("/chat/:listId")
|
const match = useMatch("/chat/:listId")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Request notification permission on component mount
|
||||||
|
if ("Notification" in window && Notification.permission !== 'granted') {
|
||||||
|
Notification.requestPermission().then(() => {
|
||||||
|
sendDesktopNotification(
|
||||||
|
"Willkommen bei StuVe! Du kannst jetzt Desktop Benachrichtigungen erhalten.",
|
||||||
|
`${APP_URL}/chat`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
useSubscription<MessagesModel>({
|
useSubscription<MessagesModel>({
|
||||||
idOrName: "messages",
|
idOrName: "messages",
|
||||||
topic: "*",
|
topic: "*",
|
||||||
callback: (event) => {
|
callback: (event) => {
|
||||||
if (event.action == "create") {
|
if (event.action == "create") {
|
||||||
if (event.record.isAnnouncement) {
|
// check if chat page is not already open and if sender is not the user
|
||||||
start()
|
if (match?.params.listId === event.record.eventList && event.record.sender === user?.id) {
|
||||||
setNewMessage("announcements")
|
return
|
||||||
} else if (
|
|
||||||
event.record.eventList
|
|
||||||
&&
|
|
||||||
match?.params.listId !== event.record.eventList // check if chat page is not already open
|
|
||||||
&&
|
|
||||||
event.record.sender !== user?.id // check if sender is not the user
|
|
||||||
) {
|
|
||||||
start()
|
|
||||||
setNewMessage(event.record.eventList)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const notificationUrl = `/chat/${event.record.isAnnouncement ? "announcements" : event.record.eventList}`
|
||||||
|
|
||||||
|
setNotificationUrl(notificationUrl)
|
||||||
|
start()
|
||||||
|
|
||||||
|
sendDesktopNotification(
|
||||||
|
parseHtmlString(event.record.content),
|
||||||
|
`${APP_URL}${notificationUrl}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -42,12 +81,12 @@ export default function ChatNavIcon() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<Indicator inline processing disabled={newMessage === null} classNames={{
|
<Indicator inline processing disabled={notificationUrl === null} classNames={{
|
||||||
root: "stack"
|
root: "stack"
|
||||||
}}>
|
}}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
component={Link}
|
component={Link}
|
||||||
to={`/chat${newMessage ? `/${newMessage}` : ""}`}
|
to={notificationUrl ?? "/chat"}
|
||||||
variant={"transparent"}
|
variant={"transparent"}
|
||||||
color={"gray"}
|
color={"gray"}
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in New Issue