Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
5.3 KiB

import {
DesktopWindows as DesktopWindowsIcon,
PowerOffOutlined as PowerOffOutlinedIcon,
} from '@mui/icons-material';
import {
Box,
IconButton as MUIIconButton,
IconButtonProps as MUIIconButtonProps,
} from '@mui/material';
import { FC, ReactNode, useEffect, useMemo, useState } from 'react';
import API_BASE_URL from '../../lib/consts/API_BASE_URL';
import { BORDER_RADIUS, GREY } from '../../lib/consts/DEFAULT_THEME';
import FlexBox from '../FlexBox';
import IconButton, { IconButtonProps } from '../IconButton';
import { InnerPanel, InnerPanelHeader, Panel, PanelHeader } from '../Panels';
import Spinner from '../Spinner';
import { BodyText, HeaderText } from '../Text';
type PreviewOptionalProps = {
externalPreview?: string;
headerEndAdornment?: ReactNode;
isExternalPreviewStale?: boolean;
isFetchPreview?: boolean;
isShowControls?: boolean;
isUseInnerPanel?: boolean;
onClickConnectButton?: IconButtonProps['onClick'];
onClickPreview?: MUIIconButtonProps['onClick'];
serverName?: string;
};
type PreviewProps = PreviewOptionalProps & {
serverUUID: string;
};
const PREVIEW_DEFAULT_PROPS: Required<
Omit<PreviewOptionalProps, 'onClickConnectButton' | 'onClickPreview'>
> &
Pick<PreviewOptionalProps, 'onClickConnectButton' | 'onClickPreview'> = {
externalPreview: '',
headerEndAdornment: null,
isExternalPreviewStale: false,
isFetchPreview: true,
isShowControls: true,
isUseInnerPanel: false,
onClickConnectButton: undefined,
onClickPreview: undefined,
serverName: '',
};
const PreviewPanel: FC<{ isUseInnerPanel: boolean }> = ({
children,
isUseInnerPanel,
}) =>
isUseInnerPanel ? (
<InnerPanel>{children}</InnerPanel>
) : (
<Panel>{children}</Panel>
);
const PreviewPanelHeader: FC<{
isUseInnerPanel: boolean;
text: string | undefined;
}> = ({ children, isUseInnerPanel, text }) =>
isUseInnerPanel ? (
<InnerPanelHeader>
{text ? <BodyText text={text} /> : <></>}
{children}
</InnerPanelHeader>
) : (
<PanelHeader>
{text ? <HeaderText text={text} /> : <></>}
{children}
</PanelHeader>
);
const Preview: FC<PreviewProps> = ({
externalPreview = PREVIEW_DEFAULT_PROPS.externalPreview,
headerEndAdornment,
isExternalPreviewStale = PREVIEW_DEFAULT_PROPS.isExternalPreviewStale,
isFetchPreview = PREVIEW_DEFAULT_PROPS.isFetchPreview,
isShowControls = PREVIEW_DEFAULT_PROPS.isShowControls,
isUseInnerPanel = PREVIEW_DEFAULT_PROPS.isUseInnerPanel,
onClickPreview: previewClickHandler,
serverName,
serverUUID,
onClickConnectButton: connectButtonClickHandle = previewClickHandler,
}) => {
const [isPreviewLoading, setIsPreviewLoading] = useState<boolean>(true);
const [isPreviewStale, setIsPreviewStale] = useState<boolean>(false);
const [preview, setPreview] = useState<string>('');
const previewButtonContent = useMemo(
() =>
preview ? (
<Box
alt=""
component="img"
src={`data:image/png;base64,${preview}`}
sx={{
height: '100%',
opacity: isPreviewStale ? '0.4' : '1',
padding: isUseInnerPanel ? '.2em' : 0,
width: '100%',
}}
/>
) : (
<PowerOffOutlinedIcon
sx={{
height: '100%',
width: '100%',
}}
/>
),
[isPreviewStale, isUseInnerPanel, preview],
);
useEffect(() => {
if (isFetchPreview) {
(async () => {
try {
const response = await fetch(
`${API_BASE_URL}/server/${serverUUID}?ss`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
);
const { screenshot: fetchedScreenshot } = await response.json();
setPreview(fetchedScreenshot);
setIsPreviewStale(false);
} catch {
setIsPreviewStale(true);
} finally {
setIsPreviewLoading(false);
}
})();
} else if (externalPreview) {
setPreview(externalPreview);
setIsPreviewStale(isExternalPreviewStale);
setIsPreviewLoading(false);
}
}, [externalPreview, isExternalPreviewStale, isFetchPreview, serverUUID]);
return (
<PreviewPanel isUseInnerPanel={isUseInnerPanel}>
<PreviewPanelHeader isUseInnerPanel={isUseInnerPanel} text={serverName}>
{headerEndAdornment}
</PreviewPanelHeader>
<FlexBox row sx={{ '& > :first-child': { flexGrow: 1 } }}>
{/* Box wrapper below is required to keep external preview size sane. */}
<Box>
{isPreviewLoading ? (
<Spinner mt="1em" mb="1em" />
) : (
<MUIIconButton
component="span"
onClick={previewClickHandler}
sx={{
borderRadius: BORDER_RADIUS,
color: GREY,
padding: 0,
}}
>
{previewButtonContent}
</MUIIconButton>
)}
</Box>
{isShowControls && (
<FlexBox>
<IconButton onClick={connectButtonClickHandle}>
<DesktopWindowsIcon />
</IconButton>
</FlexBox>
)}
</FlexBox>
</PreviewPanel>
);
};
Preview.defaultProps = PREVIEW_DEFAULT_PROPS;
export default Preview;