import { DesktopWindows as DesktopWindowsIcon, PowerSettingsNewOutlined as PowerSettingsNewOutlinedIcon, } from '@mui/icons-material'; import { Box, IconButton as MUIIconButton, IconButtonProps as MUIIconButtonProps, } from '@mui/material'; import { FC, ReactNode, useEffect, useMemo, useState } from 'react'; import { BORDER_RADIUS, GREY, UNSELECTED, } from '../../lib/consts/DEFAULT_THEME'; import api from '../../lib/api'; import FlexBox from '../FlexBox'; import IconButton, { IconButtonProps } from '../IconButton'; import { InnerPanel, InnerPanelHeader, Panel, PanelHeader } from '../Panels'; import ServerMenu from '../ServerMenu'; import Spinner from '../Spinner'; import { BodyText, HeaderText } from '../Text'; import { elapsed, last, now } from '../../lib/time'; type PreviewOptionalProps = { externalPreview?: string; externalTimestamp?: number; headerEndAdornment?: ReactNode; hrefPreview?: string; isExternalLoading?: boolean; isExternalPreviewStale?: boolean; isFetchPreview?: boolean; isShowControls?: boolean; isUseInnerPanel?: boolean; onClickConnectButton?: IconButtonProps['onClick']; onClickPreview?: MUIIconButtonProps['onClick']; serverName?: string; serverState?: string; }; type PreviewProps = PreviewOptionalProps & { serverUUID: string; }; const PREVIEW_DEFAULT_PROPS: Required< Omit< PreviewOptionalProps, 'hrefPreview' | 'onClickConnectButton' | 'onClickPreview' > > & Pick< PreviewOptionalProps, 'hrefPreview' | 'onClickConnectButton' | 'onClickPreview' > = { externalPreview: '', externalTimestamp: 0, headerEndAdornment: null, hrefPreview: undefined, isExternalLoading: false, isExternalPreviewStale: false, isFetchPreview: true, isShowControls: true, isUseInnerPanel: false, onClickConnectButton: undefined, onClickPreview: undefined, serverName: '', serverState: '', }; const PreviewPanel: FC<{ isUseInnerPanel: boolean }> = ({ children, isUseInnerPanel, }) => isUseInnerPanel ? ( {children} ) : ( {children} ); const PreviewPanelHeader: FC<{ isUseInnerPanel: boolean; text: string | undefined; }> = ({ children, isUseInnerPanel, text }) => isUseInnerPanel ? ( {text ? : <>} {children} ) : ( {text ? : <>} {children} ); const Preview: FC = ({ externalPreview = PREVIEW_DEFAULT_PROPS.externalPreview, externalTimestamp = PREVIEW_DEFAULT_PROPS.externalTimestamp, headerEndAdornment, hrefPreview, isExternalLoading = PREVIEW_DEFAULT_PROPS.isExternalLoading, isExternalPreviewStale = PREVIEW_DEFAULT_PROPS.isExternalPreviewStale, isFetchPreview = PREVIEW_DEFAULT_PROPS.isFetchPreview, isShowControls = PREVIEW_DEFAULT_PROPS.isShowControls, isUseInnerPanel = PREVIEW_DEFAULT_PROPS.isUseInnerPanel, onClickPreview: previewClickHandler, serverName = PREVIEW_DEFAULT_PROPS.serverName, serverState = PREVIEW_DEFAULT_PROPS.serverState, serverUUID, onClickConnectButton: connectButtonClickHandle = previewClickHandler, }) => { const [isPreviewLoading, setIsPreviewLoading] = useState(true); const [isPreviewStale, setIsPreviewStale] = useState(false); const [preview, setPreview] = useState(''); const [previewTimstamp, setPreviewTimestamp] = useState(0); const nao = now(); const previewButtonContent = useMemo( () => serverState === 'running' ? ( <> {isPreviewStale && ((sst: number) => { const { unit, value } = elapsed(nao - sst); return ( Updated ~{value} {unit} ago ); })(previewTimstamp)} ) : ( ), [ isPreviewStale, isUseInnerPanel, nao, preview, previewTimstamp, serverState, ], ); const iconButton = useMemo(() => { if (isPreviewLoading) { return ; } const disabled = !preview; const sx: MUIIconButtonProps['sx'] = { borderRadius: BORDER_RADIUS, color: GREY, padding: 0, }; if (hrefPreview) { return ( {previewButtonContent} ); } return ( {previewButtonContent} ); }, [ hrefPreview, isPreviewLoading, preview, previewButtonContent, previewClickHandler, ]); useEffect(() => { if (isFetchPreview) { (async () => { try { const { data } = await api.get<{ screenshot: string; timestamp: number; }>(`/server/${serverUUID}?ss=1`); const { screenshot, timestamp } = data; setPreview(screenshot); setPreviewTimestamp(timestamp); setIsPreviewStale(!last(timestamp, 300)); } catch { setIsPreviewStale(true); } finally { setIsPreviewLoading(false); } })(); } else if (!isExternalLoading) { setPreview(externalPreview); setPreviewTimestamp(externalTimestamp); setIsPreviewStale(isExternalPreviewStale); setIsPreviewLoading(false); } }, [ externalPreview, externalTimestamp, isExternalLoading, isExternalPreviewStale, isFetchPreview, serverUUID, ]); return ( {headerEndAdornment} :first-child': { flexGrow: 1 } }}> {/* Box wrapper below is required to keep external preview size sane. */} {iconButton} {isShowControls && preview && ( )} ); }; Preview.defaultProps = PREVIEW_DEFAULT_PROPS; export default Preview;