mirror of https://github.com/jetkvm/kvm.git
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
This commit is contained in:
parent
078e719133
commit
e51667e4cb
|
@ -17,13 +17,14 @@ import MountPopopover from "./popovers/MountPopover";
|
||||||
import { Fragment, useCallback, useRef } from "react";
|
import { Fragment, useCallback, useRef } from "react";
|
||||||
import { CommandLineIcon } from "@heroicons/react/20/solid";
|
import { CommandLineIcon } from "@heroicons/react/20/solid";
|
||||||
import ExtensionPopover from "./popovers/ExtensionPopover";
|
import ExtensionPopover from "./popovers/ExtensionPopover";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
|
||||||
|
|
||||||
export default function Actionbar({
|
export default function Actionbar({
|
||||||
requestFullscreen,
|
requestFullscreen,
|
||||||
}: {
|
}: {
|
||||||
requestFullscreen: () => Promise<void>;
|
requestFullscreen: () => Promise<void>;
|
||||||
}) {
|
}) {
|
||||||
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
const virtualKeyboard = useHidStore(state => state.isVirtualKeyboardEnabled);
|
const virtualKeyboard = useHidStore(state => state.isVirtualKeyboardEnabled);
|
||||||
|
|
||||||
const setVirtualKeyboard = useHidStore(state => state.setVirtualKeyboardEnabled);
|
const setVirtualKeyboard = useHidStore(state => state.setVirtualKeyboardEnabled);
|
||||||
|
@ -54,8 +55,6 @@ export default function Actionbar({
|
||||||
[setDisableFocusTrap],
|
[setDisableFocusTrap],
|
||||||
);
|
);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="border-b border-b-slate-800/20 bg-white dark:border-b-slate-300/20 dark:bg-slate-900">
|
<Container className="border-b border-b-slate-800/20 bg-white dark:border-b-slate-300/20 dark:bg-slate-900">
|
||||||
<div
|
<div
|
||||||
|
@ -269,7 +268,7 @@ export default function Actionbar({
|
||||||
theme="light"
|
theme="light"
|
||||||
text="Settings"
|
text="Settings"
|
||||||
LeadingIcon={LuSettings}
|
LeadingIcon={LuSettings}
|
||||||
onClick={() => navigate("settings")}
|
onClick={() => navigateTo("/settings")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ const Modal = React.memo(function Modal({
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
console.log("Modal", open);
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={onClose} className="relative z-10">
|
<Dialog open={open} onClose={onClose} className="relative z-10">
|
||||||
<DialogBackdrop
|
<DialogBackdrop
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { cx } from "@/cva.config";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import { GridCard } from "./Card";
|
import { GridCard } from "./Card";
|
||||||
import LoadingSpinner from "./LoadingSpinner";
|
import LoadingSpinner from "./LoadingSpinner";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
|
||||||
|
|
||||||
export default function UpdateInProgressStatusCard() {
|
export default function UpdateInProgressStatusCard() {
|
||||||
const navigate = useNavigate();
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full select-none opacity-100 transition-all duration-300 ease-in-out">
|
<div className="w-full select-none opacity-100 transition-all duration-300 ease-in-out">
|
||||||
|
@ -31,10 +31,7 @@ export default function UpdateInProgressStatusCard() {
|
||||||
className="pointer-events-auto"
|
className="pointer-events-auto"
|
||||||
theme="light"
|
theme="light"
|
||||||
text="View Details"
|
text="View Details"
|
||||||
onClick={() => {
|
onClick={() => navigateTo("/settings/general/update")}
|
||||||
// TODO: this wont work in cloud mode
|
|
||||||
navigate("/settings/general/update");
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</GridCard>
|
</GridCard>
|
||||||
|
|
|
@ -16,7 +16,8 @@ import {
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import notifications from "../../notifications";
|
import notifications from "../../notifications";
|
||||||
import { useClose } from "@headlessui/react";
|
import { useClose } from "@headlessui/react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
|
||||||
|
|
||||||
const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
const diskDataChannelStats = useRTCStore(state => state.diskDataChannelStats);
|
const diskDataChannelStats = useRTCStore(state => state.diskDataChannelStats);
|
||||||
|
@ -187,7 +188,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
syncRemoteVirtualMediaState();
|
syncRemoteVirtualMediaState();
|
||||||
}, [syncRemoteVirtualMediaState, location.pathname]);
|
}, [syncRemoteVirtualMediaState, location.pathname]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridCard>
|
<GridCard>
|
||||||
|
@ -307,7 +308,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
text="Add New Media"
|
text="Add New Media"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setModalView("mode");
|
setModalView("mode");
|
||||||
navigate("mount");
|
navigateTo("/mount");
|
||||||
}}
|
}}
|
||||||
LeadingIcon={LuPlus}
|
LeadingIcon={LuPlus}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -517,9 +517,7 @@ export const useUpdateStore = create<UpdateState>(set => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface UsbConfigModalState {
|
interface UsbConfigModalState {
|
||||||
modalView:
|
modalView: "updateUsbConfig" | "updateUsbConfigSuccess";
|
||||||
| "updateUsbConfig"
|
|
||||||
| "updateUsbConfigSuccess";
|
|
||||||
errorMessage: string | null;
|
errorMessage: string | null;
|
||||||
setModalView: (view: UsbConfigModalState["modalView"]) => void;
|
setModalView: (view: UsbConfigModalState["modalView"]) => void;
|
||||||
setErrorMessage: (message: string | null) => void;
|
setErrorMessage: (message: string | null) => void;
|
||||||
|
@ -548,14 +546,10 @@ interface LocalAuthModalState {
|
||||||
| "creationSuccess"
|
| "creationSuccess"
|
||||||
| "deleteSuccess"
|
| "deleteSuccess"
|
||||||
| "updateSuccess";
|
| "updateSuccess";
|
||||||
errorMessage: string | null;
|
|
||||||
setModalView: (view: LocalAuthModalState["modalView"]) => void;
|
setModalView: (view: LocalAuthModalState["modalView"]) => void;
|
||||||
setErrorMessage: (message: string | null) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLocalAuthModalStore = create<LocalAuthModalState>(set => ({
|
export const useLocalAuthModalStore = create<LocalAuthModalState>(set => ({
|
||||||
modalView: "createPassword",
|
modalView: "createPassword",
|
||||||
errorMessage: null,
|
|
||||||
setModalView: view => set({ modalView: view }),
|
setModalView: view => set({ modalView: view }),
|
||||||
setErrorMessage: message => set({ errorMessage: message }),
|
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { useNavigate, useParams, NavigateOptions } from "react-router-dom";
|
||||||
|
import { isOnDevice } from "../main";
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook that provides context-aware navigation and path generation
|
||||||
|
* that works in both cloud and device modes.
|
||||||
|
*
|
||||||
|
* In cloud mode, paths are prefixed with /devices/:id
|
||||||
|
* In device mode, paths start from the root
|
||||||
|
* Relative paths (starting with . or ..) are preserved in both modes
|
||||||
|
* Supports all React Router navigation options
|
||||||
|
*/
|
||||||
|
export function useDeviceUiNavigation() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const params = useParams();
|
||||||
|
|
||||||
|
// Get the device ID from params
|
||||||
|
const deviceId = useMemo(() => params.id, [params.id]);
|
||||||
|
|
||||||
|
// Function to generate the correct path
|
||||||
|
const getPath = useCallback(
|
||||||
|
(path: string): string => {
|
||||||
|
// Check if it's a relative path (starts with . or ..)
|
||||||
|
const isRelativePath = path.startsWith(".") || path === "";
|
||||||
|
|
||||||
|
// If it's a relative path, don't modify it
|
||||||
|
if (isRelativePath) return path;
|
||||||
|
|
||||||
|
// Ensure absolute path starts with a slash
|
||||||
|
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
||||||
|
|
||||||
|
if (isOnDevice) {
|
||||||
|
return normalizedPath;
|
||||||
|
} else {
|
||||||
|
if (!deviceId) {
|
||||||
|
console.error("No device ID found in params when generating path");
|
||||||
|
throw new Error("No device ID found in params when generating path");
|
||||||
|
}
|
||||||
|
return `/devices/${deviceId}${normalizedPath}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[deviceId],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Function to navigate to the correct path with all options
|
||||||
|
const navigateTo = useCallback(
|
||||||
|
(path: string, options?: NavigateOptions) => {
|
||||||
|
navigate(getPath(path), options);
|
||||||
|
},
|
||||||
|
[getPath, navigate],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
navigateTo,
|
||||||
|
getPath,
|
||||||
|
};
|
||||||
|
}
|
|
@ -93,9 +93,7 @@ export function Dialog({ onClose }: { onClose: () => void }) {
|
||||||
|
|
||||||
clearMountMediaState();
|
clearMountMediaState();
|
||||||
syncRemoteVirtualMediaState()
|
syncRemoteVirtualMediaState()
|
||||||
.then(() => {
|
.then(() => navigate(".."))
|
||||||
navigate("..");
|
|
||||||
})
|
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
triggerError(err instanceof Error ? err.message : String(err));
|
triggerError(err instanceof Error ? err.message : String(err));
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,10 +13,11 @@ import { CLOUD_APP } from "../ui.config";
|
||||||
import notifications from "../notifications";
|
import notifications from "../notifications";
|
||||||
import { isOnDevice } from "../main";
|
import { isOnDevice } from "../main";
|
||||||
import Checkbox from "../components/Checkbox";
|
import Checkbox from "../components/Checkbox";
|
||||||
|
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
|
||||||
|
|
||||||
export default function SettingsGeneralRoute() {
|
export default function SettingsGeneralRoute() {
|
||||||
const [send] = useJsonRpc();
|
const [send] = useJsonRpc();
|
||||||
const navigate = useNavigate();
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
|
|
||||||
const [devChannel, setDevChannel] = useState(false);
|
const [devChannel, setDevChannel] = useState(false);
|
||||||
const [autoUpdate, setAutoUpdate] = useState(true);
|
const [autoUpdate, setAutoUpdate] = useState(true);
|
||||||
|
@ -135,10 +136,7 @@ export default function SettingsGeneralRoute() {
|
||||||
size="SM"
|
size="SM"
|
||||||
theme="light"
|
theme="light"
|
||||||
text="Check for Updates"
|
text="Check for Updates"
|
||||||
onClick={() => {
|
onClick={() => navigateTo("./update")}
|
||||||
// TODO: this wont work in cloud mode
|
|
||||||
navigate("./update");
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { UpdateState, useUpdateStore } from "@/hooks/stores";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
import { CheckCircleIcon } from "@heroicons/react/20/solid";
|
import { CheckCircleIcon } from "@heroicons/react/20/solid";
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner";
|
import LoadingSpinner from "@/components/LoadingSpinner";
|
||||||
|
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
|
||||||
|
|
||||||
export default function SettingsGeneralUpdateRoute() {
|
export default function SettingsGeneralUpdateRoute() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -36,15 +37,7 @@ export default function SettingsGeneralUpdateRoute() {
|
||||||
{
|
{
|
||||||
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
|
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
|
||||||
}
|
}
|
||||||
return (
|
return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />;
|
||||||
<Dialog
|
|
||||||
onClose={() => {
|
|
||||||
// TODO: This wont work in cloud mode
|
|
||||||
navigate("..");
|
|
||||||
}}
|
|
||||||
onConfirmUpdate={onConfirmUpdate}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemVersionInfo {
|
export interface SystemVersionInfo {
|
||||||
|
@ -61,7 +54,7 @@ export function Dialog({
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onConfirmUpdate: () => void;
|
onConfirmUpdate: () => void;
|
||||||
}) {
|
}) {
|
||||||
const navigate = useNavigate();
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
|
|
||||||
const [versionInfo, setVersionInfo] = useState<null | SystemVersionInfo>(null);
|
const [versionInfo, setVersionInfo] = useState<null | SystemVersionInfo>(null);
|
||||||
const { modalView, setModalView, otaState } = useUpdateStore();
|
const { modalView, setModalView, otaState } = useUpdateStore();
|
||||||
|
@ -113,10 +106,7 @@ export function Dialog({
|
||||||
{modalView === "updating" && (
|
{modalView === "updating" && (
|
||||||
<UpdatingDeviceState
|
<UpdatingDeviceState
|
||||||
otaState={otaState}
|
otaState={otaState}
|
||||||
onMinimizeUpgradeDialog={() => {
|
onMinimizeUpgradeDialog={() => navigateTo("/")}
|
||||||
// TODO: This wont work in cloud mode
|
|
||||||
navigate("/");
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { SectionHeader } from "@/components/SectionHeader";
|
import { SectionHeader } from "@/components/SectionHeader";
|
||||||
import { SettingsItem } from "./devices.$id.settings";
|
import { SettingsItem } from "./devices.$id.settings";
|
||||||
import { useNavigate, useLoaderData } from "react-router-dom";
|
import { useLoaderData } from "react-router-dom";
|
||||||
import { Button } from "../components/Button";
|
import { Button } from "../components/Button";
|
||||||
import { DEVICE_API } from "../ui.config";
|
import { DEVICE_API } from "../ui.config";
|
||||||
import api from "../api";
|
import api from "../api";
|
||||||
import { LocalDevice } from "./devices.$id";
|
import { LocalDevice } from "./devices.$id";
|
||||||
|
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
|
||||||
|
|
||||||
export const loader = async () => {
|
export const loader = async () => {
|
||||||
const status = await api
|
const status = await api
|
||||||
|
@ -15,7 +16,7 @@ export const loader = async () => {
|
||||||
|
|
||||||
export default function SettingsSecurityIndexRoute() {
|
export default function SettingsSecurityIndexRoute() {
|
||||||
const { authMode } = useLoaderData() as LocalDevice;
|
const { authMode } = useLoaderData() as LocalDevice;
|
||||||
const navigate = useNavigate();
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
@ -35,7 +36,7 @@ export default function SettingsSecurityIndexRoute() {
|
||||||
theme="light"
|
theme="light"
|
||||||
text="Disable Protection"
|
text="Disable Protection"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("local-auth", { state: { init: "deletePassword" } });
|
navigateTo("./local-auth", { state: { init: "deletePassword" } });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -44,7 +45,7 @@ export default function SettingsSecurityIndexRoute() {
|
||||||
theme="light"
|
theme="light"
|
||||||
text="Enable Password"
|
text="Enable Password"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("local-auth", { state: { init: "createPassword" } });
|
navigateTo("./local-auth", { state: { init: "createPassword" } });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -60,7 +61,7 @@ export default function SettingsSecurityIndexRoute() {
|
||||||
theme="light"
|
theme="light"
|
||||||
text="Change Password"
|
text="Change Password"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("local-auth", { state: { init: "updatePassword" } });
|
navigateTo("./local-auth", { state: { init: "updatePassword" } });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
|
@ -3,32 +3,33 @@ import { Button } from "@components/Button";
|
||||||
import { InputFieldWithLabel } from "@/components/InputField";
|
import { InputFieldWithLabel } from "@/components/InputField";
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import { useLocalAuthModalStore } from "@/hooks/stores";
|
import { useLocalAuthModalStore } from "@/hooks/stores";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useRevalidator } from "react-router-dom";
|
||||||
|
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
|
||||||
|
|
||||||
export default function LocalAuthRoute() {
|
export default function LocalAuthRoute() {
|
||||||
const navigate = useNavigate();
|
|
||||||
const { setModalView } = useLocalAuthModalStore();
|
const { setModalView } = useLocalAuthModalStore();
|
||||||
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const init = location.state?.init;
|
const init = location.state?.init;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!init) {
|
if (!init) {
|
||||||
navigate("..");
|
navigateTo("..");
|
||||||
} else {
|
} else {
|
||||||
setModalView(init);
|
setModalView(init);
|
||||||
}
|
}
|
||||||
}, [init, navigate, setModalView]);
|
}, [init, navigateTo, setModalView]);
|
||||||
|
|
||||||
{
|
{
|
||||||
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
|
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
|
||||||
}
|
}
|
||||||
return <Dialog onClose={() => navigate("..")} />;
|
return <Dialog onClose={() => navigateTo("..")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Dialog({ onClose }: { onClose: () => void }) {
|
export function Dialog({ onClose }: { onClose: () => void }) {
|
||||||
const { modalView, setModalView } = useLocalAuthModalStore();
|
const { modalView, setModalView } = useLocalAuthModalStore();
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const revalidator = useRevalidator();
|
||||||
|
|
||||||
const handleCreatePassword = async (password: string, confirmPassword: string) => {
|
const handleCreatePassword = async (password: string, confirmPassword: string) => {
|
||||||
if (password === "") {
|
if (password === "") {
|
||||||
|
@ -45,6 +46,8 @@ export function Dialog({ onClose }: { onClose: () => void }) {
|
||||||
const res = await api.POST("/auth/password-local", { password });
|
const res = await api.POST("/auth/password-local", { password });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setModalView("creationSuccess");
|
setModalView("creationSuccess");
|
||||||
|
// The rest of the app needs to revalidate the device authMode
|
||||||
|
revalidator.revalidate();
|
||||||
} else {
|
} else {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setError(data.error || "An error occurred while setting the password");
|
setError(data.error || "An error occurred while setting the password");
|
||||||
|
@ -82,6 +85,8 @@ export function Dialog({ onClose }: { onClose: () => void }) {
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setModalView("updateSuccess");
|
setModalView("updateSuccess");
|
||||||
|
// The rest of the app needs to revalidate the device authMode
|
||||||
|
revalidator.revalidate();
|
||||||
} else {
|
} else {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setError(data.error || "An error occurred while changing the password");
|
setError(data.error || "An error occurred while changing the password");
|
||||||
|
@ -101,6 +106,8 @@ export function Dialog({ onClose }: { onClose: () => void }) {
|
||||||
const res = await api.DELETE("/auth/local-password", { password });
|
const res = await api.DELETE("/auth/local-password", { password });
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setModalView("deleteSuccess");
|
setModalView("deleteSuccess");
|
||||||
|
// The rest of the app needs to revalidate the device authMode
|
||||||
|
revalidator.revalidate();
|
||||||
} else {
|
} else {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setError(data.error || "An error occurred while disabling the password");
|
setError(data.error || "An error occurred while disabling the password");
|
||||||
|
|
|
@ -38,6 +38,7 @@ import Terminal from "@components/Terminal";
|
||||||
import { CLOUD_API, DEVICE_API } from "@/ui.config";
|
import { CLOUD_API, DEVICE_API } from "@/ui.config";
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
import { motion, AnimatePresence } from "motion/react";
|
import { motion, AnimatePresence } from "motion/react";
|
||||||
|
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
|
||||||
|
|
||||||
interface LocalLoaderResp {
|
interface LocalLoaderResp {
|
||||||
authMode: "password" | "noPassword" | null;
|
authMode: "password" | "noPassword" | null;
|
||||||
|
@ -322,11 +323,11 @@ export default function KvmIdRoute() {
|
||||||
const setHdmiState = useVideoStore(state => state.setHdmiState);
|
const setHdmiState = useVideoStore(state => state.setHdmiState);
|
||||||
|
|
||||||
const [hasUpdated, setHasUpdated] = useState(false);
|
const [hasUpdated, setHasUpdated] = useState(false);
|
||||||
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
|
|
||||||
function onJsonRpcRequest(resp: JsonRpcRequest) {
|
function onJsonRpcRequest(resp: JsonRpcRequest) {
|
||||||
if (resp.method === "otherSessionConnected") {
|
if (resp.method === "otherSessionConnected") {
|
||||||
console.log("otherSessionConnected", resp.params);
|
navigateTo("/other-session");
|
||||||
navigate("other-session");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.method === "usbState") {
|
if (resp.method === "usbState") {
|
||||||
|
@ -350,8 +351,7 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
if (otaState.error) {
|
if (otaState.error) {
|
||||||
setModalView("error");
|
setModalView("error");
|
||||||
// TODO: this wont work in cloud mode
|
navigateTo("/settings/general/update");
|
||||||
navigate("update");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,12 +381,9 @@ export default function KvmIdRoute() {
|
||||||
// When the update is successful, we need to refresh the client javascript and show a success modal
|
// When the update is successful, we need to refresh the client javascript and show a success modal
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (queryParams.get("updateSuccess")) {
|
if (queryParams.get("updateSuccess")) {
|
||||||
// TODO: this wont work in cloud mode
|
navigateTo("/settings/general/update", { state: { updateSuccess: true } });
|
||||||
navigate("./settings/general/update", {
|
|
||||||
state: { updateSuccess: true },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [navigate, queryParams, setModalView, setQueryParams]);
|
}, [navigate, navigateTo, queryParams, setModalView, setQueryParams]);
|
||||||
|
|
||||||
const diskChannel = useRTCStore(state => state.diskChannel)!;
|
const diskChannel = useRTCStore(state => state.diskChannel)!;
|
||||||
const file = useMountMediaStore(state => state.localFile)!;
|
const file = useMountMediaStore(state => state.localFile)!;
|
||||||
|
@ -442,10 +439,8 @@ export default function KvmIdRoute() {
|
||||||
const outlet = useOutlet();
|
const outlet = useOutlet();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const onModalClose = useCallback(() => {
|
const onModalClose = useCallback(() => {
|
||||||
if (location.pathname !== "/other-session") {
|
if (location.pathname !== "/other-session") navigateTo("..");
|
||||||
navigate("..");
|
}, [navigateTo, location.pathname]);
|
||||||
}
|
|
||||||
}, [navigate, location.pathname]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -497,7 +492,7 @@ export default function KvmIdRoute() {
|
||||||
onKeyUp={e => e.stopPropagation()}
|
onKeyUp={e => e.stopPropagation()}
|
||||||
onKeyDown={e => {
|
onKeyDown={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === "Escape") navigate("./");
|
if (e.key === "Escape") navigateTo("/");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal open={outlet !== null} onClose={onModalClose}>
|
<Modal open={outlet !== null} onClose={onModalClose}>
|
||||||
|
|
Loading…
Reference in New Issue