diff --git a/src/App.tsx b/src/App.tsx
index 48a806f4..49d24745 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -11,6 +11,7 @@ import { useAppStore } from "@core/stores/appStore.js";
import { useDeviceStore } from "@core/stores/deviceStore.js";
import { Dashboard } from "@pages/Dashboard/index.js";
import { MapProvider } from "react-map-gl";
+import { Notifications } from "./components/Notifications";
export const App = (): JSX.Element => {
const { getDevice } = useDeviceStore();
@@ -39,6 +40,7 @@ export const App = (): JSX.Element => {
+
) : (
<>
diff --git a/src/components/Notifications.tsx b/src/components/Notifications.tsx
new file mode 100644
index 00000000..0f7bfe24
--- /dev/null
+++ b/src/components/Notifications.tsx
@@ -0,0 +1,40 @@
+import { useDevice } from "@app/core/stores/deviceStore";
+import type { Types } from "@meshtastic/js";
+import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import { useCallback, useEffect } from "react";
+
+export const Notifications = (): JSX.Element | null => {
+ const { nodes, connection, channels } = useDevice();
+ let notificationPermission = Notification.permission;
+
+ useEffect(() => {
+ if (!("Notification" in window)) {
+ console.log("This browser does not support notifications.");
+ notificationPermission = "denied";
+ return;
+ }
+ Notification.requestPermission().then(() => notificationPermission);
+ connection?.events.onMessagePacket.subscribe(messageHandler);
+ return () => connection?.events.onMessagePacket.unsubscribe(messageHandler);
+ }, [notificationPermission, connection]);
+
+ const messageHandler = useCallback(
+ (packet: Types.PacketMetadata) => {
+ if (notificationPermission !== "granted") return;
+
+ const notificationBody = packet.data;
+ let notificationTitle = `New Message from ${nodes.get(packet.from)?.user?.longName ?? `!${numberToHexUnpadded(packet.from)}`}`;
+
+ if (packet.type === "broadcast") {
+ notificationTitle = `New Message in ${channels.get(packet.channel)?.settings?.name ?? "Broadcast"}, from ${nodes.get(packet.from)?.user?.longName ?? `!${numberToHexUnpadded(packet.from)}`}`;
+ }
+
+ new Notification(notificationTitle, {
+ body: notificationBody,
+ icon: "/favicon.ico",
+ });
+ },
+ [notificationPermission, nodes, channels],
+ );
+ return null;
+};