From 4052b3d225140d829a27d3899755f100db22935b Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Thu, 27 Feb 2025 16:48:50 +0100 Subject: [PATCH] Move settings to modals & better modal handling (#194) * feat(ui): Add other session handling route and modal * feat(ui): Add dedicated update route and refactor update dialog state management * feat(ui): Add local authentication route * refactor(ui): Remove LocalAuthPasswordDialog component and clean up related code * refactor(ui): Remove OtherSessionConnectedModal component * feat(ui): Add dedicated mount route and refactor mount media dialog * refactor(ui): Simplify Escape key navigation in device route * refactor(ui): Add TODO comments for future URL-based state migration * refactor(ui): Migrate settings and update routes to dedicated routes This commit introduces a comprehensive refactoring of the UI routing and state management: - Removed sidebar-based settings view - Replaced global modal state with URL-based routing - Added dedicated routes for settings, including general, security, and update sections - Simplified modal and sidebar interactions - Improved animation and transition handling using motion library - Removed deprecated components and simplified route structure * fix(ui): Add TODO comment for modal session interaction * refactor(ui): Move USB configuration to new settings setup This commit introduces several improvements to the USB configuration workflow: - Refactored USB configuration dialog component - Simplified USB config state management - Moved USB configuration to hardware settings route - Updated JSON-RPC type definitions - Cleaned up unused imports and components - Improved error handling and notifications * refactor(ui): Replace react-router-dom navigation with custom navigation hook This commit introduces a new custom navigation hook `useDeviceUiNavigation` to replace direct usage of `useNavigate` across multiple components: - Removed direct `useNavigate` imports in various components - Added `navigateTo` method from new navigation hook - Updated navigation calls in ActionBar, MountPopover, UpdateInProgressStatusCard, and other routes - Simplified navigation logic and prepared for potential future navigation enhancements - Removed console logs and unnecessary comments * refactor(ui): Remove unused react-router-dom import Clean up unnecessary import of `useNavigate` from react-router-dom in device settings route * feat(ui): Improve mobile navigation and scrolling in device settings * refactor(ui): Reorganize device access and security settings This commit introduces several changes to the device access and security settings: - Renamed "Security" section to "Access" in settings navigation - Moved local authentication routes from security to access - Removed deprecated security settings route - Added new route for device access settings with cloud and local authentication management - Updated cloud URL and adoption logic to be part of the access settings - Simplified routing and component structure for better user experience * fix(ui): Update logout button hover state color * fix(ui): Adjust device de-registration button size to small * fix(ui): Update appearance settings section header and description * refactor(ui): Replace SectionHeader with new SettingsPageHeader and SettingsSectionHeader components This commit introduces two new header components for settings pages: - Created SettingsPageHeader for main page headers - Created SettingsSectionHeader for subsection headers - Replaced all existing SectionHeader imports with new components - Updated styling and type definitions to support more flexible header rendering * feat(ui): Add dev channel toggle to advanced settings Move dev channel update option from general settings to advanced settings - Introduced new state and handler for dev channel toggle - Removed dev channel option from general settings route - Added dev channel toggle in advanced settings with error handling --- config.go | 2 +- jsonrpc.go | 5 + ui/.env.device | 2 +- ui/package-lock.json | 65 + ui/package.json | 1 + ui/src/components/ActionBar.tsx | 7 +- ui/src/components/AutoHeight.tsx | 2 +- ui/src/components/Card.tsx | 17 +- ui/src/components/Header.tsx | 93 +- ui/src/components/Modal.tsx | 23 +- ui/src/components/SelectMenuBasic.tsx | 14 +- ...ctionHeader.tsx => SettingsPageheader.tsx} | 6 +- ui/src/components/SettingsSectionHeader.tsx | 16 + ui/src/components/USBConfigDialog.tsx | 173 +-- .../components/UpdateInProgressStatusCard.tsx | 21 +- ui/src/components/VideoOverlay.tsx | 277 ++-- ui/src/components/VirtualKeyboard.tsx | 525 +++---- ui/src/components/WebRTCVideo.tsx | 25 +- .../components/extensions/ATXPowerControl.tsx | 4 +- .../components/extensions/DCPowerControl.tsx | 4 +- .../components/extensions/SerialConsole.tsx | 4 +- .../components/popovers/ExtensionPopover.tsx | 4 +- ui/src/components/popovers/MountPopover.tsx | 102 +- ui/src/components/popovers/PasteModal.tsx | 4 +- .../components/popovers/WakeOnLan/Index.tsx | 4 +- ui/src/components/sidebar/connectionStats.tsx | 19 +- ui/src/components/sidebar/settings.tsx | 1254 ----------------- ui/src/hooks/stores.ts | 24 +- ui/src/hooks/useAppNavigation.ts | 58 + ui/src/hooks/useJsonRpc.ts | 30 +- ui/src/index.css | 12 +- ui/src/main.tsx | 187 ++- .../devices.$id.mount.tsx} | 159 ++- .../devices.$id.other-session.tsx} | 34 +- ui/src/routes/devices.$id.settings._index.tsx | 5 + .../devices.$id.settings.access._index.tsx | 331 +++++ ...evices.$id.settings.access.local-auth.tsx} | 116 +- .../routes/devices.$id.settings.advanced.tsx | 263 ++++ .../devices.$id.settings.appearance.tsx | 53 + .../devices.$id.settings.general._index.tsx | 108 ++ .../devices.$id.settings.general.update.tsx} | 198 +-- .../routes/devices.$id.settings.hardware.tsx | 288 ++++ ui/src/routes/devices.$id.settings.mouse.tsx | 133 ++ ui/src/routes/devices.$id.settings.tsx | 254 ++++ ui/src/routes/devices.$id.settings.video.tsx | 185 +++ ui/src/routes/devices.$id.tsx | 117 +- 46 files changed, 2887 insertions(+), 2341 deletions(-) rename ui/src/components/{SectionHeader.tsx => SettingsPageheader.tsx} (60%) create mode 100644 ui/src/components/SettingsSectionHeader.tsx delete mode 100644 ui/src/components/sidebar/settings.tsx create mode 100644 ui/src/hooks/useAppNavigation.ts rename ui/src/{components/MountMediaDialog.tsx => routes/devices.$id.mount.tsx} (90%) rename ui/src/{components/OtherSessionConnectedModal.tsx => routes/devices.$id.other-session.tsx} (65%) create mode 100644 ui/src/routes/devices.$id.settings._index.tsx create mode 100644 ui/src/routes/devices.$id.settings.access._index.tsx rename ui/src/{components/LocalAuthPasswordDialog.tsx => routes/devices.$id.settings.access.local-auth.tsx} (81%) create mode 100644 ui/src/routes/devices.$id.settings.advanced.tsx create mode 100644 ui/src/routes/devices.$id.settings.appearance.tsx create mode 100644 ui/src/routes/devices.$id.settings.general._index.tsx rename ui/src/{components/UpdateDialog.tsx => routes/devices.$id.settings.general.update.tsx} (75%) create mode 100644 ui/src/routes/devices.$id.settings.hardware.tsx create mode 100644 ui/src/routes/devices.$id.settings.mouse.tsx create mode 100644 ui/src/routes/devices.$id.settings.tsx create mode 100644 ui/src/routes/devices.$id.settings.video.tsx diff --git a/config.go b/config.go index 4119a0c..38a2b5b 100644 --- a/config.go +++ b/config.go @@ -53,7 +53,7 @@ var defaultConfig = &Config{ ProductId: "0x0104", //Multifunction Composite Gadget SerialNumber: "", Manufacturer: "JetKVM", - Product: "JetKVM USB Emulation Device", + Product: "USB Emulation Device", }, } diff --git a/jsonrpc.go b/jsonrpc.go index a07d461..9d10cb4 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -780,6 +780,10 @@ func rpcResetCloudUrl() error { return nil } +func rpcGetDefaultCloudUrl() (string, error) { + return defaultConfig.CloudURL, nil +} + var rpcHandlers = map[string]RPCHandler{ "ping": {Func: rpcPing}, "getDeviceID": {Func: rpcGetDeviceID}, @@ -841,4 +845,5 @@ var rpcHandlers = map[string]RPCHandler{ "setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"url"}}, "getCloudUrl": {Func: rpcGetCloudUrl}, "resetCloudUrl": {Func: rpcResetCloudUrl}, + "getDefaultCloudUrl": {Func: rpcGetDefaultCloudUrl}, } diff --git a/ui/.env.device b/ui/.env.device index 252fac4..c8d7c4f 100644 --- a/ui/.env.device +++ b/ui/.env.device @@ -1,2 +1,2 @@ # Used in settings page to know where to link to when user wants to adopt a device to the cloud -VITE_CLOUD_APP=http://localhost:5173 +VITE_CLOUD_APP=http://app.jetkvm.com diff --git a/ui/package-lock.json b/ui/package-lock.json index 0ba5323..dd34a03 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -22,6 +22,7 @@ "framer-motion": "^11.15.0", "lodash.throttle": "^4.1.1", "mini-svg-data-uri": "^1.4.4", + "motion": "^12.4.7", "react": "^18.2.0", "react-animate-height": "^3.2.3", "react-dom": "^18.2.0", @@ -4245,6 +4246,31 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/motion": { + "version": "12.4.7", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.4.7.tgz", + "integrity": "sha512-mhegHAbf1r80fr+ytC6OkjKvIUegRNXKLWNPrCN2+GnixlNSPwT03FtKqp9oDny1kNcLWZvwbmEr+JqVryFrcg==", + "dependencies": { + "framer-motion": "^12.4.7", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/motion-dom": { "version": "11.14.3", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.14.3.tgz", @@ -4255,6 +4281,45 @@ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.14.3.tgz", "integrity": "sha512-Xg+8xnqIJTpr0L/cidfTTBFkvRw26ZtGGuIhA94J9PQ2p4mEa06Xx7QVYZH0BP+EpMSaDlu+q0I0mmvwADPsaQ==" }, + "node_modules/motion/node_modules/framer-motion": { + "version": "12.4.7", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.4.7.tgz", + "integrity": "sha512-VhrcbtcAMXfxlrjeHPpWVu2+mkcoR31e02aNSR7OUS/hZAciKa8q6o3YN2mA1h+jjscRsSyKvX6E1CiY/7OLMw==", + "dependencies": { + "motion-dom": "^12.4.5", + "motion-utils": "^12.0.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion/node_modules/motion-dom": { + "version": "12.4.5", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.4.5.tgz", + "integrity": "sha512-Q2xmhuyYug1CGTo0jdsL05EQ4RhIYXlggFS/yPhQQRNzbrhjKQ1tbjThx5Plv68aX31LsUQRq4uIkuDxdO5vRQ==", + "dependencies": { + "motion-utils": "^12.0.0" + } + }, + "node_modules/motion/node_modules/motion-utils": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz", + "integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/ui/package.json b/ui/package.json index 54f9c08..5be3e2c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -31,6 +31,7 @@ "framer-motion": "^11.15.0", "lodash.throttle": "^4.1.1", "mini-svg-data-uri": "^1.4.4", + "motion": "^12.4.7", "react": "^18.2.0", "react-animate-height": "^3.2.3", "react-dom": "^18.2.0", diff --git a/ui/src/components/ActionBar.tsx b/ui/src/components/ActionBar.tsx index 6558a55..0a21dae 100644 --- a/ui/src/components/ActionBar.tsx +++ b/ui/src/components/ActionBar.tsx @@ -17,12 +17,14 @@ import MountPopopover from "./popovers/MountPopover"; import { Fragment, useCallback, useRef } from "react"; import { CommandLineIcon } from "@heroicons/react/20/solid"; import ExtensionPopover from "./popovers/ExtensionPopover"; +import { useDeviceUiNavigation } from "../hooks/useAppNavigation"; export default function Actionbar({ requestFullscreen, }: { requestFullscreen: () => Promise; }) { + const { navigateTo } = useDeviceUiNavigation(); const virtualKeyboard = useHidStore(state => state.isVirtualKeyboardEnabled); const setVirtualKeyboard = useHidStore(state => state.setVirtualKeyboardEnabled); @@ -260,15 +262,16 @@ export default function Actionbar({ /> -
+
+
+
+
Logged in as
+
+ {userEmail} +
+ )} +
+ +
+ +
+
- - - +
+ + ) : null} diff --git a/ui/src/components/Modal.tsx b/ui/src/components/Modal.tsx index 886469d..49fb0c3 100644 --- a/ui/src/components/Modal.tsx +++ b/ui/src/components/Modal.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Dialog, DialogBackdrop, DialogPanel } from "@headlessui/react"; import { cx } from "@/cva.config"; -export default function Modal({ +const Modal = React.memo(function Modal({ children, className, open, @@ -17,22 +17,25 @@ export default function Modal({ -
-
+ {/* TODO: This doesn't work well with other-sessions */} +
-
+
-
e.stopPropagation()}> +
e.stopPropagation()} + > {children}
@@ -42,4 +45,6 @@ export default function Modal({
); -} +}); + +export default Modal; diff --git a/ui/src/components/SelectMenuBasic.tsx b/ui/src/components/SelectMenuBasic.tsx index eb4c540..b68bc38 100644 --- a/ui/src/components/SelectMenuBasic.tsx +++ b/ui/src/components/SelectMenuBasic.tsx @@ -19,7 +19,7 @@ type SelectMenuProps = Pick< direction?: "vertical" | "horizontal"; error?: string; fullWidth?: boolean; -} & React.ComponentProps; +} & Partial>; const sizes = { XS: "h-[24.5px] pl-3 pr-8 text-xs", @@ -60,7 +60,7 @@ export const SelectMenuBasic = React.forwardRef {label && } - +