diff --git a/Cargo.lock b/Cargo.lock index a51dedb..8a94802 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,7 +359,7 @@ dependencies = [ [[package]] name = "bluetooth" -version = "0.4.1" +version = "0.4.2" dependencies = [ "chrono", "dashmap", @@ -379,7 +379,7 @@ dependencies = [ [[package]] name = "bluetooth_battery_monitor" -version = "0.4.1" +version = "0.4.2" dependencies = [ "bluetooth", "chrono", @@ -4353,7 +4353,7 @@ dependencies = [ [[package]] name = "timer" -version = "0.4.1" +version = "0.4.2" dependencies = [ "dashmap", "once_cell", diff --git a/crates/bluetooth/src/device/windows/device_info/mod.rs b/crates/bluetooth/src/device/windows/device_info/mod.rs index ea7ec41..22c5ad5 100644 --- a/crates/bluetooth/src/device/windows/device_info/mod.rs +++ b/crates/bluetooth/src/device/windows/device_info/mod.rs @@ -108,20 +108,31 @@ impl BluetoothDeviceInfo { /// - is_connected /// - last_used /// - last_updated - pub(crate) fn update_info( - &mut self, - is_connected: bool, - ) -> Result<(), BluetoothDeviceInfoError> { + pub(crate) fn update_info(&mut self) -> Result<(), BluetoothDeviceInfoError> { let device = DeviceInstance::new(self.device_instance); self.battery_level = device.get_device_property(&DEVPKEY_Bluetooth_Battery)?; self.last_updated = LocalTime::now(); // NOTE: `is_connected` & `last_used` must be taken by device_search to get a decent value, so the information is merged. - self.is_connected = is_connected; - if is_connected { - self.last_used = LocalTime::now(); - } + let sys_device = { + let mut devices = match super::device_searcher::get_bluetooth_devices() { + Ok(devices) => devices, + Err(err) => { + tracing::error!("{err}"); + return Ok(()); + } + }; + devices.remove(&self.address) + }; + // NOTE: `is_connected` & `last_used` must be taken by device_search to get a decent value, so the information is merged. + self.is_connected = sys_device + .as_ref() + .map(|device| device.is_connected) + .unwrap_or_default(); + self.last_used = sys_device + .map(|device| device.last_used) + .unwrap_or_default(); Ok(()) } diff --git a/crates/bluetooth/src/device/windows/watch.rs b/crates/bluetooth/src/device/windows/watch.rs index 48438b2..7d90e6b 100644 --- a/crates/bluetooth/src/device/windows/watch.rs +++ b/crates/bluetooth/src/device/windows/watch.rs @@ -97,19 +97,21 @@ impl Watcher { match DEVICES.get_mut(&address) { Some(mut dev) => { - let map = device.Properties()?; - // for prop in &map { // dbg!(address); // type_to_value(prop)?; // } - // In my tests, the only properties selected by kind that exist are the ones the device was able to get via pnp. - // Therefore, any device that exists in DashMap should be able to retrieve it. - let is_connected = - map.HasKey(&HSTRING::from(IS_CONNECTED)).unwrap_or_default(); + // Not use this pattern + // ``` + // let map = device.Properties()?; + // let is_connected = map.HasKey(&HSTRING::from(IS_CONNECTED)).unwrap_or_default(); + // ``` + // Why? + // There were times when the information from props was unreliable. + // The information is obtained when connected, but for some reason this value may not be obtained when not connected. - match dev.value_mut().update_info(is_connected) { + match dev.value_mut().update_info() { Ok(()) => update_fn(dev.value()), Err(err) => tracing::error!("{err}"), } diff --git a/gui/backend/src/cmd/interval.rs b/gui/backend/src/cmd/interval.rs index ca0bce5..bcea747 100644 --- a/gui/backend/src/cmd/interval.rs +++ b/gui/backend/src/cmd/interval.rs @@ -61,7 +61,7 @@ pub async fn restart_interval(app: AppHandle) { err_log!(update_tray_inner(dev_name, battery_level, dev.is_connected)); }; - err_log!(window.emit("bt_monitor://update_devices", &devices)); + err_log!(window.emit("bt_monitor://restart_devices", &devices)); // Replace all for (address, device) in devices { diff --git a/gui/frontend/src/components/organisms/BluetoothGrid/DevicesProvider.tsx b/gui/frontend/src/components/organisms/BluetoothGrid/DevicesProvider.tsx index 3a46a27..fa065c5 100644 --- a/gui/frontend/src/components/organisms/BluetoothGrid/DevicesProvider.tsx +++ b/gui/frontend/src/components/organisms/BluetoothGrid/DevicesProvider.tsx @@ -15,7 +15,7 @@ import { NOTIFY } from '@/lib/notify'; import { PRIVATE_CACHE_OBJ } from '@/lib/storage/cacheKeys'; import { numberSchema } from '@/lib/zod/schema-utils'; import { BluetoothDeviceInfoSchema, type Devices } from '@/services/api/bluetooth_finder'; -import { deviceListener } from '@/services/api/device_listener'; +import { deviceListener, devicesListener } from '@/services/api/device_listener'; type ContextType = { devices: Devices | undefined; @@ -30,16 +30,18 @@ export const OptBluetoothDeviceInfoSchema = z .union([z.record(numberSchema, BluetoothDeviceInfoSchema), z.undefined()]) .catch(undefined); -let unlisten: (() => void) | undefined; +let unlistenUpdate: (() => void) | undefined; +let unlistenRestart: (() => void) | undefined; type Props = { children: ReactNode }; export const DevicesProvider: FC = ({ children }) => { const [devices, setDevices] = useStorageState(PRIVATE_CACHE_OBJ.devices, OptBluetoothDeviceInfoSchema); const [loading, setLoading] = useState(false); + // Update a device information useEffect(() => { NOTIFY.asyncTry(async () => { - unlisten = await deviceListener({ + unlistenUpdate = await deviceListener({ setDev: (newDevice) => { setDevices((prev) => { if (prev) { @@ -52,8 +54,25 @@ export const DevicesProvider: FC = ({ children }) => { }); return () => { - if (unlisten) { - unlisten(); + if (unlistenUpdate) { + unlistenUpdate(); + } + }; + }, [setDevices]); + + // Restart(Replace all devices) + useEffect(() => { + NOTIFY.asyncTry(async () => { + unlistenRestart = await devicesListener({ + setDev: (newDevices) => { + setDevices(newDevices); + }, + }); + }); + + return () => { + if (unlistenRestart) { + unlistenRestart(); } }; }, [setDevices]); diff --git a/gui/frontend/src/components/organisms/BluetoothGrid/RestartButton.tsx b/gui/frontend/src/components/organisms/BluetoothGrid/RestartButton.tsx index 302b37d..13705dc 100644 --- a/gui/frontend/src/components/organisms/BluetoothGrid/RestartButton.tsx +++ b/gui/frontend/src/components/organisms/BluetoothGrid/RestartButton.tsx @@ -4,8 +4,7 @@ import { useCallback } from 'react'; import { useTranslation } from '@/components/hooks/useTranslation'; import { LoadingButtonWithToolTip } from '@/components/molecules/LoadingButtonWithToolTip'; import { NOTIFY } from '@/lib/notify'; -import { getDevices, restartDeviceWatcher } from '@/services/api/bluetooth_finder'; -import { defaultTrayIcon } from '@/services/api/sys_tray'; +import { getDevices, restartDeviceWatcher, restartInterval } from '@/services/api/bluetooth_finder'; import { useDevicesContext } from './DevicesProvider'; @@ -23,8 +22,8 @@ export function RestartButton() { (async () => { try { setLoading(true); - await defaultTrayIcon(); - await restartDeviceWatcher(); + await restartInterval(); + await restartDeviceWatcher(); // set loading icon(by backend) & set device icon setDevices(await getDevices()); setLoading(false); } catch (err) { diff --git a/gui/frontend/src/services/api/device_listener.ts b/gui/frontend/src/services/api/device_listener.ts index 398d853..92e276f 100644 --- a/gui/frontend/src/services/api/device_listener.ts +++ b/gui/frontend/src/services/api/device_listener.ts @@ -1,7 +1,7 @@ import { listen } from '@tauri-apps/api/event'; import { NOTIFY } from '@/lib/notify'; -import type { BluetoothDeviceInfo } from '@/services/api/bluetooth_finder'; +import type { BluetoothDeviceInfo, Devices } from '@/services/api/bluetooth_finder'; import type { EventCallback } from '@tauri-apps/api/event'; import type { JSX } from 'react/jsx-runtime'; @@ -31,3 +31,30 @@ export async function deviceListener({ setDev, error }: ListenerProps) { } } } + +export async function devicesListener({ + setDev, + error, +}: { + setDev: (devices: Devices) => void; + /** @default Error */ + error?: string | JSX.Element; +}) { + let unlisten: (() => void) | null = null; + const eventHandler: EventCallback = (event) => { + if (event.payload) { + setDev(event.payload); + } + }; + + try { + // Setup before run Promise(For event hook) + unlisten = await listen('bt_monitor://restart_devices', eventHandler); + return unlisten; + } catch (err) { + NOTIFY.error(error ?? `${err}`); + if (unlisten) { + unlisten(); + } + } +} diff --git a/gui/frontend/src/services/api/sys_tray.ts b/gui/frontend/src/services/api/sys_tray.ts index de64f5a..086a7c5 100644 --- a/gui/frontend/src/services/api/sys_tray.ts +++ b/gui/frontend/src/services/api/sys_tray.ts @@ -1,9 +1,11 @@ import { invoke } from '@tauri-apps/api/core'; +/** set device info icon */ export async function updateTrayIcon(deviceName: string, batteryLevel: number, isConnected: boolean) { await invoke('update_tray', { deviceName, batteryLevel, isConnected }); } +/** set Loading tray icon */ export async function defaultTrayIcon() { await invoke('default_tray'); }