import React from "react" import { ExpoBleConnector } from "./ExpoBleConnector" import useBleStore, { BleDevice, ConnectionProtocol, DeviceRole, PrinterType } from "./ExpoBleStore" export type UseBleOptions = { autoScan?: boolean } export default function useBle(opts: UseBleOptions = {}) { const { autoScan = false } = opts const available = React.useMemo(() => ExpoBleConnector.isAvailable(), []) const removeConnectedDevice = useBleStore((state) => state.removeConnectedDevice) const barcodeReady = useBleStore((state) => state.barcodeReady) const rfidReady = useBleStore((state) => state.rfidReady) const setPrinterType = useBleStore((state) => state.setPrinterType) React.useEffect(() => { if (!autoScan) return if (!available) return ExpoBleConnector.startScan() return () => { ExpoBleConnector.stopScan() } }, [autoScan, available]) const actions = React.useMemo( () => ({ startScan: () => ExpoBleConnector.startScan(), stopScan: () => ExpoBleConnector.stopScan(), connect: (id: string, role: DeviceRole = "unknown", connectionProtocol: ConnectionProtocol = "ble") => ExpoBleConnector.connect(id, role, connectionProtocol), disconnect: (id: string) => ExpoBleConnector.disconnect(id), removeConnectedDevice: (id: string) => removeConnectedDevice(id), sendData: (id: string, s: string, c: string, data: string) => ExpoBleConnector.sendData(id, s, c, data), readCharacteristic: (id: string, s: string, c: string) => ExpoBleConnector.readCharacteristic(id, s, c), isAvailable: () => ExpoBleConnector.isAvailable(), startDataFlow: (callback: CallableFunction, acceptTypes?: string[]) => ExpoBleConnector.startDataFlow(callback, acceptTypes), startRFID: () => ExpoBleConnector.startRFID(), stopRFID: () => ExpoBleConnector.stopRFID(), startBarcode: () => ExpoBleConnector.startBarcode(), stopBarcode: () => ExpoBleConnector.stopBarcode(), readFromClassic: (id: string) => ExpoBleConnector.readFromClassic(id), writeToClassic: (id: string | string[], data: string) => ExpoBleConnector.writeToClassic(id, data), setPrinterType: (id: string, printerType: PrinterType) => setPrinterType(id, printerType), }), [removeConnectedDevice, setPrinterType] ) return { available, barcodeReady, rfidReady, ...actions, } } export function useDeviceIds(throttleMs: number = 5000) { const [deviceIds, setDeviceIds] = React.useState(() => Object.keys(useBleStore.getState().devices)) const showNamedOnly = useBleStore((s) => s.showNamedOnly) React.useEffect(() => { let mounted = true let timer: number | null = null // Helper to get filtered device IDs const getFilteredIds = () => { const allIds = Object.keys(useBleStore.getState().devices) if (!showNamedOnly) return allIds return allIds.filter((id) => { const device = useBleStore.getState().devices[id] return device?.name }) } // initialize lastIds from current store let lastIds = getFilteredIds() setDeviceIds(lastIds) const unsub = useBleStore.subscribe((s) => { const nextIds = getFilteredIds() // shallow compare if (nextIds.length === lastIds.length && nextIds.every((v, i) => v === lastIds[i])) return lastIds = nextIds if (throttleMs <= 0) { if (mounted) setDeviceIds(nextIds) return } if (timer != null) return timer = window.setTimeout(() => { timer = null if (!mounted) return setDeviceIds(lastIds) }, throttleMs) as unknown as number }) return () => { mounted = false unsub() if (timer != null) clearTimeout(timer as unknown as number) } }, [throttleMs, showNamedOnly]) return deviceIds } export function useDevice(id: string, opts: { throttleMs?: number } = { throttleMs: 5000 }) { const { throttleMs = 5000 } = opts // Local state holds the device snapshot that the component will render. const [device, setDevice] = React.useState(() => useBleStore.getState().devices[id]) React.useEffect(() => { let mounted = true let pending = false let lastSnapshot: BleDevice | undefined = device let timer: number | null = null // refresh initial snapshot for this id when id/throttle changes const current = useBleStore.getState().devices[id] lastSnapshot = current if (mounted) setDevice(current) // subscribe to whole store and watch the device key; zustand here exposes // a simple subscribe(listener) API in this setup. const unsub = useBleStore.subscribe((s) => { const next = s.devices[id] // if the snapshot is strictly equal, ignore if (next === lastSnapshot) return // update lastSnapshot and schedule update if needed lastSnapshot = next if (pending) return if (throttleMs <= 0) { if (mounted) setDevice(next) return } pending = true timer = window.setTimeout(() => { pending = false if (!mounted) return setDevice(lastSnapshot) timer = null }, throttleMs) as unknown as number }) return () => { mounted = false unsub() if (timer) clearTimeout(timer) } }, [id, throttleMs]) return device } // Add custom hooks for specific states export function useTrigger() { return useBleStore((state) => state.trigger) } export function useRfidReady() { return useBleStore((state) => state.devices.rfidReady) } export function useBarcodeReady() { return useBleStore((state) => state.devices.barcodeReady) } export function useScanner() { return useBleStore((state) => Object.values(state.connectedDevices).filter((device) => device.role === "scanner")) } export function usePrinter(printerType?: PrinterType) { return useBleStore((state) => { let printers = Object.values(state.connectedDevices).filter((device) => device.role === "printer") if (printerType) { printers = printers.filter((device) => device.printerType === printerType) } return printers }) }