From 5d01b8a12f88c59217cbc92356bd83e8d4e322b4 Mon Sep 17 00:00:00 2001 From: AndrewH Date: Mon, 11 Nov 2024 10:23:36 +1100 Subject: [PATCH] merge birdpump notif sound, message persistence and notification icon --- public/notification.wav | Bin 0 -> 65442 bytes src/components/DeviceSelector.tsx | 12 ++ src/components/Sidebar.tsx | 6 +- src/components/UI/Sidebar/sidebarButton.tsx | 8 +- src/core/stores/appStore.ts | 10 ++ src/core/stores/deviceStore.ts | 166 ++++++++++++++++++-- src/core/subscriptions.ts | 4 + src/core/utils/notify.ts | 17 ++ src/index.css | 8 + src/pages/Messages.tsx | 5 + 10 files changed, 221 insertions(+), 15 deletions(-) create mode 100644 public/notification.wav create mode 100644 src/core/utils/notify.ts diff --git a/public/notification.wav b/public/notification.wav new file mode 100644 index 0000000000000000000000000000000000000000..1e9140fa2b643f58082193d1e9834580edc4491d GIT binary patch literal 65442 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 J2>hQC_#elAZ=3)C literal 0 HcmV?d00001 diff --git a/src/components/DeviceSelector.tsx b/src/components/DeviceSelector.tsx index 42be3ecf..bc4d6308 100644 --- a/src/components/DeviceSelector.tsx +++ b/src/components/DeviceSelector.tsx @@ -5,12 +5,15 @@ import { useAppStore } from "@core/stores/appStore.ts"; import { useDeviceStore } from "@core/stores/deviceStore.ts"; import { Hashicon } from "@emeraldpay/hashicon-react"; import { + BellIcon, + BellOffIcon, HomeIcon, LanguagesIcon, MoonIcon, PlusIcon, SearchIcon, SunIcon, + TerminalIcon, } from "lucide-react"; export const DeviceSelector = (): JSX.Element => { @@ -20,6 +23,8 @@ export const DeviceSelector = (): JSX.Element => { setSelectedDevice, darkMode, setDarkMode, + notifications, + setNotifications, setCommandPaletteOpen, setConnectDialogOpen, } = useAppStore(); @@ -61,6 +66,13 @@ export const DeviceSelector = (): JSX.Element => {
+
- + + {pages.map((link) => ( { setActivePage(link.page); }} active={link.page === activePage} + unread={link.name == "Messages" ? hasUnread(null, null) : false} /> ))} diff --git a/src/components/UI/Sidebar/sidebarButton.tsx b/src/components/UI/Sidebar/sidebarButton.tsx index c3f30e79..cb459030 100644 --- a/src/components/UI/Sidebar/sidebarButton.tsx +++ b/src/components/UI/Sidebar/sidebarButton.tsx @@ -1,11 +1,12 @@ import { Button } from "@components/UI/Button.tsx"; -import type { LucideIcon } from "lucide-react"; +import {LucideMessageSquareDot, type LucideIcon } from "lucide-react"; export interface SidebarButtonProps { label: string; active?: boolean; Icon?: LucideIcon; element?: JSX.Element; + unread: boolean; onClick?: () => void; } @@ -14,6 +15,7 @@ export const SidebarButton = ({ active, Icon, element, + unread, onClick, }: SidebarButtonProps): JSX.Element => ( ); diff --git a/src/core/stores/appStore.ts b/src/core/stores/appStore.ts index c7bc2c62..315d908d 100644 --- a/src/core/stores/appStore.ts +++ b/src/core/stores/appStore.ts @@ -26,6 +26,7 @@ interface AppState { rasterSources: RasterSource[]; commandPaletteOpen: boolean; darkMode: boolean; + notifications: boolean; nodeNumToBeRemoved: number; accent: AccentColor; connectDialogOpen: boolean; @@ -39,6 +40,7 @@ interface AppState { removeDevice: (deviceId: number) => void; setCommandPaletteOpen: (open: boolean) => void; setDarkMode: (enabled: boolean) => void; + setNotifications: (enabled: boolean) => void; setNodeNumToBeRemoved: (nodeNum: number) => void; setAccent: (color: AccentColor) => void; setConnectDialogOpen: (open: boolean) => void; @@ -50,6 +52,7 @@ export const useAppStore = create()((set) => ({ currentPage: "messages", rasterSources: [], commandPaletteOpen: false, + notifications: true, darkMode: localStorage.getItem("theme-dark") !== null ? localStorage.getItem("theme-dark") === "true" @@ -106,6 +109,13 @@ export const useAppStore = create()((set) => ({ }), ); }, + setNotifications: (enabled: boolean) => { + set( + produce((draft) => { + draft.notifications = enabled; + }), + ); + }, setNodeNumToBeRemoved: (nodeNum) => set((state) => ({ nodeNumToBeRemoved: nodeNum, diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts index a716a85b..3039cfa2 100644 --- a/src/core/stores/deviceStore.ts +++ b/src/core/stores/deviceStore.ts @@ -9,6 +9,7 @@ export type Page = "messages" | "map" | "config" | "channels" | "nodes"; export interface MessageWithState extends Types.PacketMetadata { state: MessageState; + unread: boolean; } export type MessageState = "ack" | "waiting" | Protobuf.Mesh.Routing_Error; @@ -95,6 +96,8 @@ export interface Device { setDialogOpen: (dialog: DialogVariant, open: boolean) => void; processPacket: (data: ProcessPacketParams) => void; setMessageDraft: (message: string) => void; + hasUnread: (channel: number | null, nodeNum: number | null) => boolean; + markAllRead: (channel: number | null, nodeNum: number | null) => void; } export interface DeviceState { @@ -112,7 +115,7 @@ export const useDeviceStore = create((set, get) => ({ remoteDevices: new Map(), addDevice: (id: number) => { - set( + set( produce((draft) => { draft.devices.set(id, { id, @@ -312,10 +315,10 @@ export const useDeviceStore = create((set, get) => ({ }, setHardware: (hardware: Protobuf.Mesh.MyNodeInfo) => { set( - produce((draft) => { - const device = draft.devices.get(id); + produce((draft) => { + const device = draft.devices.get(id); if (device) { - device.hardware = hardware; + device.hardware = hardware; } }), ); @@ -392,6 +395,30 @@ export const useDeviceStore = create((set, get) => ({ return; } device.channels.set(channel.index, channel); + + const messageGroup = device.messages["broadcast"]; + + let msgJson = localStorage.getItem("msg_" + channel.index) + if (msgJson !== null) { + let storedMsgs = JSON.parse(msgJson) + for (let a = 0; a < storedMsgs.length; a++) { + let message = storedMsgs[a] + if (channel.index == 0) { + message.unread = false + } + message.rxTime = new Date(message.rxTime) + message.state = "ack" + let messageIndex = message.channel + let messages = messageGroup.get(messageIndex); + if (messages === undefined) { + messages = [message] + } else { + messages.push(message); + } + messageGroup.set(messageIndex, messages) + } + } + }), ); }, @@ -421,6 +448,26 @@ export const useDeviceStore = create((set, get) => ({ return; } device.nodes.set(nodeInfo.num, nodeInfo); + + const messageGroup = device.messages["direct"]; + + let msgJson = localStorage.getItem("msg_" + nodeInfo.num) + if (msgJson !== null) { + let storedMsgs = JSON.parse(msgJson) + for (let a = 0; a < storedMsgs.length; a++) { + let message = storedMsgs[a] + message.rxTime = new Date(message.rxTime) + message.state = "ack" + let messageIndex = (message.from === device.hardware.myNodeNum) ? message.to : message.from + let messages = messageGroup.get(messageIndex); + if (messages === undefined) { + messages = [message] + } else { + messages.push(message); + } + messageGroup.set(messageIndex, messages) + } + } }), ); }, @@ -474,9 +521,10 @@ export const useDeviceStore = create((set, get) => ({ ); }, addMessage: (message) => { + set( produce((draft) => { - const device = draft.devices.get(id); + let device = draft.devices.get(id); if (!device) { return; } @@ -487,14 +535,19 @@ export const useDeviceStore = create((set, get) => ({ ? message.to : message.from : message.channel; - const messages = messageGroup.get(messageIndex); + let messages = messageGroup.get(messageIndex); - if (messages) { - messages.push(message); - messageGroup.set(messageIndex, messages); + if (message.from !== device.hardware.myNodeNum) { + message.unread = true; + } + + if (messages === undefined) { + messages = [message]; } else { - messageGroup.set(messageIndex, [message]); + messages.push(message); } + messageGroup.set(messageIndex, messages); + localStorage.setItem("msg_" + messageIndex, JSON.stringify(messages)) }), ); }, @@ -513,8 +566,8 @@ export const useDeviceStore = create((set, get) => ({ addTraceRoute: (traceroute) => { set( produce((draft) => { - console.log("addTraceRoute called"); - console.log(traceroute); + //console.log("addTraceRoute called"); + //console.log(traceroute); const device = draft.devices.get(id); if (!device) { return; @@ -632,6 +685,94 @@ export const useDeviceStore = create((set, get) => ({ }), ); }, + hasUnread: (channel: number | null, nodeNum: number | null) => { + const device = get().devices.get(id); + + if (device == null) { + return false + } + + if (channel == null && nodeNum == null) { + let channelsWithMessages = device.messages["broadcast"] + for (let [channelIndex, messages] of channelsWithMessages) { + for (let msg of messages) { + if (msg.unread) { + return true + } + } + } + channelsWithMessages = device.messages["direct"] + for (let [nodeIndex, messages] of channelsWithMessages) { + for (let msg of messages) { + if (msg.unread) { + return true + } + } + } + } + + if (channel !== null) { + const channelsWithMessages = device.messages["broadcast"] + let messages = channelsWithMessages.get(channel) + if (messages == null) { + return false + } + for (let msg of messages) { + if (msg.unread) { + return true + } + } + } + + if (nodeNum !== null) { + const channelsWithMessages = device.messages["direct"] + let messages = channelsWithMessages.get(nodeNum) + if (messages == null) { + return false + } + for (let msg of messages) { + if (msg.unread) { + return true + } + } + } + + return false + }, + markAllRead: (channel: number | null, nodeNum: number | null) => { + set( + produce((draft) => { + const device = draft.devices.get(id); + if (device == null) { + return + } + if (channel !== null) { + const channelsWithMessages = device.messages["broadcast"] + let messages = channelsWithMessages.get(channel) + if (messages == null) { + return + } + for (let msg of messages) { + msg.unread = false + } + channelsWithMessages.set(channel, messages); + localStorage.setItem("msg_" + channel, JSON.stringify(messages)) + } + if (nodeNum !== null) { + const directsWithMessages = device.messages["direct"] + let messages = directsWithMessages.get(nodeNum) + if (messages == null) { + return + } + for (let msg of messages) { + msg.unread = false + } + directsWithMessages.set(nodeNum, messages); + localStorage.setItem("msg_" + nodeNum, JSON.stringify(messages)) + } + }) + ) + } }); }), ); @@ -654,6 +795,7 @@ export const useDeviceStore = create((set, get) => ({ getDevices: () => Array.from(get().devices.values()), getDevice: (id) => get().devices.get(id), + })); export const DeviceContext = createContext(undefined); diff --git a/src/core/subscriptions.ts b/src/core/subscriptions.ts index afba12b7..41631678 100644 --- a/src/core/subscriptions.ts +++ b/src/core/subscriptions.ts @@ -1,5 +1,6 @@ import type { Device } from "@core/stores/deviceStore.ts"; import { Protobuf, type Types } from "@meshtastic/js"; +import { playNotificationSound } from "./utils/notify"; export const subscribeAll = ( device: Device, @@ -84,6 +85,9 @@ export const subscribeAll = ( ...messagePacket, state: messagePacket.from !== myNodeNum ? "ack" : "waiting", }); + if (messagePacket.from !== myNodeNum) { + playNotificationSound(); + } }); connection.events.onTraceRoutePacket.subscribe((traceRoutePacket) => { diff --git a/src/core/utils/notify.ts b/src/core/utils/notify.ts new file mode 100644 index 00000000..adf2f10e --- /dev/null +++ b/src/core/utils/notify.ts @@ -0,0 +1,17 @@ +import { useAppStore } from "@core/stores/appStore.ts"; + +const notificationSound = new Audio("/notification.wav"); //change sound if needed + +let isPlaying = false; + +export const playNotificationSound = () => { + const { notifications } = useAppStore.getState(); + if (notifications && !isPlaying) { + isPlaying = true; + notificationSound.play(); + + notificationSound.onended = () => { + isPlaying = false; + }; + } +}; diff --git a/src/index.css b/src/index.css index 8a35066d..f173eb20 100644 --- a/src/index.css +++ b/src/index.css @@ -100,3 +100,11 @@ img { -drag: none; -webkit-user-drag: none; } + +.notifyBadge { + background-color: #f25555; + border-radius: 50%; + padding:5px; + color: #ffffff; + font-weight: bold; +} \ No newline at end of file diff --git a/src/pages/Messages.tsx b/src/pages/Messages.tsx index 28a35d2c..39a5c8ac 100644 --- a/src/pages/Messages.tsx +++ b/src/pages/Messages.tsx @@ -15,6 +15,7 @@ import { useState } from "react"; export const MessagesPage = (): JSX.Element => { const { channels, nodes, hardware, messages, traceroutes, connection } = useDevice(); + const { hasUnread, markAllRead } = useDevice(); const [chatType, setChatType] = useState("broadcast"); const [activeChat, setActiveChat] = useState( @@ -48,10 +49,12 @@ export const MessagesPage = (): JSX.Element => { } active={activeChat === channel.index} onClick={() => { + markAllRead(channel.index, null) setChatType("broadcast"); setActiveChat(channel.index); }} element={} + unread={hasUnread(channel.index, null)} /> ))} @@ -62,10 +65,12 @@ export const MessagesPage = (): JSX.Element => { label={node.user?.longName ?? `!${numberToHexUnpadded(node.num)}`} active={activeChat === node.num} onClick={() => { + markAllRead(null, node.num) setChatType("direct"); setActiveChat(node.num); }} element={} + unread={hasUnread(null, node.num)} /> ))}