import { create } from "zustand" import { Subscription } from "react-native-ble-plx" // Ensure consistent Subscription type import AsyncStorage from "@react-native-async-storage/async-storage" import { BluetoothDevice as ClassicDevice } from "react-native-bluetooth-classic" import { persist, createJSONStorage, subscribeWithSelector } from "zustand/middleware" export type PrinterType = "labelPrinter" | "rfidPrinter" export type DeviceRole = "scanner" | "printer" | "unknown" export type ConnectionProtocol = "ble" | "classic" | "other" export type CharacteristicData = { uuid: string serviceUUID: string value?: string // base64 or hex depending on usage rawBase64?: string decoded?: string } export type BleDevice = { id: string name?: string | null connected: boolean role: DeviceRole connectionProtocol: ConnectionProtocol rssi?: number | null serviceUUIDs?: string[] | null characteristics: Record manuallyDisconnected?: boolean // Added property to track manual disconnections classicConnection?: ClassicDevice // Classic Bluetooth connection instance printerType?: PrinterType // Distinguish printer types if role is printer } // Extend the Subscription type to include transactionId export interface ExtendedSubscription extends Subscription { transactionId?: number } export type BleState = { devices: Record connectedDevices: Record showNamedOnly: boolean trigger: boolean subscriptions: ExtendedSubscription[] // Use react-native-ble-plx Subscription type rfidReady: boolean barcodeReady: boolean setShowNamedOnly: (value: boolean) => void setTrigger: (value: boolean) => void setSubscriptions: (subscriptions: Subscription[]) => void // Update subscriptions setRfidReady: (value: boolean) => void setBarcodeReady: (value: boolean) => void addDevice: (device: Partial & { id: string }) => void addConnectedDevice: (device: BleDevice) => void removeConnectedDevice: (id: string) => void removeDevice: (id: string) => void updateDevice: (id: string, patch: Partial) => void updateConnectedDevice: (id: string, patch: Partial) => void clear: () => void resetConnections: () => void setPrinterType: (id: string, printerType: PrinterType) => void } const useBleStore = create()( subscribeWithSelector( persist( (set, get) => ({ devices: {} as Record, connectedDevices: {} as Record, showNamedOnly: true, trigger: false, // Default value for trigger state subscriptions: [], // Initialize subscriptions as an empty array rfidReady: false, barcodeReady: false, setShowNamedOnly(value: boolean) { set({ showNamedOnly: value }) }, setTrigger(value: boolean) { set({ trigger: value }) }, setSubscriptions(subscriptions: Subscription[]) { set({ subscriptions }) }, setRfidReady(value: boolean) { set({ rfidReady: value }) }, setBarcodeReady(value: boolean) { set({ barcodeReady: value }) }, addDevice(device: Partial & { id: string }) { set((state: BleState) => ({ devices: { ...state.devices, [device.id]: { id: device.id, name: device.name ?? null, connected: device.connected ?? false, connectionProtocol: device.connectionProtocol ?? "other", role: device.role ?? "unknown", // Use a valid DeviceRole value rssi: device.rssi ?? null, serviceUUIDs: device.serviceUUIDs ?? [], characteristics: device.characteristics ?? {}, }, }, })) }, addConnectedDevice(device: BleDevice) { set((state) => ({ connectedDevices: { ...state.connectedDevices, [device.id]: device, }, })) }, removeConnectedDevice(id: string) { set((state) => { const updated = { ...state.connectedDevices } delete updated[id] return { connectedDevices: updated } }) }, removeDevice(id: string) { set((state) => { const next = { ...state.devices } delete next[id] return { devices: next } }) }, updateDevice(id: string, patch: Partial) { set((state) => { const prev = state.devices[id] if (!prev) return state const updated: BleDevice = { ...prev, ...patch } return { devices: { ...state.devices, [id]: updated } } }) }, updateConnectedDevice(id: string, patch: Partial) { set((state) => { const prev = state.connectedDevices[id] if (!prev) return state // Compare the patch with the existing state to avoid redundant updates const updated: BleDevice = { ...prev, ...patch } if (JSON.stringify(prev) === JSON.stringify(updated)) { return state // No changes, skip the update } return { connectedDevices: { ...state.connectedDevices, [id]: updated } } }) }, setPrinterType(id: string, printerType: PrinterType) { set((state) => { const prev = state.devices[id] if (!prev) return state const updated: BleDevice = { ...prev, printerType } return { devices: { ...state.devices, [id]: updated } } }) }, clear() { set({ devices: {}, connectedDevices: {} }) }, resetConnections: () => { const updated = Object.fromEntries( Object.entries(get().connectedDevices).map(([id, d]) => [ id, { ...d, connected: false, manuallyDisconnected: false, classicConnection: undefined, serviceUUIDs: [], characteristics: {} } as BleDevice, ]) ) console.log("Reset connections in BLE store") set({ devices: {}, connectedDevices: updated, trigger: false, rfidReady: false, barcodeReady: false }) }, }), { name: "ble-store", // Key for localStorage or AsyncStorage storage: createJSONStorage(() => AsyncStorage), // Use JSON storage adapter for AsyncStorage onRehydrateStorage: () => (state, error) => { console.log("Rehydrating BLE store...") if (error) { console.error("Error rehydrating BLE store:", error) return } // Use Zustand set() to update store after rehydration if (state?.connectedDevices) { console.log("Rehydrating BLE store... useBleStore.getState().resetConnections()") // Run logic cleanly via store method useBleStore.getState().resetConnections() } }, } ) ) ) export default useBleStore export const selectDevices = (s: BleState) => s.devices export const selectConnectedDevices = (s: BleState) => s.connectedDevices export const selectShowNamedOnly = (s: BleState) => s.showNamedOnly export const selectSetShowNamedOnly = (s: BleState) => s.setShowNamedOnly export const selectTrigger = (s: BleState) => s.trigger export const selectSetTrigger = (s: BleState) => s.setTrigger export const selectSubscriptions = (s: BleState) => s.subscriptions export const selectSetSubscriptions = (s: BleState) => s.setSubscriptions