3 changed files with 214 additions and 49 deletions
@ -0,0 +1,177 @@ |
|||
import { createStorageWithMapSupport } from "@core/stores/utils/indexDB.ts"; |
|||
import type { Protobuf } from "@meshtastic/core"; |
|||
import { produce } from "immer"; |
|||
import { create as createStore } from "zustand"; |
|||
import { persist } from "zustand/middleware"; |
|||
import type { NodeError } from "./types"; |
|||
|
|||
const CURRENT_STORE_VERSION = 0; |
|||
|
|||
export interface nodeDB { |
|||
userId: number; |
|||
nodeMap: Map<number, Protobuf.Mesh.NodeInfo>; |
|||
nodeErrors: Map<number, NodeError>; |
|||
|
|||
addNode: (nodeInfo: Protobuf.Mesh.NodeInfo) => void; |
|||
removeNode: (nodeNum: number) => void; |
|||
|
|||
getNodesLength: () => number; |
|||
getNode: (nodeNum: number) => Protobuf.Mesh.NodeInfo | undefined; |
|||
getNodes: ( |
|||
filter?: (node: Protobuf.Mesh.NodeInfo) => boolean, |
|||
) => Protobuf.Mesh.NodeInfo[]; |
|||
|
|||
setNodeError: (nodeNum: number, error: string) => void; |
|||
clearNodeError: (nodeNum: number) => void; |
|||
getNodeError: (nodeNum: number) => NodeError | undefined; |
|||
hasNodeError: (nodeNum: number) => boolean; |
|||
} |
|||
|
|||
export interface nodeDBState { |
|||
addNodeDB: (id: number) => nodeDB; |
|||
removeNodeDB: (id: number) => void; |
|||
getNodeDBs: () => nodeDB[]; |
|||
getNodeDB: (id: number) => nodeDB | undefined; |
|||
} |
|||
|
|||
interface PrivateNodeDBState extends nodeDBState { |
|||
nodeDBs: Map<number, nodeDB>; |
|||
} |
|||
|
|||
type NodeDBPersisted = Pick<PrivateNodeDBState, "nodeDBs">; |
|||
|
|||
export const useNodeDBStore = createStore<PrivateNodeDBState>()( |
|||
persist( |
|||
(set, get) => ({ |
|||
nodeDBs: new Map(), |
|||
addNodeDB: (userId: number) => { |
|||
set( |
|||
produce<PrivateNodeDBState>((draft) => { |
|||
draft.nodeDBs.set(userId, { |
|||
userId: userId, |
|||
nodeMap: new Map(), |
|||
nodeErrors: new Map(), |
|||
|
|||
addNode: (node: Protobuf.Mesh.NodeInfo) => |
|||
set( |
|||
produce((draft: PrivateNodeDBState) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
nodeDB.nodeMap.set(node.num, node); |
|||
}), |
|||
), |
|||
|
|||
removeNode: (nodeNum: number) => |
|||
set( |
|||
produce((draft: PrivateNodeDBState) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
nodeDB.nodeMap.delete(nodeNum); |
|||
}), |
|||
), |
|||
|
|||
setNodeError: (nodeNum: number, error: string) => |
|||
set( |
|||
produce((draft: PrivateNodeDBState) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
nodeDB.nodeErrors.set(nodeNum, { node: nodeNum, error }); |
|||
}), |
|||
), |
|||
|
|||
clearNodeError: (nodeNum: number) => |
|||
set( |
|||
produce((draft: PrivateNodeDBState) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
nodeDB.nodeErrors.delete(nodeNum); |
|||
}), |
|||
), |
|||
|
|||
getNodesLength: () => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
return nodeDB.nodeMap.size; |
|||
}, |
|||
|
|||
getNode: (nodeNum: number) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
return nodeDB.nodeMap.get(nodeNum); |
|||
}, |
|||
|
|||
getNodes: (filter?) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
const allNodes = Array.from(nodeDB.nodeMap.values()).filter( |
|||
(node) => node.num !== nodeDB.userId, |
|||
); |
|||
if (filter) { |
|||
return allNodes.filter(filter); |
|||
} |
|||
return allNodes; |
|||
}, |
|||
|
|||
getNodeError: (nodeNum: number) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
return nodeDB.nodeErrors.get(nodeNum); |
|||
}, |
|||
|
|||
hasNodeError: (nodeNum: number) => { |
|||
const nodeDB = draft.nodeDBs.get(userId); |
|||
if (!nodeDB) { |
|||
throw new Error(`NodeDB for userID ${userId} not found`); |
|||
} |
|||
return nodeDB.nodeErrors.has(nodeNum) ?? false; |
|||
}, |
|||
}); |
|||
}), |
|||
); |
|||
|
|||
const nodeDB = get().nodeDBs.get(userId); |
|||
|
|||
if (!nodeDB) { |
|||
throw new Error( |
|||
`Failed to create or retrieve nodeDB for userID ${userId}`, |
|||
); |
|||
} |
|||
return nodeDB; |
|||
}, |
|||
removeNodeDB: (userId) => { |
|||
set( |
|||
produce<PrivateNodeDBState>((draft) => { |
|||
draft.nodeDBs.delete(userId); |
|||
}), |
|||
); |
|||
}, |
|||
|
|||
getNodeDBs: () => Array.from(get().nodeDBs.values()), |
|||
getNodeDB: (userId) => get().nodeDBs.get(userId), |
|||
}), |
|||
{ |
|||
name: "meshtastic-nodedb-store", |
|||
storage: createStorageWithMapSupport<NodeDBPersisted>(), |
|||
version: CURRENT_STORE_VERSION, |
|||
partialize: (s) => ({ |
|||
nodeDBs: s.nodeDBs, |
|||
}), |
|||
}, |
|||
), |
|||
); |
|||
@ -0,0 +1,6 @@ |
|||
type NodeError = { |
|||
node: number; |
|||
error: string; |
|||
}; |
|||
|
|||
export type { NodeError }; |
|||
Loading…
Reference in new issue