mirror of https://github.com/jetkvm/kvm.git
feat(ui): Add other session handling route and modal
This commit is contained in:
parent
ba0c937e2a
commit
a2652c5265
|
@ -17,11 +17,11 @@ export default function Modal({
|
|||
<Dialog open={open} onClose={onClose} className="relative z-10">
|
||||
<DialogBackdrop
|
||||
transition
|
||||
className="fixed inset-0 bg-gray-500/75 dark:bg-slate-900/90 transition-opacity data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in"
|
||||
className="fixed inset-0 bg-gray-500/75 transition-opacity data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in dark:bg-slate-900/90"
|
||||
/>
|
||||
|
||||
<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||
<div className="flex items-end justify-center min-h-full p-4 text-center sm:items-center sm:p-0">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<DialogPanel
|
||||
transition
|
||||
className={cx(
|
||||
|
@ -30,9 +30,12 @@ export default function Modal({
|
|||
className,
|
||||
)}
|
||||
>
|
||||
<div className="inline-block w-full text-left pointer-events-auto">
|
||||
<div className="pointer-events-auto inline-block w-full text-left">
|
||||
<div className="flex justify-center" onClick={onClose}>
|
||||
<div className="w-full pointer-events-none" onClick={e => e.stopPropagation()}>
|
||||
<div
|
||||
className="pointer-events-none w-full"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -28,6 +28,7 @@ import WelcomeLocalModeRoute from "./routes/welcome-local.mode";
|
|||
import WelcomeRoute from "./routes/welcome-local";
|
||||
import WelcomeLocalPasswordRoute from "./routes/welcome-local.password";
|
||||
import { CLOUD_API } from "./ui.config";
|
||||
import OtherSessionRoute from "./routes/devices.$id.other-session";
|
||||
|
||||
export const isOnDevice = import.meta.env.MODE === "device";
|
||||
export const isInCloud = !isOnDevice;
|
||||
|
@ -75,7 +76,14 @@ if (isOnDevice) {
|
|||
errorElement: <ErrorBoundary />,
|
||||
element: <DeviceRoute />,
|
||||
loader: DeviceRoute.loader,
|
||||
children: [
|
||||
{
|
||||
path: "other-session",
|
||||
element: <OtherSessionRoute />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: "/adopt",
|
||||
element: <AdoptRoute />,
|
||||
|
@ -116,6 +124,12 @@ if (isOnDevice) {
|
|||
path: "devices/:id",
|
||||
element: <DeviceRoute />,
|
||||
loader: DeviceRoute.loader,
|
||||
children: [
|
||||
{
|
||||
path: "other-session",
|
||||
element: <OtherSessionRoute />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "devices/:id/deregister",
|
||||
|
@ -164,8 +178,8 @@ function ErrorBoundary() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="h-full w-full">
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="w-full max-w-2xl">
|
||||
<EmptyCard
|
||||
IconElm={ExclamationTriangleIcon}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import { useNavigate, useOutletContext } from "react-router-dom";
|
||||
import { GridCard } from "@/components/Card";
|
||||
import { Button } from "@components/Button";
|
||||
import LogoBlue from "@/assets/logo-blue.svg";
|
||||
import LogoWhite from "@/assets/logo-white.svg";
|
||||
|
||||
interface ContextType {
|
||||
connectWebRTC: () => Promise<void>;
|
||||
}
|
||||
|
||||
export default function OtherSessionRoute() {
|
||||
const outletContext = useOutletContext<ContextType>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Function to handle closing the modal
|
||||
const handleClose = () => {
|
||||
outletContext?.connectWebRTC().then(() => navigate(".."));
|
||||
};
|
||||
|
||||
return (
|
||||
<GridCard cardClassName="relative mx-auto max-w-md text-left pointer-events-auto">
|
||||
<div className="p-10">
|
||||
<div className="flex min-h-[140px] flex-col items-start justify-start space-y-4 text-left">
|
||||
<div className="h-[24px]">
|
||||
<img src={LogoBlue} alt="" className="h-full dark:hidden" />
|
||||
<img src={LogoWhite} alt="" className="hidden h-full dark:block" />
|
||||
</div>
|
||||
|
||||
<div className="text-left">
|
||||
<p className="text-base font-semibold dark:text-white">
|
||||
Another Active Session Detected
|
||||
</p>
|
||||
<p className="mb-4 text-sm text-slate-600 dark:text-slate-400">
|
||||
Only one active session is supported at a time. Would you like to take over
|
||||
this session?
|
||||
</p>
|
||||
<div className="flex items-center justify-start space-x-4">
|
||||
<Button size="SM" theme="primary" text="Use Here" onClick={handleClose} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridCard>
|
||||
);
|
||||
}
|
|
@ -16,10 +16,12 @@ import {
|
|||
import WebRTCVideo from "@components/WebRTCVideo";
|
||||
import {
|
||||
LoaderFunctionArgs,
|
||||
Outlet,
|
||||
Params,
|
||||
redirect,
|
||||
useLoaderData,
|
||||
useNavigate,
|
||||
useOutlet,
|
||||
useParams,
|
||||
useSearchParams,
|
||||
} from "react-router-dom";
|
||||
|
@ -34,9 +36,9 @@ import UpdateInProgressStatusCard from "../components/UpdateInProgressStatusCard
|
|||
import api from "../api";
|
||||
import { DeviceStatus } from "./welcome-local";
|
||||
import FocusTrap from "focus-trap-react";
|
||||
import OtherSessionConnectedModal from "@/components/OtherSessionConnectedModal";
|
||||
import Terminal from "@components/Terminal";
|
||||
import { CLOUD_API, DEVICE_API } from "@/ui.config";
|
||||
import Modal from "../components/Modal";
|
||||
|
||||
interface LocalLoaderResp {
|
||||
authMode: "password" | "noPassword" | null;
|
||||
|
@ -131,9 +133,6 @@ export default function KvmIdRoute() {
|
|||
setModalView,
|
||||
} = useUpdateStore();
|
||||
|
||||
const [isOtherSessionConnectedModalOpen, setIsOtherSessionConnectedModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const sdp = useCallback(
|
||||
async (event: RTCPeerConnectionIceEvent, pc: RTCPeerConnection) => {
|
||||
if (!pc) return;
|
||||
|
@ -243,8 +242,7 @@ export default function KvmIdRoute() {
|
|||
) {
|
||||
return;
|
||||
}
|
||||
// We don't want to connect if another session is connected
|
||||
if (isOtherSessionConnectedModalOpen) return;
|
||||
if (location.pathname.includes("other-session")) return;
|
||||
connectWebRTC();
|
||||
}, 3000);
|
||||
|
||||
|
@ -334,7 +332,7 @@ export default function KvmIdRoute() {
|
|||
function onJsonRpcRequest(resp: JsonRpcRequest) {
|
||||
if (resp.method === "otherSessionConnected") {
|
||||
console.log("otherSessionConnected", resp.params);
|
||||
setIsOtherSessionConnectedModalOpen(true);
|
||||
navigate("other-session");
|
||||
}
|
||||
|
||||
if (resp.method === "usbState") {
|
||||
|
@ -445,6 +443,8 @@ export default function KvmIdRoute() {
|
|||
};
|
||||
}, [kvmTerminal]);
|
||||
|
||||
const outlet = useOutlet();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transition show={!isUpdateDialogOpen && otaState.updating}>
|
||||
|
@ -486,18 +486,16 @@ export default function KvmIdRoute() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<UpdateDialog open={isUpdateDialogOpen} setOpen={setIsUpdateDialogOpen} />
|
||||
<OtherSessionConnectedModal
|
||||
open={isOtherSessionConnectedModalOpen}
|
||||
setOpen={state => {
|
||||
if (!state) connectWebRTC().then(r => r);
|
||||
|
||||
// It takes some time for the WebRTC connection to be established, so we wait a bit before closing the modal
|
||||
setTimeout(() => {
|
||||
setIsOtherSessionConnectedModalOpen(state);
|
||||
}, 1000);
|
||||
}}
|
||||
/>
|
||||
<Modal
|
||||
open={outlet !== null}
|
||||
onClose={() => location.pathname !== "/other-session" && navigate("..")}
|
||||
>
|
||||
<Outlet context={{ connectWebRTC }} />
|
||||
</Modal>
|
||||
|
||||
<UpdateDialog open={isUpdateDialogOpen} setOpen={setIsUpdateDialogOpen} />
|
||||
|
||||
{kvmTerminal && (
|
||||
<Terminal type="kvm" dataChannel={kvmTerminal} title="KVM Terminal" />
|
||||
)}
|
||||
|
|
Loading…
Reference in New Issue