import { Button } from "@components/Button"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import Card, { GridCard } from "@components/Card"; import { PlusCircleIcon } from "@heroicons/react/20/solid"; import { useMemo, forwardRef, useEffect, useCallback } from "react"; import { formatters } from "@/utils"; import { RemoteVirtualMediaState, useMountMediaStore, useRTCStore } from "@/hooks/stores"; import { SettingsPageHeader } from "@components/SettingsPageheader"; import { LuArrowUpFromLine, LuCheckCheck, LuLink, LuPlus, LuRadioReceiver, } from "react-icons/lu"; import { useJsonRpc } from "@/hooks/useJsonRpc"; import notifications from "../../notifications"; import { useClose } from "@headlessui/react"; import { useLocation } from "react-router-dom"; import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; const MountPopopover = forwardRef((_props, ref) => { const diskDataChannelStats = useRTCStore(state => state.diskDataChannelStats); const [send] = useJsonRpc(); const { remoteVirtualMediaState, setModalView, setRemoteVirtualMediaState } = useMountMediaStore(); const bytesSentPerSecond = useMemo(() => { if (diskDataChannelStats.size < 2) return null; const secondLastItem = Array.from(diskDataChannelStats)[diskDataChannelStats.size - 2]; const lastItem = Array.from(diskDataChannelStats)[diskDataChannelStats.size - 1]; if (!secondLastItem || !lastItem) return 0; const lastTime = lastItem[0]; const secondLastTime = secondLastItem[0]; const timeDelta = lastTime - secondLastTime; const lastBytesSent = lastItem[1].bytesSent; const secondLastBytesSent = secondLastItem[1].bytesSent; const bytesDelta = lastBytesSent - secondLastBytesSent; return bytesDelta / timeDelta; }, [diskDataChannelStats]); const syncRemoteVirtualMediaState = useCallback(() => { send("getVirtualMediaState", {}, response => { if ("error" in response) { notifications.error( `Failed to get virtual media state: ${response.error.message}`, ); } else { setRemoteVirtualMediaState(response.result as unknown as RemoteVirtualMediaState); } }); }, [send, setRemoteVirtualMediaState]); const handleUnmount = () => { send("unmountImage", {}, response => { if ("error" in response) { notifications.error(`Failed to unmount image: ${response.error.message}`); } else { syncRemoteVirtualMediaState(); } }); }; const renderGridCardContent = () => { if (!remoteVirtualMediaState) { return (

No mounted media

Add a file to get started

); } const { source, filename, size, url, path } = remoteVirtualMediaState; switch (source) { case "WebRTC": return ( <>

Streaming from Browser

{formatters.truncateMiddle(filename, 50)}
{formatters.bytes(size ?? 0)}
{bytesSentPerSecond !== null ? `${formatters.bytes(bytesSentPerSecond)}/s` : "N/A"}
); case "HTTP": return (

Streaming from URL

{formatters.truncateMiddle(url, 55)}

{formatters.truncateMiddle(filename, 30)}

{formatters.bytes(size ?? 0)}

); case "Storage": return (

Mounted from JetKVM Storage

{formatters.truncateMiddle(path, 50)}

{formatters.truncateMiddle(filename, 30)}

{formatters.bytes(size ?? 0)}

); default: return null; } }; const close = useClose(); const location = useLocation(); useEffect(() => { syncRemoteVirtualMediaState(); }, [syncRemoteVirtualMediaState, location.pathname]); const { navigateTo } = useDeviceUiNavigation(); return (
{remoteVirtualMediaState?.source === "WebRTC" ? (
Closing this tab will unmount the image
) : null}
{renderGridCardContent()}
{remoteVirtualMediaState ? (
Mounted as{" "} {remoteVirtualMediaState.mode === "Disk" ? "Disk" : "CD-ROM"}
) : null}
{!remoteVirtualMediaState && (
)}
); }); MountPopopover.displayName = "MountSidebarRoute"; export default MountPopopover;