import { useCallback, useEffect, useState } from "react"; import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import { m } from "@localizations/messages.js"; import { SettingsItem } from "@components/SettingsItem"; import Checkbox from "@components/Checkbox"; import { Button } from "@components/Button"; import { SelectMenuBasic } from "@components/SelectMenuBasic"; import { SettingsSectionHeader } from "@components/SettingsSectionHeader"; import Fieldset from "@components/Fieldset"; import notifications from "@/notifications"; import { sleep } from "@/utils"; export interface USBConfig { vendor_id: string; product_id: string; serial_number: string; manufacturer: string; product: string; } export interface UsbDeviceConfig { keyboard: boolean; absolute_mouse: boolean; relative_mouse: boolean; mass_storage: boolean; audio: boolean; } const defaultUsbDeviceConfig: UsbDeviceConfig = { keyboard: true, absolute_mouse: true, relative_mouse: true, mass_storage: true, audio: true, }; const usbPresets = [ { label: m.usb_device_keyboard_mouse_mass_storage_and_audio(), value: "default", config: { keyboard: true, absolute_mouse: true, relative_mouse: true, mass_storage: true, audio: true, }, }, { label: m.usb_device_keyboard_mouse_and_mass_storage(), value: "keyboard_mouse_and_mass_storage", config: { keyboard: true, absolute_mouse: true, relative_mouse: true, mass_storage: true, audio: false, }, }, { label: m.usb_device_keyboard_only(), value: "keyboard_only", config: { keyboard: true, absolute_mouse: false, relative_mouse: false, mass_storage: false, audio: false, }, }, { label: m.usb_device_custom(), value: "custom", }, ]; export function UsbDeviceSetting() { const { send } = useJsonRpc(); const [loading, setLoading] = useState(false); const [usbDeviceConfig, setUsbDeviceConfig] = useState(defaultUsbDeviceConfig); const [selectedPreset, setSelectedPreset] = useState("default"); const syncUsbDeviceConfig = useCallback(() => { send("getUsbDevices", {}, (resp: JsonRpcResponse) => { if ("error" in resp) { console.error("Failed to load USB devices:", resp.error); notifications.error( m.usb_device_failed_load({ error: String(resp.error.data || m.unknown_error()) }), ); } else { const usbConfigState = resp.result as UsbDeviceConfig; setUsbDeviceConfig(usbConfigState); // Set the appropriate preset based on current config const matchingPreset = usbPresets.find( preset => preset.value !== "custom" && preset.config && Object.keys(preset.config).length === Object.keys(usbConfigState).length && Object.keys(preset.config).every(key => { const configKey = key as keyof typeof preset.config; return preset.config[configKey] === usbConfigState[configKey]; }), ); setSelectedPreset(matchingPreset ? matchingPreset.value : "custom"); } }); }, [send]); const handleUsbConfigChange = useCallback( (devices: UsbDeviceConfig) => { setLoading(true); send("setUsbDevices", { devices }, async (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( m.usb_device_failed_set({ error: String(resp.error.data || m.unknown_error()) }), ); setLoading(false); return; } // We need some time to ensure the USB devices are updated await sleep(2000); setLoading(false); syncUsbDeviceConfig(); notifications.success(m.usb_device_updated()); }); }, [send, syncUsbDeviceConfig], ); const onUsbConfigItemChange = useCallback( (key: keyof UsbDeviceConfig) => (e: React.ChangeEvent) => { setUsbDeviceConfig(prev => ({ ...prev, [key]: e.target.checked, })); }, [], ); const handlePresetChange = useCallback( (e: React.ChangeEvent) => { const newPreset = e.target.value; setSelectedPreset(newPreset); if (newPreset !== "custom") { const presetConfig = usbPresets.find( preset => preset.value === newPreset, )?.config; if (presetConfig) { handleUsbConfigChange(presetConfig); } } }, [handleUsbConfigChange], ); useEffect(() => { syncUsbDeviceConfig(); }, [syncUsbDeviceConfig]); return (
{selectedPreset === "custom" && (
)}
); }