mirror of
https://github.com/luckfox-eng29/kvm.git
synced 2026-05-28 17:11:20 +02:00
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:
@@ -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;
|
||||||
@@ -176,11 +176,13 @@ export default function BottomBarMobile() {
|
|||||||
text={$at("HDMI")}
|
text={$at("HDMI")}
|
||||||
isActive={!!peerConnectionState}
|
isActive={!!peerConnectionState}
|
||||||
/>
|
/>
|
||||||
<ConnectionStatusButton
|
<div onClick={() => toggleSidebarView("UsbStatusPanel")}>
|
||||||
icon={usbState === "configured" ? <Usb2SVG /> : <UsbSVG />}
|
<ConnectionStatusButton
|
||||||
text={$at("USB")}
|
icon={usbState === "configured" ? <Usb2SVG /> : <UsbSVG />}
|
||||||
isActive={usbState === "configured"}
|
text={$at("USB")}
|
||||||
/>
|
isActive={usbState === "configured"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<VpnStatusButton
|
<VpnStatusButton
|
||||||
text={$at("TailScale")}
|
text={$at("TailScale")}
|
||||||
peerState={peerConnectionState}
|
peerState={peerConnectionState}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import BottomPopoverButton from "@components/PopoverButton";
|
|||||||
import MousePanel from "@components/MousePanel";
|
import MousePanel from "@components/MousePanel";
|
||||||
import KeyboardPanel from "@/layout/components_bottom/keyboard/KeyboardPanel";
|
import KeyboardPanel from "@/layout/components_bottom/keyboard/KeyboardPanel";
|
||||||
import UsbEpModeSelect from "@/layout/components_bottom/usbepmode/UsbEpModeSelect";
|
import UsbEpModeSelect from "@/layout/components_bottom/usbepmode/UsbEpModeSelect";
|
||||||
|
import UsbStatusPanel from "@/layout/components_bottom/usb_status/UsbStatusPanel";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import { dark_bg2_style, selected_bt_bg } from "@/layout/theme_color";
|
import { dark_bg2_style, selected_bt_bg } from "@/layout/theme_color";
|
||||||
import { useThemeSettings } from "@routes/login_page/useLocalAuth";
|
import { useThemeSettings } from "@routes/login_page/useLocalAuth";
|
||||||
@@ -115,10 +116,12 @@ export default function BottomBarPC() {
|
|||||||
isActive={hdmiState === "ready"}
|
isActive={hdmiState === "ready"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConnectionStatusButton
|
<BottomPopoverButton
|
||||||
icon={usbState === "configured" ? <Usb2SVG fontSize={16} /> : <UsbSVG fontSize={16} />}
|
buttonIconNode={usbState === "configured" ? <Usb2SVG fontSize={16} /> : <UsbSVG fontSize={16} />}
|
||||||
text={$at("USB")}
|
buttonText={$at("USB")}
|
||||||
isActive={usbState === "configured"}
|
style={{ color: usbState === "configured" ? "rgba(0, 205, 27, 1)" : "inherit" }}
|
||||||
|
panelContent={<UsbStatusPanel />}
|
||||||
|
align="left"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VpnStatusButton
|
<VpnStatusButton
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import SettingsMacros from "@/layout/components_side/Macros";
|
|||||||
import { useTouchZoom } from "@/layout/core/desktop/hooks/useTouchZoom";
|
import { useTouchZoom } from "@/layout/core/desktop/hooks/useTouchZoom";
|
||||||
import { usePasteHandler } from "@/layout/core/desktop/hooks/usePasteHandler";
|
import { usePasteHandler } from "@/layout/core/desktop/hooks/usePasteHandler";
|
||||||
import UsbEpModeSelect from "@/layout/components_bottom/usbepmode/UsbEpModeSelect";
|
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 VirtualMediaSource from "@/layout/components_side/VirtualMediaSource";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import OcrOverlay from "@components/OcrOverlay";
|
import OcrOverlay from "@components/OcrOverlay";
|
||||||
@@ -423,6 +424,11 @@ export default function MobileDesktop({ isFullscreen }: { isFullscreen?: number
|
|||||||
drawerRender={() => (<UsbEpModeSelect/>)}
|
drawerRender={() => (<UsbEpModeSelect/>)}
|
||||||
className={"px-[20px]"}
|
className={"px-[20px]"}
|
||||||
/>
|
/>
|
||||||
|
<EnhancedDrawer
|
||||||
|
targetView={"UsbStatusPanel"}
|
||||||
|
placement={"bottom"}
|
||||||
|
drawerRender={() => (<UsbStatusPanel/>)}
|
||||||
|
/>
|
||||||
|
|
||||||
<EnhancedDrawer
|
<EnhancedDrawer
|
||||||
title={$at("Virtual Media Source")}
|
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",
|
"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",
|
isDark ? "bg-gray-500/70" : "bg-black/30",
|
||||||
)}
|
)}
|
||||||
onClick={() => send("wheelReport", { wheelY: 1 })}
|
onClick={() => { send("wheelReport", { wheelY: 1 }); }}
|
||||||
>
|
>
|
||||||
▲
|
▲
|
||||||
</div>
|
</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",
|
"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",
|
isDark ? "bg-gray-500/70" : "bg-black/30",
|
||||||
)}
|
)}
|
||||||
onClick={() => send("wheelReport", { wheelY: -1 })}
|
onClick={() => { send("wheelReport", { wheelY: -1 }); }}
|
||||||
>
|
>
|
||||||
▼
|
▼
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user