This commit is contained in:
Dan Ditomaso
2025-03-24 15:37:51 -04:00
parent 5a142e671d
commit 0d6c5878fc
12 changed files with 484 additions and 3727 deletions
+14
View File
@@ -0,0 +1,14 @@
import { StateStorage } from "zustand/middleware";
import { get, set, del } from "idb-keyval";
export const zustandIDBStorage: StateStorage = {
getItem: async (name: string): Promise<string | null> => {
return (await get(name)) || null;
},
setItem: async (name: string, value: string): Promise<void> => {
await set(name, value);
},
removeItem: async (name: string): Promise<void> => {
await del(name);
},
};
-22
View File
@@ -30,10 +30,6 @@ export type DialogVariant =
| "unsafeRoles"
| "refreshKeys";
type QueueStatus = {
res: number, free: number, maxlen: number
}
type NodeError = {
node: number;
error: string;
@@ -66,8 +62,6 @@ export interface Device {
// currentMetrics: Protobuf.DeviceMetrics;
pendingSettingsChanges: boolean;
messageDraft: string;
queueStatus: QueueStatus,
isQueueingMessages: boolean,
dialog: {
import: boolean;
QR: boolean;
@@ -116,7 +110,6 @@ export interface Device {
getDialogOpen: (dialog: DialogVariant) => boolean;
processPacket: (data: ProcessPacketParams) => void;
setMessageDraft: (message: string) => void;
setQueueStatus: (status: QueueStatus) => void;
setNodeError: (nodeNum: number, error: string) => void;
clearNodeError: (nodeNum: number) => void;
getNodeError: (nodeNum: number) => NodeError | undefined;
@@ -160,10 +153,6 @@ export const useDeviceStore = createStore<DeviceState>((set, get) => ({
activePage: "messages",
activeNode: 0,
waypoints: [],
queueStatus: {
res: 0, free: 0, maxlen: 0
},
isQueueingMessages: false,
dialog: {
import: false,
QR: false,
@@ -664,17 +653,6 @@ export const useDeviceStore = createStore<DeviceState>((set, get) => ({
}),
);
},
setQueueStatus: (status: QueueStatus) => {
set(
produce<DeviceState>((draft) => {
const device = draft.devices.get(id);
if (device) {
device.queueStatus = status;
device.queueStatus.free >= 10 ? true : false
}
}),
);
},
setNodeError: (nodeNum, error) => {
set(
produce<DeviceState>((draft) => {
+110
View File
@@ -0,0 +1,110 @@
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { produce } from 'immer';
import { Types } from '@meshtastic/core';
import { zustandIDBStorage } from "@core/services/messaging/db.ts";
export interface MessageWithState {
id: number;
from: number;
to: number;
channel: number;
content: string;
state: 'ack' | 'waiting' | 'failed';
type: 'direct' | 'broadcast';
}
type MessageType = 'direct' | 'broadcast';
export interface MessageStore {
messages: {
direct: Record<number, MessageWithState[]>;
broadcast: Record<number, MessageWithState[]>;
};
activeChat: number;
chatType: MessageType;
setActiveChat: (chat: number) => void;
setChatType: (type: MessageType) => void;
addMessage: (message: MessageWithState) => void;
getMessages: (type: MessageType, key: number) => MessageWithState[];
setMessageState: (
type: MessageType,
key: number,
messageId: number,
newState: MessageWithState['state']
) => void;
clearMessages: () => void;
}
export const useMessageStore = create<MessageStore>()(
persist(
(set, get) => ({
messages: {
direct: {},
broadcast: {},
},
activeChat: Types.ChannelNumber.Primary,
chatType: 'broadcast',
setActiveChat: (chat) => {
set(produce((state: MessageStore) => {
state.activeChat = chat;
}));
},
setChatType: (type) => {
set(produce((state: MessageStore) => {
state.chatType = type;
}));
},
addMessage: (message) => {
set(produce((state: MessageStore) => {
const group = message.type === 'direct' ? state.messages.direct : state.messages.broadcast;
const key = message.type === 'direct' ? message.from : message.channel;
if (!group[key]) {
group[key] = [];
}
group[key].push(message);
}));
},
getMessages: (type, key) => {
const group = type === 'direct' ? get().messages.direct : get().messages.broadcast;
return group[key] ?? [];
},
setMessageState: (type, key, messageId, newState) => {
set(produce((state: MessageStore) => {
const group = type === 'direct' ? state.messages.direct : state.messages.broadcast;
const messages = group[key];
if (!messages) return;
const message = messages.find((msg) => msg.id === messageId);
if (message) {
message.state = newState;
}
}));
},
clearMessages: () => {
set(produce((state: MessageStore) => {
state.messages.direct = {};
state.messages.broadcast = {};
}));
},
}),
{
name: 'mesh-messages',
storage: createJSONStorage(() => zustandIDBStorage),
// ✅ No need for partialize magic — simple object storage
partialize: (state) => ({
activeChat: state.activeChat,
chatType: state.chatType,
messages: state.messages,
}),
}
)
);
+10 -4
View File
@@ -1,9 +1,11 @@
import type { Device } from "@core/stores/deviceStore.ts";
import { MeshDevice, Protobuf } from "@meshtastic/core";
import type { MessageStore } from "@core/stores/messageStore.ts";
export const subscribeAll = (
device: Device,
connection: MeshDevice,
messageStore: MessageStore
) => {
let myNodeNum = 0;
@@ -15,6 +17,8 @@ export const subscribeAll = (
});
connection.events.onRoutingPacket.subscribe((routingPacket) => {
console.log("routingPacket", routingPacket);
switch (routingPacket.data.variant.case) {
case "errorReason": {
if (
@@ -81,8 +85,10 @@ export const subscribeAll = (
connection.events.onMessagePacket.subscribe((messagePacket) => {
device.addMessage({
console.log("messagePacket", messagePacket);
messageStore.addMessage({
...messagePacket,
state: messagePacket.from !== myNodeNum ? "ack" : "waiting",
});
});
@@ -105,9 +111,9 @@ export const subscribeAll = (
});
});
connection.events.onQueueStatus.subscribe((queueStatus) => {
device.setQueueStatus(queueStatus);
});
// connection.events.onQueueStatus.subscribe((queueStatus) => {
// device.setQueueStatus(queueStatus);
// });
connection.events.onRoutingPacket.subscribe((routingPacket) => {
if (routingPacket.data.variant.case === "errorReason") {