refactor state refresh.

This commit is contained in:
Vixalie 2025-02-27 22:19:25 +08:00
parent d9a4f92c04
commit 3ca730a82b

View File

@ -1,9 +1,9 @@
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import { Event, listen, UnlistenFn } from '@tauri-apps/api/event'; import { listen, UnlistenFn } from '@tauri-apps/api/event';
import { message } from '@tauri-apps/plugin-dialog'; import { message } from '@tauri-apps/plugin-dialog';
import { atom, PrimitiveAtom, useSetAtom } from 'jotai'; import { atom, PrimitiveAtom, useSetAtom } from 'jotai';
import { atomFamily } from 'jotai/utils'; import { atomFamily, atomWithRefresh } from 'jotai/utils';
import { FC, ReactNode, useCallback, useEffect, useRef } from 'react'; import { FC, ReactNode, useEffect, useRef } from 'react';
export type Channels = 'a' | 'b'; export type Channels = 'a' | 'b';
type ChannelState = { type ChannelState = {
@ -15,7 +15,19 @@ type ChannelState = {
boostLevel: number; boostLevel: number;
maxBoostLevel: number; maxBoostLevel: number;
}; };
type PeripheralItem = {
id: string;
address: string;
represent: string;
isConnected: boolean;
rssi: number | null;
battery: number | null;
};
type DeviceState = { type DeviceState = {
id: string | null;
address: string | null;
represent: string | null;
isConnected: boolean | null;
rssi: number | null; rssi: number | null;
battery: number | null; battery: number | null;
}; };
@ -25,57 +37,136 @@ type BluetoothState = {
connected: string | null; connected: string | null;
}; };
export const BleState = atom<BluetoothState>({ export const BleState = atomWithRefresh<BluetoothState>(async (get) => {
ready: null, try {
searching: null, const state = await invoke('central_device_state');
connected: null, return {
ready: state.isReady,
searching: state.isScanning,
connected: state.connected,
};
} catch (e) {
console.error('[refresh central]', e);
}
return {
ready: null,
searching: null,
connected: null,
};
}); });
export const DeviceState = atom<DeviceState>({ export const DeviceState = atomWithRefresh<DeviceState>(async (get) => {
rssi: null, try {
battery: null, const state = await invoke('connected_peripheral_state');
return {
id: state?.id,
address: state?.address,
represent: state?.represent,
isConnected: state?.isConnected,
rssi: state?.rssi,
battery: state?.battery,
};
} catch (e) {
console.error('[refresh connected]', e);
}
return {
id: null,
address: null,
represent: null,
isConnected: null,
rssi: null,
battery: null,
};
});
export const FoundPeripherals = atomWithRefresh<PeripheralItem[]>(async (get) => {
try {
const peripherals = await invoke('found_peripherals');
return peripherals.map((peripheral: PeripheralItem) => ({
id: peripheral.id,
address: peripheral.address,
represent: peripheral.represent,
isConnected: peripheral.isConnected,
rssi: peripheral.rssi,
battery: peripheral.battery,
}));
} catch (e) {
console.error('[refresh found]', e);
}
return [];
}); });
const Channels: Record<Channels, PrimitiveAtom<ChannelState>> = { const Channels: Record<Channels, PrimitiveAtom<ChannelState>> = {
a: atom<ChannelState>({ a: atomWithRefresh<ChannelState>(async (get) => {
playing: false, try {
playMode: 'repeat-one', const state = await invoke('channel_a_state');
strength: 0, return {
maxStrength: 100, playing: state.isPlaying,
boosting: false, playMode: state.playMode,
boostLevel: 0, strength: state.strength,
maxBoostLevel: 100, maxStrength: state.maxStrength,
boosting: state.isBoosting,
boostLevel: state.boostLevel,
maxBoostLevel: state.maxBoostLevel,
};
} catch (e) {
console.error('[refresh channel a]', e);
}
return {
playing: false,
playMode: 'repeat-one',
strength: 0,
maxStrength: 100,
boosting: false,
boostLevel: 0,
maxBoostLevel: 100,
};
}), }),
b: atom<ChannelState>({ b: atom<ChannelState>(async (get) => {
playing: false, try {
playMode: 'repeat-one', const state = await invoke('channel_b_state');
strength: 0, return {
maxStrength: 100, playing: state.isPlaying,
boosting: false, playMode: state.playMode,
boostLevel: 0, strength: state.strength,
maxBoostLevel: 100, maxStrength: state.maxStrength,
boosting: state.isBoosting,
boostLevel: state.boostLevel,
maxBoostLevel: state.maxBoostLevel,
};
} catch (e) {
console.error('[refresh channel b]', e);
}
return {
playing: false,
playMode: 'repeat-one',
strength: 0,
maxStrength: 100,
boosting: false,
boostLevel: 0,
maxBoostLevel: 100,
};
}), }),
}; };
export const ChannelState = atomFamily((channel: Channels) => Channels[channel]); export const ChannelState = atomFamily((channel: Channels) => Channels[channel]);
const EstimWatchProvider: FC<{ children?: ReactNode }> = ({ children }) => { const EstimWatchProvider: FC<{ children?: ReactNode }> = ({ children }) => {
const unlisten = useRef<UnlistenFn | null>(null); const unlistenBle = useRef<UnlistenFn | null>(null);
const setBleState = useSetAtom(BleState); const unlistenDevice = useRef<UnlistenFn | null>(null);
const handleAppStateRefresh = useCallback(async (event: Event<unknown>) => { const unlistenPreipherals = useRef<UnlistenFn | null>(null);
try { const unlistenChannelA = useRef<UnlistenFn | null>(null);
const newState = await invoke('refresh_application_state'); const unlistenChannelB = useRef<UnlistenFn | null>(null);
setBleState({ const refreshBle = useSetAtom(BleState);
ready: newState.central.is_ready, const refreshDevice = useSetAtom(DeviceState);
searching: newState.central.is_scanning, const refreshPeripherals = useSetAtom(FoundPeripherals);
connected: newState.central.connected, const refreshChannelA = useSetAtom(ChannelState('a'));
}); const refreshChannelB = useSetAtom(ChannelState('b'));
} catch (e) {
console.error('[Answer refresh state]', e);
}
}, []);
useEffect(() => { useEffect(() => {
(async function () { (async function () {
try { try {
unlisten.current = await listen('app_state_updated', handleAppStateRefresh); unlistenBle.current = await listen('central_state_updated', () => refreshBle());
unlistenDevice.current = await listen('peripheral_connected', () => refreshDevice());
unlistenPreipherals.current = await listen('peripherals_found', () => refreshPeripherals());
unlistenChannelA.current = await listen('channel_a_updated', () => refreshChannelA());
unlistenChannelB.current = await listen('channel_b_updated', () => refreshChannelB());
await invoke('activate_central_adapter'); await invoke('activate_central_adapter');
} catch (e) { } catch (e) {
console.error('[Activate Adapter]', e); console.error('[Activate Adapter]', e);
@ -87,7 +178,11 @@ const EstimWatchProvider: FC<{ children?: ReactNode }> = ({ children }) => {
})(); })();
return () => { return () => {
unlisten.current?.(); unlistenBle.current?.();
unlistenDevice.current?.();
unlistenPreipherals.current?.();
unlistenChannelA.current?.();
unlistenChannelB.current?.();
}; };
}, []); }, []);
return <>{children}</>; return <>{children}</>;