Show VM screenshot, if possible
This commit is contained in:
parent
31955ca738
commit
d63d3ecebc
@ -86,4 +86,16 @@ export class VMApi {
|
||||
static async ResetVM(vm: VMInfo): Promise<void> {
|
||||
await APIClient.exec({ method: "GET", uri: `/vm/${vm.uiid}/reset` });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a screenshot of a VM
|
||||
*/
|
||||
static async Screenshot(vm: VMInfo): Promise<Blob> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uiid}/screenshot`,
|
||||
method: "GET",
|
||||
})
|
||||
).data;
|
||||
}
|
||||
}
|
||||
|
42
remote_frontend/src/widgets/VMLiveScreenshot.tsx
Normal file
42
remote_frontend/src/widgets/VMLiveScreenshot.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import React from "react";
|
||||
import { VMApi, VMInfo } from "../api/VMApi";
|
||||
import { useToast } from "../hooks/providers/ToastProvider";
|
||||
|
||||
export function VMLiveScreenshot(p: { vm: VMInfo }): React.ReactElement {
|
||||
const toast = useToast();
|
||||
|
||||
const [screenshotURL, setScreenshotURL] = React.useState<
|
||||
string | undefined
|
||||
>();
|
||||
|
||||
const int = React.useRef<number | undefined>();
|
||||
|
||||
React.useEffect(() => {
|
||||
const refresh = async () => {
|
||||
try {
|
||||
const screenshot = await VMApi.Screenshot(p.vm);
|
||||
const u = URL.createObjectURL(screenshot);
|
||||
setScreenshotURL(u);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast(p.vm.name, "Failed to get a screenshot of the VM!", "error");
|
||||
}
|
||||
};
|
||||
|
||||
if (int.current === undefined) {
|
||||
refresh();
|
||||
int.current = setInterval(() => refresh(), 5000);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (int.current !== undefined) {
|
||||
clearInterval(int.current);
|
||||
int.current = undefined;
|
||||
}
|
||||
};
|
||||
}, [p.vm, toast]);
|
||||
|
||||
return (
|
||||
<img src={screenshotURL} style={{ width: "100%" }} alt="VM screenshot" />
|
||||
);
|
||||
}
|
@ -8,6 +8,8 @@ import {
|
||||
CardPreview,
|
||||
Spinner,
|
||||
Tooltip,
|
||||
makeStyles,
|
||||
typographyStyles,
|
||||
} from "@fluentui/react-components";
|
||||
import {
|
||||
ArrowResetRegular,
|
||||
@ -22,6 +24,11 @@ import { VMApi, VMInfo, VMState } from "../api/VMApi";
|
||||
import { useToast } from "../hooks/providers/ToastProvider";
|
||||
import { AsyncWidget } from "./AsyncWidget";
|
||||
import { SectionContainer } from "./SectionContainer";
|
||||
import { VMLiveScreenshot } from "./VMLiveScreenshot";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
body1Stronger: typographyStyles.body1Stronger,
|
||||
});
|
||||
|
||||
export function VirtualMachinesWidget(): React.ReactElement {
|
||||
const [list, setList] = React.useState<VMInfo[] | undefined>();
|
||||
@ -86,13 +93,15 @@ function VMWidget(p: { vm: VMInfo }): React.ReactElement {
|
||||
style={{
|
||||
width: "400px",
|
||||
maxWidth: "49%",
|
||||
height: "250px",
|
||||
height: "350px",
|
||||
margin: "10px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<CardPreview>TODO preview</CardPreview>
|
||||
<CardPreview style={{ height: "150px", display: "flex" }}>
|
||||
<VMPreview {...p} state={state} />
|
||||
</CardPreview>
|
||||
|
||||
<CardHeader
|
||||
image={<DesktopRegular fontSize={32} />}
|
||||
@ -183,6 +192,26 @@ function VMWidget(p: { vm: VMInfo }): React.ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
function VMPreview(p: { vm: VMInfo; state?: VMState }): React.ReactElement {
|
||||
const styles = useStyles();
|
||||
if (!p.vm.can_screenshot || p.state !== "Running") {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
flex: "1",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<span className={styles.body1Stronger}>{p.vm.name}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <VMLiveScreenshot {...p} />;
|
||||
}
|
||||
|
||||
function VMAction(p: {
|
||||
vm: VMInfo;
|
||||
label: string;
|
||||
@ -194,6 +223,7 @@ function VMAction(p: {
|
||||
onClick: (vm: VMInfo) => Promise<void>;
|
||||
}): React.ReactElement {
|
||||
const toast = useToast();
|
||||
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const onClick = async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user