From 131ac405a1a372c689e6952e3b1b11f89ba4e54d Mon Sep 17 00:00:00 2001 From: Grigoris Mallios Date: Thu, 3 Oct 2024 01:19:15 +0300 Subject: [PATCH] feat(web): add device information modal --- manager-ui/src/WebApp.tsx | 24 +++++++- .../DeviceInfoModal/deviceInfoModal.tsx | 61 +++++++++++++++++++ .../src/stores/web/useWebManagerStore.tsx | 25 ++++---- manager-ui/src/types/soundcore-lib.ts | 9 ++- manager-ui/src/types/tauri-backend.d.ts | 4 +- soundcore-lib/src/models/fw.rs | 23 +++++-- .../src/packets/response/info/a3951.rs | 2 +- .../src/packets/response/state/a3027.rs | 2 +- .../src/packets/response/state/a3028.rs | 2 +- .../src/packets/response/state/a3029.rs | 2 +- 10 files changed, 129 insertions(+), 25 deletions(-) create mode 100644 manager-ui/src/components/DeviceInfoModal/deviceInfoModal.tsx diff --git a/manager-ui/src/WebApp.tsx b/manager-ui/src/WebApp.tsx index 6971375..b15d5eb 100644 --- a/manager-ui/src/WebApp.tsx +++ b/manager-ui/src/WebApp.tsx @@ -14,11 +14,13 @@ import { NavbarBrand, NavbarContent, NavbarItem, - Spinner + Spinner, + useDisclosure } from '@nextui-org/react'; import { BlurredOverlay } from '@components/atoms/blurredOverlay'; -import { ChevronDown, Unplug } from 'lucide-react'; +import { ChevronDown, Info, Unplug } from 'lucide-react'; import { DeviceStateLayout } from './layouts/deviceState'; +import { DeviceInfoModal } from '@components/DeviceInfoModal/deviceInfoModal'; enum ConnectionDialogStatus { DIALOG_OPEN, @@ -32,6 +34,13 @@ export const WebApp: React.FC = () => { disconnect: state.disconnect, setDevice: state.setDevice })); + + const { + isOpen: isDeviceInfoOpen, + onOpen: onDeviceInfoOpen, + onClose: onDeviceInfoClose + } = useDisclosure(); + const [isConnecting, setIsConnecting] = useState(ConnectionDialogStatus.CLOSED); const scan = async () => { setIsConnecting(ConnectionDialogStatus.DIALOG_OPEN); @@ -78,6 +87,14 @@ export const WebApp: React.FC = () => { + + }> + Device Information + { )} + {state && ( + + )} ); }; diff --git a/manager-ui/src/components/DeviceInfoModal/deviceInfoModal.tsx b/manager-ui/src/components/DeviceInfoModal/deviceInfoModal.tsx new file mode 100644 index 0000000..656f09d --- /dev/null +++ b/manager-ui/src/components/DeviceInfoModal/deviceInfoModal.tsx @@ -0,0 +1,61 @@ +import { SoundcoreDeviceState } from '@generated-types/soundcore-lib'; +import { + Modal, + ModalBody, + ModalContent, + ModalHeader, + Table, + TableBody, + TableCell, + TableColumn, + TableHeader, + TableRow +} from '@nextui-org/react'; + +export interface DeviceInfoModalProps { + isOpen: boolean; + onClose: () => void; + state: SoundcoreDeviceState; +} + +export const DeviceInfoModal: React.FC = ({ isOpen, onClose, state }) => { + return ( + + + Device Information + + + + Hidden + Hidden + + + + Model + {state.serial?.model} + + + Serial Number + {state.serial?.value} + + + Firmware Version + + {state.fw?.primary.major}.{state.fw?.primary.minor} + + + + LDAC + {state.ldac ? 'Enabled' : 'Disabled/Unknown'} + + + Prompt Language + {state.promptLanguage} + + +
+
+
+
+ ); +}; diff --git a/manager-ui/src/stores/web/useWebManagerStore.tsx b/manager-ui/src/stores/web/useWebManagerStore.tsx index 354a069..3efed5d 100644 --- a/manager-ui/src/stores/web/useWebManagerStore.tsx +++ b/manager-ui/src/stores/web/useWebManagerStore.tsx @@ -1,4 +1,5 @@ import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; import { BLEDevice } from '../../ble/bleDevice'; import { SoundcoreDeviceState } from '@generated-types/soundcore-lib'; @@ -10,14 +11,16 @@ export interface WebManagerStore { disconnect: () => void; } -export const useWebManagerStore = create((set) => ({ - device: null, - currentState: null, - setDevice: (device) => set({ device }), - setCurrentState: (currentState) => set({ currentState }), - disconnect: () => - set((state) => { - state.device?.free(); - return { device: null, currentState: null }; - }) -})); +export const useWebManagerStore = create()( + devtools((set) => ({ + device: null, + currentState: null, + setDevice: (device) => set({ device }), + setCurrentState: (currentState) => set({ currentState }), + disconnect: () => + set((state) => { + state.device?.free(); + return { device: null, currentState: null }; + }) + })) +); diff --git a/manager-ui/src/types/soundcore-lib.ts b/manager-ui/src/types/soundcore-lib.ts index a2f9502..e9dc094 100644 --- a/manager-ui/src/types/soundcore-lib.ts +++ b/manager-ui/src/types/soundcore-lib.ts @@ -1,5 +1,5 @@ /* - Generated by typeshare 1.8.0 + Generated by typeshare 1.9.2 */ export type CustomANCValue = number; @@ -130,6 +130,11 @@ export interface FirmwareVer { minor: number; } +export interface DeviceFirmware { + primary: FirmwareVer; + secondary?: FirmwareVer; +} + export type ButtonModel = | { type: 'a3909'; value: A3909ButtonModel } | { type: 'a3040'; value: A3040ButtonModel }; @@ -159,7 +164,7 @@ export interface SoundcoreDeviceState { soundMode: SoundMode; eqConfiguration: EQConfiguration; serial?: SerialNumber; - fw?: FirmwareVer; + fw?: DeviceFirmware; buttonModel?: ButtonModel; hostDevice?: number; sideTone?: SideTone; diff --git a/manager-ui/src/types/tauri-backend.d.ts b/manager-ui/src/types/tauri-backend.d.ts index ce34401..d3cbd2f 100644 --- a/manager-ui/src/types/tauri-backend.d.ts +++ b/manager-ui/src/types/tauri-backend.d.ts @@ -1,5 +1,5 @@ /* - Generated by typeshare 1.8.0 + Generated by typeshare 1.9.2 */ export interface AddrWrappedPayload { @@ -26,7 +26,7 @@ export type BridgeCommand = | { command: 'setEqualizer'; payload: AddrWrappedPayload }; export type SetEqualizerPayload = - | { command: 'setCustomEqualizer'; payload: MonoEQ } + | { command: 'setCustomEqualizer'; payload: number[] } | { command: 'setEqualizerPreset'; payload: EQProfile }; export type BridgeResponse = diff --git a/soundcore-lib/src/models/fw.rs b/soundcore-lib/src/models/fw.rs index eb95409..4b6065c 100644 --- a/soundcore-lib/src/models/fw.rs +++ b/soundcore-lib/src/models/fw.rs @@ -3,9 +3,24 @@ use std::fmt::Display; use typeshare::typeshare; #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)] -pub enum DeviceFirmware { - DUAL(FirmwareVer, FirmwareVer), - SINGLE(FirmwareVer), +#[typeshare] +pub struct DeviceFirmware { + primary: FirmwareVer, + secondary: Option, +} + +impl DeviceFirmware { + pub fn new(primary: FirmwareVer, secondary: Option) -> Self { + Self { primary, secondary } + } + + pub fn primary(&self) -> FirmwareVer { + self.primary + } + + pub fn secondary(&self) -> Option { + self.secondary + } } #[derive( @@ -40,7 +55,7 @@ impl Display for FirmwareVer { impl From for Option { fn from(val: FirmwareVer) -> Self { - Some(DeviceFirmware::SINGLE(val)) + Some(DeviceFirmware::new(val, None)) } } diff --git a/soundcore-lib/src/packets/response/info/a3951.rs b/soundcore-lib/src/packets/response/info/a3951.rs index 17e476d..da209a9 100644 --- a/soundcore-lib/src/packets/response/info/a3951.rs +++ b/soundcore-lib/src/packets/response/info/a3951.rs @@ -25,7 +25,7 @@ pub fn parse_a3951_device_info_packet<'a, E: ParseError<'a>>( "parse_a3951_device_info", all_consuming(map(pair(parse_dual_fw, parse_serial_number), |(fw, sn)| { A3951DeviceInfoResponse { - fw: DeviceFirmware::DUAL(fw.0, fw.1), + fw: DeviceFirmware::new(fw.0, Some(fw.1)), sn, } })), diff --git a/soundcore-lib/src/packets/response/state/a3027.rs b/soundcore-lib/src/packets/response/state/a3027.rs index 8597f81..e422503 100644 --- a/soundcore-lib/src/packets/response/state/a3027.rs +++ b/soundcore-lib/src/packets/response/state/a3027.rs @@ -92,7 +92,7 @@ pub fn parse_a3027_state_response<'a, E: ParseError<'a>>( hear_id, sound_mode, wear_detection, - fw: DeviceFirmware::DUAL(fw.0, fw.1), + fw: DeviceFirmware::new(fw.0, Some(fw.1)), sn, touch_func: touch_func.unwrap_or(false), }, diff --git a/soundcore-lib/src/packets/response/state/a3028.rs b/soundcore-lib/src/packets/response/state/a3028.rs index 7a9cba4..62c2778 100644 --- a/soundcore-lib/src/packets/response/state/a3028.rs +++ b/soundcore-lib/src/packets/response/state/a3028.rs @@ -77,7 +77,7 @@ pub fn parse_a3028_state_response<'a, E: ParseError<'a>>( age_range, hear_id, sound_mode, - fw: DeviceFirmware::DUAL(fw.0, fw.1), + fw: DeviceFirmware::new(fw.0, Some(fw.1)), sn, }, }, diff --git a/soundcore-lib/src/packets/response/state/a3029.rs b/soundcore-lib/src/packets/response/state/a3029.rs index 02d67be..af3162a 100644 --- a/soundcore-lib/src/packets/response/state/a3029.rs +++ b/soundcore-lib/src/packets/response/state/a3029.rs @@ -84,7 +84,7 @@ pub fn parse_a3029_state_response<'a, E: ParseError<'a>>( age_range, hear_id, sound_mode, - fw: DeviceFirmware::DUAL(fw.0, fw.1), + fw: DeviceFirmware::new(fw.0, Some(fw.1)), sn, hear_id_has_data, },