feat(usb): add UsbStatusPanel component and integrate it into BottomBarPC and MobileDesktop

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
This commit is contained in:
luckfox-eng29
2026-05-15 18:45:23 +08:00
parent b1090c9493
commit 40f5af2120
4 changed files with 101 additions and 11 deletions

View File

@@ -0,0 +1,79 @@
import React, { useCallback } from "react";
import { useReactAt } from "i18n-auto-extractor/react";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import notifications from "@/notifications";
import { dark_bg2_style, dark_bd_style, dark_line_style, dark_bg_style_fun } from "@/layout/theme_color";
import { useThemeSettings } from "@routes/login_page/useLocalAuth";
import { isMobile } from "react-device-detect";
const UsbStatusPanel: React.FC = () => {
const { $at } = useReactAt();
const { isDark } = useThemeSettings();
const [send] = useJsonRpc();
const handleReinitializeUsbGadget = useCallback(() => {
send("reinitializeUsbGadget", {}, resp => {
if ("error" in resp) {
notifications.error(
`Failed to reinitialize USB gadget: ${resp.error.data || "Unknown error"}`,
);
return;
}
notifications.success("USB Gadget reinitialized successfully");
});
}, [send]);
if (isMobile) {
return (
<div className={`w-full h-full flex flex-col ${dark_bg_style_fun(isDark)} p-4`}>
<div className={`flex flex-col w-full mx-auto ${isDark ? 'text-white' : 'text-black'}`}>
<div
className={`
flex items-center justify-between py-4 w-full
cursor-pointer transition-all duration-200 ease-in-out
${isDark ? 'text-white' : 'text-black'}
`}
onClick={handleReinitializeUsbGadget}
>
<span className="font-normal tracking-[0.5px]">
{$at("Reinitialize USB Gadget")}
</span>
</div>
</div>
</div>
);
}
return (
<div
style={{ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)' }}
className={`p-1.5 w-[200px] rounded font-sans ${dark_bg2_style} border ${dark_bd_style}`}
>
<div className="flex flex-col">
<div
style={{
padding: '8px 12px',
cursor: 'pointer',
backgroundColor: 'transparent',
color: isDark ? 'rgba(255, 255, 255, 0.85)' : 'rgba(0, 0, 0, 0.85)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
borderRadius: '4px',
}}
onClick={handleReinitializeUsbGadget}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0.04)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent';
}}
>
<span style={{ fontSize: "12px" }}>{$at("Reinitialize USB Gadget")}</span>
</div>
</div>
</div>
);
};
export default UsbStatusPanel;

View File

@@ -176,11 +176,13 @@ export default function BottomBarMobile() {
text={$at("HDMI")}
isActive={!!peerConnectionState}
/>
<ConnectionStatusButton
icon={usbState === "configured" ? <Usb2SVG /> : <UsbSVG />}
text={$at("USB")}
isActive={usbState === "configured"}
/>
<div onClick={() => toggleSidebarView("UsbStatusPanel")}>
<ConnectionStatusButton
icon={usbState === "configured" ? <Usb2SVG /> : <UsbSVG />}
text={$at("USB")}
isActive={usbState === "configured"}
/>
</div>
<VpnStatusButton
text={$at("TailScale")}
peerState={peerConnectionState}

View File

@@ -32,6 +32,7 @@ import BottomPopoverButton from "@components/PopoverButton";
import MousePanel from "@components/MousePanel";
import KeyboardPanel from "@/layout/components_bottom/keyboard/KeyboardPanel";
import UsbEpModeSelect from "@/layout/components_bottom/usbepmode/UsbEpModeSelect";
import UsbStatusPanel from "@/layout/components_bottom/usb_status/UsbStatusPanel";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { dark_bg2_style, selected_bt_bg } from "@/layout/theme_color";
import { useThemeSettings } from "@routes/login_page/useLocalAuth";
@@ -115,10 +116,12 @@ export default function BottomBarPC() {
isActive={hdmiState === "ready"}
/>
<ConnectionStatusButton
icon={usbState === "configured" ? <Usb2SVG fontSize={16} /> : <UsbSVG fontSize={16} />}
text={$at("USB")}
isActive={usbState === "configured"}
<BottomPopoverButton
buttonIconNode={usbState === "configured" ? <Usb2SVG fontSize={16} /> : <UsbSVG fontSize={16} />}
buttonText={$at("USB")}
style={{ color: usbState === "configured" ? "rgba(0, 205, 27, 1)" : "inherit" }}
panelContent={<UsbStatusPanel />}
align="left"
/>
<VpnStatusButton

View File

@@ -37,6 +37,7 @@ import SettingsMacros from "@/layout/components_side/Macros";
import { useTouchZoom } from "@/layout/core/desktop/hooks/useTouchZoom";
import { usePasteHandler } from "@/layout/core/desktop/hooks/usePasteHandler";
import UsbEpModeSelect from "@/layout/components_bottom/usbepmode/UsbEpModeSelect";
import UsbStatusPanel from "@/layout/components_bottom/usb_status/UsbStatusPanel";
import VirtualMediaSource from "@/layout/components_side/VirtualMediaSource";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import OcrOverlay from "@components/OcrOverlay";
@@ -423,6 +424,11 @@ export default function MobileDesktop({ isFullscreen }: { isFullscreen?: number
drawerRender={() => (<UsbEpModeSelect/>)}
className={"px-[20px]"}
/>
<EnhancedDrawer
targetView={"UsbStatusPanel"}
placement={"bottom"}
drawerRender={() => (<UsbStatusPanel/>)}
/>
<EnhancedDrawer
title={$at("Virtual Media Source")}
@@ -686,7 +692,7 @@ export default function MobileDesktop({ isFullscreen }: { isFullscreen?: number
"flex h-8 w-8 items-center justify-center rounded-full text-white text-xs active:scale-90 transition-transform duration-100",
isDark ? "bg-gray-500/70" : "bg-black/30",
)}
onClick={() => send("wheelReport", { wheelY: 1 })}
onClick={() => { send("wheelReport", { wheelY: 1 }); }}
>
</div>
@@ -712,7 +718,7 @@ export default function MobileDesktop({ isFullscreen }: { isFullscreen?: number
"mt-1 flex h-8 w-8 items-center justify-center rounded-full text-white text-xs active:scale-90 transition-transform duration-100",
isDark ? "bg-gray-500/70" : "bg-black/30",
)}
onClick={() => send("wheelReport", { wheelY: -1 })}
onClick={() => { send("wheelReport", { wheelY: -1 }); }}
>
</div>