12 changed files with 648 additions and 3891 deletions
File diff suppressed because it is too large
@ -33,7 +33,7 @@ |
|||
] |
|||
}, |
|||
"homepage": "https://meshtastic.org", |
|||
"dependencies": { |
|||
"ddbependencies": { |
|||
"@bufbuild/protobuf": "^2.2.3", |
|||
"@meshtastic/core": "npm:@jsr/[email protected]", |
|||
"@meshtastic/js": "npm:@jsr/[email protected]", |
|||
@ -61,6 +61,7 @@ |
|||
"clsx": "^2.1.1", |
|||
"cmdk": "^1.0.4", |
|||
"crypto-random-string": "^5.0.0", |
|||
"idb-keyval": "^6.2.1", |
|||
"immer": "^10.1.1", |
|||
"js-cookie": "^3.0.5", |
|||
"lucide-react": "^0.477.0", |
|||
@ -100,7 +101,7 @@ |
|||
"tar": "^7.4.3", |
|||
"testing-library": "^0.0.2", |
|||
"typescript": "^5.8.2", |
|||
"vite": "^6.2.0", |
|||
"vite": "^6.2.3", |
|||
"vitest": "^3.0.7", |
|||
"vite-plugin-pwa": "^0.21.1" |
|||
} |
|||
|
|||
@ -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); |
|||
}, |
|||
}; |
|||
@ -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, |
|||
}), |
|||
} |
|||
) |
|||
); |
|||
Loading…
Reference in new issue