add enums, improve tests, add styling
This commit is contained in:
@@ -5,23 +5,33 @@ class PacketToMessageDTO {
|
||||
channel: Types.ChannelNumber;
|
||||
to: number;
|
||||
from: number;
|
||||
date: string;
|
||||
date: number; // (timestamp ms)
|
||||
messageId: number;
|
||||
state: MessageState;
|
||||
message: string;
|
||||
type: MessageType;
|
||||
|
||||
constructor(data: Types.PacketMetadata<string>, nodeNum: number) {
|
||||
const payload = data
|
||||
this.channel = data.channel;
|
||||
this.to = data.to;
|
||||
this.from = data.from;
|
||||
this.messageId = data.id;
|
||||
this.state = data.from !== nodeNum ? MessageState.Ack : MessageState.Waiting;
|
||||
this.message = data.data;
|
||||
this.type = (data.type === 'direct') ? MessageType.Direct : MessageType.Broadcast;
|
||||
|
||||
this.channel = payload.channel
|
||||
this.to = payload.to;
|
||||
this.from = payload.from;
|
||||
this.date = new Date(payload.rxTime).toISOString();
|
||||
this.messageId = payload.id;
|
||||
this.state = payload.from !== nodeNum ? "ack" : "waiting";
|
||||
this.message = payload.data;
|
||||
this.type = payload.type;
|
||||
let dateTimestamp = Date.now();
|
||||
if (data.rxTime instanceof Date) {
|
||||
const timeValue = data.rxTime.getTime();
|
||||
|
||||
if (!isNaN(timeValue)) {
|
||||
dateTimestamp = timeValue;
|
||||
}
|
||||
}
|
||||
else if (data.rxTime != null) {
|
||||
console.warn(`Received rxTime in PacketToMessageDTO was not a Date object as expected (type: ${typeof data.rxTime}, value: ${data.rxTime}). Using current time as fallback.`);
|
||||
}
|
||||
this.date = dateTimestamp;
|
||||
}
|
||||
|
||||
toMessage(): Message {
|
||||
@@ -34,7 +44,7 @@ class PacketToMessageDTO {
|
||||
state: this.state,
|
||||
message: this.message,
|
||||
type: this.type,
|
||||
} as Message;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { useMessageStore, Message } from './messageStore';
|
||||
import { useMessageStore, Message, MessageState, MessageType } from './messageStore.ts';
|
||||
import { Types } from '@meshtastic/core';
|
||||
|
||||
vi.mock('./storage/indexDB.ts', () => ({
|
||||
zustandIndexDBStorage: {
|
||||
@@ -12,10 +13,10 @@ vi.mock('./storage/indexDB.ts', () => ({
|
||||
beforeEach(() => {
|
||||
useMessageStore.setState({
|
||||
messages: { direct: {}, broadcast: {} },
|
||||
draft: new Map(),
|
||||
draft: new Map<Types.Destination, string>(),
|
||||
nodeNum: 0,
|
||||
activeChat: 0,
|
||||
chatType: 'broadcast',
|
||||
chatType: MessageType.Broadcast,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,163 +26,312 @@ describe('useMessageStore', () => {
|
||||
expect(useMessageStore.getState().getNodeNum()).toBe(42);
|
||||
});
|
||||
|
||||
it('saves and retrieves a direct message', () => {
|
||||
const message: Message = {
|
||||
type: 'direct',
|
||||
channel: 0,
|
||||
to: 101,
|
||||
from: 202,
|
||||
date: new Date().toISOString(),
|
||||
messageId: 1,
|
||||
state: 'waiting',
|
||||
message: 'Hello Direct',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.direct[101][1]).toEqual(message);
|
||||
it('sets activeChat', () => {
|
||||
useMessageStore.getState().setActiveChat(123);
|
||||
expect(useMessageStore.getState().activeChat).toBe(123);
|
||||
});
|
||||
|
||||
it('updates message state', () => {
|
||||
const message: Message = {
|
||||
type: 'direct',
|
||||
channel: 0,
|
||||
to: 101,
|
||||
from: 202,
|
||||
date: new Date().toISOString(),
|
||||
messageId: 1,
|
||||
state: 'waiting',
|
||||
message: 'Change me',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
useMessageStore.getState().setMessageState({ type: 'direct', key: 101, messageId: 1, newState: 'ack' });
|
||||
expect(useMessageStore.getState().messages.direct[101][1].state).toBe('ack');
|
||||
it('sets chatType', () => {
|
||||
useMessageStore.getState().setChatType(MessageType.Direct);
|
||||
expect(useMessageStore.getState().chatType).toBe(MessageType.Direct);
|
||||
});
|
||||
|
||||
describe('saveMessage', () => {
|
||||
it('saves a direct message', () => {
|
||||
const message: Message = {
|
||||
type: MessageType.Direct,
|
||||
channel: 0,
|
||||
to: 101,
|
||||
from: 202,
|
||||
date: Date.now(),
|
||||
messageId: 1,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Hello Direct',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.direct[101]?.[1]).toEqual(message);
|
||||
});
|
||||
|
||||
it('saves a broadcast message', () => {
|
||||
const message: Message = {
|
||||
type: MessageType.Broadcast,
|
||||
channel: 5,
|
||||
to: 0,
|
||||
from: 303,
|
||||
date: Date.now(),
|
||||
messageId: 100,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Broadcast Message',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.broadcast[5]?.[100]).toEqual(message);
|
||||
});
|
||||
|
||||
it('ensures date is stored as milliseconds', () => {
|
||||
const now = Date.now();
|
||||
const message: Message = {
|
||||
type: MessageType.Direct,
|
||||
channel: 0,
|
||||
to: 101,
|
||||
from: 202,
|
||||
date: now,
|
||||
messageId: 1,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Hello Direct',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.direct[101]?.[1]?.date).toBe(new Date(now).getTime());
|
||||
});
|
||||
});
|
||||
|
||||
describe('setMessageState', () => {
|
||||
it('updates the state of an existing direct message', () => {
|
||||
const message: Message = {
|
||||
type: MessageType.Direct,
|
||||
channel: 0,
|
||||
to: 101,
|
||||
from: 202,
|
||||
date: Date.now(),
|
||||
messageId: 1,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Change me',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
|
||||
useMessageStore.getState().setMessageState({
|
||||
type: MessageType.Direct,
|
||||
key: 101,
|
||||
messageId: 1,
|
||||
newState: MessageState.Ack,
|
||||
});
|
||||
|
||||
expect(useMessageStore.getState().messages.direct[101]?.[1]?.state).toBe(MessageState.Ack);
|
||||
});
|
||||
|
||||
it('updates the state of an existing broadcast message', () => {
|
||||
const message: Message = {
|
||||
type: MessageType.Broadcast,
|
||||
channel: 5,
|
||||
to: 0,
|
||||
from: 303,
|
||||
date: Date.now(),
|
||||
messageId: 100,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Broadcast Message',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
|
||||
useMessageStore.getState().setMessageState({
|
||||
type: MessageType.Broadcast,
|
||||
key: 5,
|
||||
messageId: 100,
|
||||
newState: MessageState.Failed,
|
||||
});
|
||||
|
||||
expect(useMessageStore.getState().messages.broadcast[5]?.[100]?.state).toBe(MessageState.Failed);
|
||||
});
|
||||
|
||||
it('does not update if the message is not found and logs a warning', () => {
|
||||
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
||||
|
||||
useMessageStore.getState().setMessageState({
|
||||
type: MessageType.Direct,
|
||||
key: 999,
|
||||
messageId: 99,
|
||||
newState: MessageState.Ack,
|
||||
});
|
||||
|
||||
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||
'Message not found - type: direct, key: 999, messageId: 99',
|
||||
);
|
||||
|
||||
consoleWarnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
it('clears all messages', () => {
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: 'broadcast',
|
||||
type: MessageType.Broadcast,
|
||||
channel: 5,
|
||||
to: 0,
|
||||
from: 0,
|
||||
date: new Date().toISOString(),
|
||||
from: 303,
|
||||
date: Date.now(),
|
||||
messageId: 100,
|
||||
state: 'waiting',
|
||||
state: MessageState.Waiting,
|
||||
message: 'Broadcast Message',
|
||||
});
|
||||
useMessageStore.getState().clearMessages();
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: MessageType.Direct,
|
||||
channel: 0,
|
||||
to: 101,
|
||||
from: 202,
|
||||
date: Date.now(),
|
||||
messageId: 1,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Hello Direct',
|
||||
});
|
||||
useMessageStore.getState().clearAllMessages();
|
||||
expect(useMessageStore.getState().messages.direct).toEqual({});
|
||||
expect(useMessageStore.getState().messages.broadcast).toEqual({});
|
||||
});
|
||||
|
||||
it('retrieves sorted broadcast messages', () => {
|
||||
const earlier = new Date(Date.now() - 10000).toISOString();
|
||||
const later = new Date().toISOString();
|
||||
describe('getMessages', () => {
|
||||
it('retrieves sorted broadcast messages for a channel', () => {
|
||||
const now = Date.now();
|
||||
const earlier = now - 10000;
|
||||
const later = now;
|
||||
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: 'broadcast',
|
||||
channel: 4,
|
||||
to: 0,
|
||||
from: 0,
|
||||
date: later,
|
||||
messageId: 2,
|
||||
state: 'waiting',
|
||||
message: 'Second',
|
||||
});
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: 'broadcast',
|
||||
channel: 4,
|
||||
to: 0,
|
||||
from: 0,
|
||||
date: earlier,
|
||||
messageId: 1,
|
||||
state: 'waiting',
|
||||
message: 'First',
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: MessageType.Broadcast,
|
||||
channel: 4,
|
||||
to: 0,
|
||||
from: 404,
|
||||
date: later,
|
||||
messageId: 2,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Second',
|
||||
});
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: MessageType.Broadcast,
|
||||
channel: 4,
|
||||
to: 0,
|
||||
from: 404,
|
||||
date: earlier,
|
||||
messageId: 1,
|
||||
state: MessageState.Waiting,
|
||||
message: 'First',
|
||||
});
|
||||
|
||||
const messages = useMessageStore.getState().getMessages(MessageType.Broadcast, { channel: 4 });
|
||||
expect(messages.map((m) => m.message)).toEqual(['First', 'Second']);
|
||||
expect(messages[0]?.date).toBe(earlier);
|
||||
expect(messages[1]?.date).toBe(later);
|
||||
});
|
||||
|
||||
const messages = useMessageStore.getState().getMessages('broadcast', { channel: 4 });
|
||||
expect(messages.map((m) => m.message)).toEqual(['First', 'Second']);
|
||||
});
|
||||
|
||||
// this test is failing and haven't had a chance to debug it
|
||||
it.skip('merges and sorts direct messages by date', () => {
|
||||
const now = new Date();
|
||||
const earlier = new Date(now.getTime() - 10000).toISOString();
|
||||
const later = new Date(now.getTime() + 10000).toISOString();
|
||||
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: 'direct',
|
||||
channel: 0,
|
||||
to: 1, // I am node 1
|
||||
from: 2, // from node 2
|
||||
date: earlier,
|
||||
messageId: 1,
|
||||
state: 'waiting',
|
||||
message: 'Incoming',
|
||||
});
|
||||
useMessageStore.getState().saveMessage({
|
||||
type: 'direct',
|
||||
channel: 0,
|
||||
to: 2, // to node 2
|
||||
from: 1, // I am node 1
|
||||
date: later,
|
||||
messageId: 2,
|
||||
state: 'waiting',
|
||||
message: 'Outgoing',
|
||||
it('returns an empty array for broadcast messages if channel does not exist', () => {
|
||||
const messages = useMessageStore.getState().getMessages(MessageType.Broadcast, { channel: 99 });
|
||||
expect(messages).toEqual([]);
|
||||
});
|
||||
|
||||
const merged = useMessageStore.getState().getMessages('direct', {
|
||||
myNodeNum: 2,
|
||||
otherNodeNum: 1,
|
||||
it('merges and sorts direct messages by date', () => {
|
||||
const myNodeNum = 1;
|
||||
const otherNodeNum = 2;
|
||||
const now = Date.now();
|
||||
const earlier = now - 10000;
|
||||
const later = now + 10000;
|
||||
|
||||
const incomingMessage: Message = {
|
||||
type: MessageType.Direct,
|
||||
channel: 0,
|
||||
to: myNodeNum,
|
||||
from: otherNodeNum,
|
||||
date: earlier,
|
||||
messageId: 1,
|
||||
state: MessageState.Ack,
|
||||
message: 'Incoming from 2',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(incomingMessage);
|
||||
|
||||
const outgoingMessage: Message = {
|
||||
type: MessageType.Direct,
|
||||
channel: 0,
|
||||
to: otherNodeNum,
|
||||
from: myNodeNum,
|
||||
date: later,
|
||||
messageId: 2,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Outgoing from 1',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(outgoingMessage);
|
||||
|
||||
const merged = useMessageStore.getState().getMessages(MessageType.Direct, {
|
||||
myNodeNum: myNodeNum,
|
||||
otherNodeNum: otherNodeNum,
|
||||
});
|
||||
|
||||
expect(merged.length).toBe(2);
|
||||
expect(merged.map((m) => m.message)).toEqual(['Incoming from 2', 'Outgoing from 1']);
|
||||
expect(merged[0]?.date).toBe(earlier);
|
||||
expect(merged[1]?.date).toBe(later);
|
||||
});
|
||||
|
||||
console.log(merged);
|
||||
|
||||
expect(merged.map(m => m.message)).toEqual(['Incoming', 'Outgoing']);
|
||||
it('returns an empty array for direct messages if no messages exist between nodes', () => {
|
||||
const myNodeNum = 1;
|
||||
const otherNodeNum = 2;
|
||||
const messages = useMessageStore.getState().getMessages(MessageType.Direct, {
|
||||
myNodeNum: myNodeNum,
|
||||
otherNodeNum: otherNodeNum,
|
||||
});
|
||||
expect(messages).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets and gets a draft', () => {
|
||||
useMessageStore.getState().setDraft(123, 'Draft text');
|
||||
expect(useMessageStore.getState().getDraft(123)).toBe('Draft text');
|
||||
describe('draft functionality', () => {
|
||||
it('sets and gets a draft message', () => {
|
||||
const key: Types.Destination = 123;
|
||||
useMessageStore.getState().setDraft(key, 'Draft text');
|
||||
expect(useMessageStore.getState().getDraft(key)).toBe('Draft text');
|
||||
});
|
||||
|
||||
it('gets an empty string if no draft exists for a key', () => {
|
||||
const key: Types.Destination = 456;
|
||||
expect(useMessageStore.getState().getDraft(key)).toBe('');
|
||||
});
|
||||
|
||||
it('clears a draft message', () => {
|
||||
const key: Types.Destination = 123;
|
||||
useMessageStore.getState().setDraft(key, 'Draft to clear');
|
||||
useMessageStore.getState().clearDraft(key);
|
||||
expect(useMessageStore.getState().getDraft(key)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
it('clears a draft', () => {
|
||||
useMessageStore.getState().setDraft(123, 'Draft to clear');
|
||||
useMessageStore.getState().clearDraft(123);
|
||||
expect(useMessageStore.getState().getDraft(123)).toBe('');
|
||||
describe('clearMessageByMessageId', () => {
|
||||
it('clears a direct message by messageId', () => {
|
||||
const message: Message = {
|
||||
type: MessageType.Direct,
|
||||
channel: 0,
|
||||
to: 111,
|
||||
from: 222,
|
||||
date: Date.now(),
|
||||
messageId: 42,
|
||||
state: MessageState.Waiting,
|
||||
message: 'To be deleted',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.direct[111]?.[42]).toBeDefined();
|
||||
|
||||
useMessageStore.getState().clearMessageByMessageId(MessageType.Direct, 42);
|
||||
|
||||
expect(useMessageStore.getState().messages.direct[111]?.[42]).toBeUndefined();
|
||||
expect(useMessageStore.getState().messages.direct[111]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('clears a broadcast message by messageId', () => {
|
||||
const message: Message = {
|
||||
type: MessageType.Broadcast,
|
||||
channel: 2,
|
||||
to: 0,
|
||||
from: 333,
|
||||
date: Date.now(),
|
||||
messageId: 77,
|
||||
state: MessageState.Waiting,
|
||||
message: 'Broadcast to delete',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.broadcast[2]?.[77]).toBeDefined();
|
||||
|
||||
useMessageStore.getState().clearMessageByMessageId(MessageType.Broadcast, 77);
|
||||
|
||||
expect(useMessageStore.getState().messages.broadcast[2]?.[77]).toBeUndefined();
|
||||
expect(useMessageStore.getState().messages.broadcast[2]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not throw error if trying to clear a non-existent message', () => {
|
||||
expect(() => {
|
||||
useMessageStore.getState().clearMessageByMessageId(MessageType.Direct, 999);
|
||||
useMessageStore.getState().clearMessageByMessageId(MessageType.Broadcast, 999);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('clears a direct message by messageId', () => {
|
||||
const message: Message = {
|
||||
type: 'direct',
|
||||
channel: 0,
|
||||
to: 111,
|
||||
from: 222,
|
||||
date: new Date().toISOString(),
|
||||
messageId: 42,
|
||||
state: 'waiting',
|
||||
message: 'To be deleted',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.direct[111][42]).toBeDefined();
|
||||
|
||||
useMessageStore.getState().clearMessageByMessageId('direct', 42);
|
||||
expect(useMessageStore.getState().messages.direct[111]?.[42]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('clears a broadcast message by messageId', () => {
|
||||
const message: Message = {
|
||||
type: 'broadcast',
|
||||
channel: 2,
|
||||
to: 0,
|
||||
from: 0,
|
||||
date: new Date().toISOString(),
|
||||
messageId: 77,
|
||||
state: 'waiting',
|
||||
message: 'Broadcast to delete',
|
||||
};
|
||||
useMessageStore.getState().saveMessage(message);
|
||||
expect(useMessageStore.getState().messages.broadcast[2][77]).toBeDefined();
|
||||
|
||||
useMessageStore.getState().clearMessageByMessageId('broadcast', 77);
|
||||
expect(useMessageStore.getState().messages.broadcast[2]?.[77]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,25 +4,22 @@ import { produce } from 'immer';
|
||||
import { Types } from '@meshtastic/core';
|
||||
import { zustandIndexDBStorage } from "./storage/indexDB.ts";
|
||||
|
||||
const MESSAGE_STATES = {
|
||||
ack: "ack",
|
||||
waiting: "waiting",
|
||||
failed: 'failed',
|
||||
};
|
||||
export type MessageState = keyof typeof MESSAGE_STATES;
|
||||
export enum MessageState {
|
||||
Ack = "ack",
|
||||
Waiting = "waiting",
|
||||
Failed = "failed",
|
||||
}
|
||||
|
||||
export const ChatTypes = {
|
||||
DIRECT: "direct",
|
||||
BROADCAST: "broadcast",
|
||||
} as const;
|
||||
|
||||
export type MessageType = "broadcast" | "direct";
|
||||
export enum MessageType {
|
||||
Direct = "direct",
|
||||
Broadcast = "broadcast",
|
||||
}
|
||||
|
||||
interface MessageBase {
|
||||
channel: Types.ChannelNumber;
|
||||
to: number;
|
||||
from: number;
|
||||
date: string;
|
||||
date: number; // Unix timestamp in milliseconds
|
||||
messageId: number;
|
||||
state: MessageState;
|
||||
message: string;
|
||||
@@ -32,7 +29,7 @@ interface GenericMessage<T extends MessageType> extends MessageBase {
|
||||
type: T;
|
||||
}
|
||||
|
||||
export type Message = GenericMessage<'direct'> | GenericMessage<'broadcast'>;
|
||||
export type Message = GenericMessage<MessageType.Direct> | GenericMessage<MessageType.Broadcast>;
|
||||
|
||||
export interface MessageStore {
|
||||
messages: {
|
||||
@@ -55,10 +52,10 @@ export interface MessageStore {
|
||||
messageId: number;
|
||||
newState?: MessageState;
|
||||
}) => void;
|
||||
clearMessages: () => void;
|
||||
getMessages: (type: MessageType, options: { myNodeNum?: number; otherNodeNum?: number; channel?: number }) => Message[];
|
||||
getDraft: (key: Types.Destination) => string;
|
||||
setDraft: (key: Types.Destination, message: string) => void;
|
||||
clearAllMessages: () => void;
|
||||
clearMessageByMessageId: (type: MessageType, messageId: number) => void;
|
||||
clearDraft: (key: Types.Destination) => void;
|
||||
|
||||
@@ -73,7 +70,7 @@ export const useMessageStore = create<MessageStore>()(
|
||||
},
|
||||
draft: new Map<number, string>(),
|
||||
activeChat: 0,
|
||||
chatType: 'broadcast',
|
||||
chatType: MessageType.Broadcast,
|
||||
nodeNum: 0,
|
||||
setNodeNum: (nodeNum) => {
|
||||
set(produce((state: MessageStore) => {
|
||||
@@ -94,22 +91,32 @@ export const useMessageStore = create<MessageStore>()(
|
||||
saveMessage: (message) => {
|
||||
set(produce((state: MessageStore) => {
|
||||
const group = state.messages[message.type];
|
||||
const key = message.type === 'direct' ? Number(message.to) : Number(message.channel);
|
||||
// Direct messages are keyed by the RECIPIENT's node number (`message.to`)
|
||||
// Broadcast messages are keyed by the channel number (`message.channel`)
|
||||
const key = message.type === MessageType.Direct ? Number(message.to) : Number(message.channel);
|
||||
if (!group[key]) {
|
||||
group[key] = {};
|
||||
}
|
||||
group[key][message.messageId] = message;
|
||||
const messageToSave = { ...message };
|
||||
group[key][message.messageId] = messageToSave;
|
||||
}));
|
||||
},
|
||||
setMessageState: ({ type, key, messageId, newState = 'ack' }) => {
|
||||
const group = get().messages[type];
|
||||
const messageMap = group[key];
|
||||
|
||||
if (!messageMap || !messageMap[messageId]) return;
|
||||
|
||||
set(produce((state: MessageStore) => {
|
||||
state.messages[type][key][messageId].state = newState;
|
||||
}));
|
||||
setMessageState: ({
|
||||
type,
|
||||
key,
|
||||
messageId,
|
||||
newState = MessageState.Ack,
|
||||
}) => {
|
||||
set(
|
||||
produce((state: MessageStore) => {
|
||||
const message = state.messages[type]?.[key]?.[messageId];
|
||||
if (message) {
|
||||
message.state = newState;
|
||||
} else {
|
||||
console.warn(`Message not found - type: ${type}, key: ${key}, messageId: ${messageId}`);
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
clearMessages: () => {
|
||||
set(produce((state: MessageStore) => {
|
||||
@@ -120,24 +127,26 @@ export const useMessageStore = create<MessageStore>()(
|
||||
getMessages: (type, options) => {
|
||||
const state = get();
|
||||
|
||||
if (type === 'broadcast' && options.channel !== undefined) {
|
||||
if (type === MessageType.Broadcast && options.channel !== undefined) {
|
||||
const messageMap = state.messages.broadcast[options.channel] ?? {};
|
||||
return Object.values(messageMap).sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
||||
return Object.values(messageMap).sort((a, b) => a.date - b.date);
|
||||
}
|
||||
|
||||
if (type === 'direct' && options.myNodeNum !== undefined && options.otherNodeNum !== undefined) {
|
||||
const receivedMap = state.messages.direct[options.otherNodeNum] ?? {};
|
||||
const sentMap = state.messages.direct[options.myNodeNum] ?? {};
|
||||
if (type === MessageType.Direct && options.myNodeNum !== undefined && options.otherNodeNum !== undefined) {
|
||||
// Messages TO the other node (sent by me) are keyed under their nodeNum
|
||||
const messagesToOtherNodeMap = state.messages.direct[options.otherNodeNum] ?? {};
|
||||
const sentByMe = Object.values(messagesToOtherNodeMap);
|
||||
|
||||
// Pull messages where I am the sender and otherNode is the receiver
|
||||
const sentMessages = Object.values(sentMap).filter(msg => msg.to === options.otherNodeNum);
|
||||
|
||||
// Pull messages received from otherNode
|
||||
const receivedMessages = Object.values(receivedMap);
|
||||
// Messages TO me (potentially from the other node) are keyed under my nodeNum
|
||||
const messagesToMeMap = state.messages.direct[options.myNodeNum] ?? {};
|
||||
// Filter messages TO me to find the ones FROM the specific other node
|
||||
const sentByOtherNode = Object.values(messagesToMeMap).filter(
|
||||
(msg) => msg.from === options.otherNodeNum
|
||||
);
|
||||
|
||||
// Merge and sort chronologically
|
||||
return [...receivedMessages, ...sentMessages].sort(
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||
return [...sentByMe, ...sentByOtherNode].sort(
|
||||
(a, b) => a.date - b.date
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,6 +179,12 @@ export const useMessageStore = create<MessageStore>()(
|
||||
state.draft.delete(key);
|
||||
}));
|
||||
},
|
||||
clearAllMessages: () => {
|
||||
set(produce((state: MessageStore) => {
|
||||
state.messages.direct = {};
|
||||
state.messages.broadcast = {};
|
||||
}));
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: 'meshtastic-message-store',
|
||||
|
||||
Reference in New Issue
Block a user