Merge pull request #480 from ylei-tsubame/patch-screenshot-icons

Web UI: patch server screenshot representations
main
Digimer 1 year ago committed by GitHub
commit 51319b2389
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      striker-ui-api/out/index.js
  2. 5
      striker-ui-api/src/lib/request_handlers/server/getServerDetail.ts
  3. 1
      striker-ui-api/src/types/ApiServer.d.ts
  4. 115
      striker-ui/components/Display/Preview.tsx
  5. 2
      striker-ui/components/Servers.tsx
  6. 55
      striker-ui/lib/time.ts
  7. 1
      striker-ui/out/_next/static/chunks/227-a3756585a7ef09ae.js
  8. 1
      striker-ui/out/_next/static/chunks/433-a3be905e7a7d3bfc.js
  9. 1
      striker-ui/out/_next/static/chunks/528-72edc50189f30fa9.js
  10. 1
      striker-ui/out/_next/static/chunks/717-8bd60b96d67fd464.js
  11. 1
      striker-ui/out/_next/static/chunks/94-8322ed453a3c08f0.js
  12. 1
      striker-ui/out/_next/static/chunks/94-e103c3735f0e061b.js
  13. 2
      striker-ui/out/_next/static/chunks/pages/anvil-5ff2efa937105177.js
  14. 1
      striker-ui/out/_next/static/chunks/pages/index-03c43a0be65dfb49.js
  15. 1
      striker-ui/out/_next/static/chunks/pages/index-6ff72adab3a682db.js
  16. 1
      striker-ui/out/_next/static/chunks/pages/server-4ac03eba56ccfcbf.js
  17. 1
      striker-ui/out/_next/static/chunks/pages/server-db52258419acacf3.js
  18. 2
      striker-ui/out/_next/static/tsboOH-aG8W5HRHH4dt2_/_buildManifest.js
  19. 0
      striker-ui/out/_next/static/tsboOH-aG8W5HRHH4dt2_/_middlewareManifest.js
  20. 0
      striker-ui/out/_next/static/tsboOH-aG8W5HRHH4dt2_/_ssgManifest.js
  21. 2
      striker-ui/out/anvil.html
  22. 2
      striker-ui/out/config.html
  23. 2
      striker-ui/out/file-manager.html
  24. 2
      striker-ui/out/index.html
  25. 2
      striker-ui/out/init.html
  26. 2
      striker-ui/out/login.html
  27. 2
      striker-ui/out/manage-element.html
  28. 2
      striker-ui/out/server.html
  29. 28
      striker-ui/pages/index.tsx
  30. 7
      striker-ui/pages/server/index.tsx

File diff suppressed because one or more lines are too long

@ -57,7 +57,7 @@ export const getServerDetail: RequestHandler<
} }
if (ss) { if (ss) {
const rsBody: ServerDetailScreenshot = { screenshot: '' }; const rsBody: ServerDetailScreenshot = { screenshot: '', timestamp: 0 };
const ssDir = SERVER_PATHS.opt.alteeve.screenshots.self; const ssDir = SERVER_PATHS.opt.alteeve.screenshots.self;
let ssNames: string[]; let ssNames: string[];
@ -95,13 +95,14 @@ export const getServerDetail: RequestHandler<
stdoutVar(ssMetaLatest, `Latest server screenshot: `); stdoutVar(ssMetaLatest, `Latest server screenshot: `);
if (ssMetaLatest) { if (ssMetaLatest) {
const { name } = ssMetaLatest; const { name, timestamp } = ssMetaLatest;
const ssLatest = readFileSync(path.join(ssDir, name), { const ssLatest = readFileSync(path.join(ssDir, name), {
encoding: 'base64', encoding: 'base64',
}); });
rsBody.screenshot = ssLatest; rsBody.screenshot = ssLatest;
rsBody.timestamp = timestamp;
} }
return response.send(rsBody); return response.send(rsBody);

@ -19,6 +19,7 @@ type ServerDetailParsedQs = {
type ServerDetailScreenshot = { type ServerDetailScreenshot = {
screenshot: string; screenshot: string;
timestamp: number;
}; };
type ServerDetailVncInfo = { type ServerDetailVncInfo = {

@ -1,6 +1,6 @@
import { import {
DesktopWindows as DesktopWindowsIcon, DesktopWindows as DesktopWindowsIcon,
PowerOffOutlined as PowerOffOutlinedIcon, PowerSettingsNewOutlined as PowerSettingsNewOutlinedIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { import {
Box, Box,
@ -9,18 +9,25 @@ import {
} from '@mui/material'; } from '@mui/material';
import { FC, ReactNode, useEffect, useMemo, useState } from 'react'; import { FC, ReactNode, useEffect, useMemo, useState } from 'react';
import API_BASE_URL from '../../lib/consts/API_BASE_URL'; import {
import { BORDER_RADIUS, GREY } from '../../lib/consts/DEFAULT_THEME'; BORDER_RADIUS,
GREY,
UNSELECTED,
} from '../../lib/consts/DEFAULT_THEME';
import api from '../../lib/api';
import FlexBox from '../FlexBox'; import FlexBox from '../FlexBox';
import IconButton, { IconButtonProps } from '../IconButton'; import IconButton, { IconButtonProps } from '../IconButton';
import { InnerPanel, InnerPanelHeader, Panel, PanelHeader } from '../Panels'; import { InnerPanel, InnerPanelHeader, Panel, PanelHeader } from '../Panels';
import Spinner from '../Spinner'; import Spinner from '../Spinner';
import { BodyText, HeaderText } from '../Text'; import { BodyText, HeaderText } from '../Text';
import { elapsed, last, now } from '../../lib/time';
type PreviewOptionalProps = { type PreviewOptionalProps = {
externalPreview?: string; externalPreview?: string;
externalTimestamp?: number;
headerEndAdornment?: ReactNode; headerEndAdornment?: ReactNode;
isExternalLoading?: boolean;
isExternalPreviewStale?: boolean; isExternalPreviewStale?: boolean;
isFetchPreview?: boolean; isFetchPreview?: boolean;
isShowControls?: boolean; isShowControls?: boolean;
@ -28,6 +35,7 @@ type PreviewOptionalProps = {
onClickConnectButton?: IconButtonProps['onClick']; onClickConnectButton?: IconButtonProps['onClick'];
onClickPreview?: MUIIconButtonProps['onClick']; onClickPreview?: MUIIconButtonProps['onClick'];
serverName?: string; serverName?: string;
serverState?: string;
}; };
type PreviewProps = PreviewOptionalProps & { type PreviewProps = PreviewOptionalProps & {
@ -39,7 +47,9 @@ const PREVIEW_DEFAULT_PROPS: Required<
> & > &
Pick<PreviewOptionalProps, 'onClickConnectButton' | 'onClickPreview'> = { Pick<PreviewOptionalProps, 'onClickConnectButton' | 'onClickPreview'> = {
externalPreview: '', externalPreview: '',
externalTimestamp: 0,
headerEndAdornment: null, headerEndAdornment: null,
isExternalLoading: false,
isExternalPreviewStale: false, isExternalPreviewStale: false,
isFetchPreview: true, isFetchPreview: true,
isShowControls: true, isShowControls: true,
@ -47,6 +57,7 @@ const PREVIEW_DEFAULT_PROPS: Required<
onClickConnectButton: undefined, onClickConnectButton: undefined,
onClickPreview: undefined, onClickPreview: undefined,
serverName: '', serverName: '',
serverState: '',
}; };
const PreviewPanel: FC<{ isUseInnerPanel: boolean }> = ({ const PreviewPanel: FC<{ isUseInnerPanel: boolean }> = ({
@ -77,74 +88,105 @@ const PreviewPanelHeader: FC<{
const Preview: FC<PreviewProps> = ({ const Preview: FC<PreviewProps> = ({
externalPreview = PREVIEW_DEFAULT_PROPS.externalPreview, externalPreview = PREVIEW_DEFAULT_PROPS.externalPreview,
externalTimestamp = PREVIEW_DEFAULT_PROPS.externalTimestamp,
headerEndAdornment, headerEndAdornment,
isExternalLoading = PREVIEW_DEFAULT_PROPS.isExternalLoading,
isExternalPreviewStale = PREVIEW_DEFAULT_PROPS.isExternalPreviewStale, isExternalPreviewStale = PREVIEW_DEFAULT_PROPS.isExternalPreviewStale,
isFetchPreview = PREVIEW_DEFAULT_PROPS.isFetchPreview, isFetchPreview = PREVIEW_DEFAULT_PROPS.isFetchPreview,
isShowControls = PREVIEW_DEFAULT_PROPS.isShowControls, isShowControls = PREVIEW_DEFAULT_PROPS.isShowControls,
isUseInnerPanel = PREVIEW_DEFAULT_PROPS.isUseInnerPanel, isUseInnerPanel = PREVIEW_DEFAULT_PROPS.isUseInnerPanel,
onClickPreview: previewClickHandler, onClickPreview: previewClickHandler,
serverName, serverName,
serverState = PREVIEW_DEFAULT_PROPS.serverState,
serverUUID, serverUUID,
onClickConnectButton: connectButtonClickHandle = previewClickHandler, onClickConnectButton: connectButtonClickHandle = previewClickHandler,
}) => { }) => {
const [isPreviewLoading, setIsPreviewLoading] = useState<boolean>(true); const [isPreviewLoading, setIsPreviewLoading] = useState<boolean>(true);
const [isPreviewStale, setIsPreviewStale] = useState<boolean>(false); const [isPreviewStale, setIsPreviewStale] = useState<boolean>(false);
const [preview, setPreview] = useState<string>(''); const [preview, setPreview] = useState<string>('');
const [previewTimstamp, setPreviewTimestamp] = useState<number>(0);
const nao = now();
const previewButtonContent = useMemo( const previewButtonContent = useMemo(
() => () =>
preview ? ( serverState === 'running' ? (
<Box <>
alt="" <Box
component="img" alt=""
src={`data:image/png;base64,${preview}`} component="img"
sx={{ src={`data:image;base64,${preview}`}
height: '100%', sx={{
opacity: isPreviewStale ? '0.4' : '1', height: '100%',
padding: isUseInnerPanel ? '.2em' : 0, opacity: isPreviewStale ? '0.4' : '1',
width: '100%', padding: isUseInnerPanel ? '.2em' : 0,
}} width: '100%',
/> }}
/>
{isPreviewStale &&
((sst: number) => {
const { unit, value } = elapsed(nao - sst);
return (
<BodyText position="absolute">
Updated ~{value} {unit} ago
</BodyText>
);
})(previewTimstamp)}
</>
) : ( ) : (
<PowerOffOutlinedIcon <PowerSettingsNewOutlinedIcon
sx={{ sx={{
height: '100%', color: UNSELECTED,
width: '100%', height: '80%',
width: '80%',
}} }}
/> />
), ),
[isPreviewStale, isUseInnerPanel, preview], [
isPreviewStale,
isUseInnerPanel,
nao,
preview,
previewTimstamp,
serverState,
],
); );
useEffect(() => { useEffect(() => {
if (isFetchPreview) { if (isFetchPreview) {
(async () => { (async () => {
try { try {
const response = await fetch( const { data } = await api.get<{
`${API_BASE_URL}/server/${serverUUID}?ss=1`, screenshot: string;
{ timestamp: number;
method: 'GET', }>(`/server/${serverUUID}?ss=1`);
headers: {
'Content-Type': 'application/json', const { screenshot, timestamp } = data;
},
}, setPreview(screenshot);
); setPreviewTimestamp(timestamp);
const { screenshot: fetchedScreenshot } = await response.json(); setIsPreviewStale(!last(timestamp, 300));
setPreview(fetchedScreenshot);
setIsPreviewStale(false);
} catch { } catch {
setIsPreviewStale(true); setIsPreviewStale(true);
} finally { } finally {
setIsPreviewLoading(false); setIsPreviewLoading(false);
} }
})(); })();
} else if (externalPreview) { } else if (!isExternalLoading) {
setPreview(externalPreview); setPreview(externalPreview);
setPreviewTimestamp(externalTimestamp);
setIsPreviewStale(isExternalPreviewStale); setIsPreviewStale(isExternalPreviewStale);
setIsPreviewLoading(false); setIsPreviewLoading(false);
} }
}, [externalPreview, isExternalPreviewStale, isFetchPreview, serverUUID]); }, [
externalPreview,
externalTimestamp,
isExternalLoading,
isExternalPreviewStale,
isFetchPreview,
serverUUID,
]);
return ( return (
<PreviewPanel isUseInnerPanel={isUseInnerPanel}> <PreviewPanel isUseInnerPanel={isUseInnerPanel}>
@ -153,12 +195,13 @@ const Preview: FC<PreviewProps> = ({
</PreviewPanelHeader> </PreviewPanelHeader>
<FlexBox row sx={{ '& > :first-child': { flexGrow: 1 } }}> <FlexBox row sx={{ '& > :first-child': { flexGrow: 1 } }}>
{/* Box wrapper below is required to keep external preview size sane. */} {/* Box wrapper below is required to keep external preview size sane. */}
<Box> <Box textAlign="center">
{isPreviewLoading ? ( {isPreviewLoading ? (
<Spinner mt="1em" mb="1em" /> <Spinner mt="1em" mb="1em" />
) : ( ) : (
<MUIIconButton <MUIIconButton
component="span" component="span"
disabled={!preview}
onClick={previewClickHandler} onClick={previewClickHandler}
sx={{ sx={{
borderRadius: BORDER_RADIUS, borderRadius: BORDER_RADIUS,
@ -170,7 +213,7 @@ const Preview: FC<PreviewProps> = ({
</MUIIconButton> </MUIIconButton>
)} )}
</Box> </Box>
{isShowControls && ( {isShowControls && preview && (
<FlexBox> <FlexBox>
<IconButton onClick={connectButtonClickHandle}> <IconButton onClick={connectButtonClickHandle}>
<DesktopWindowsIcon /> <DesktopWindowsIcon />

@ -335,7 +335,7 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
className={classes.button} className={classes.button}
key={server.serverUUID} key={server.serverUUID}
component={showCheckbox ? 'div' : 'a'} component={showCheckbox ? 'div' : 'a'}
href={`/server?uuid=${server.serverUUID}&server_name=${server.serverName}`} href={`/server?uuid=${server.serverUUID}&server_name=${server.serverName}&server_state=${server.serverState}`}
onClick={() => handleChange(server.serverUUID)} onClick={() => handleChange(server.serverUUID)}
> >
<Box display="flex" flexDirection="row" width="100%"> <Box display="flex" flexDirection="row" width="100%">

@ -0,0 +1,55 @@
const before = (time: number, limit: number): boolean => {
const diff = time - limit;
return diff > 0;
};
const now = (ms?: boolean): number => {
let nao = Date.now();
if (!ms) nao = Math.floor(nao / 1000);
return nao;
};
const last = (
time: number,
duration: number,
{ ms }: { ms?: boolean } = {},
): boolean => {
const diff = now(ms) - time;
return diff <= duration;
};
const elapsed = (
duration: number,
): { h: number; m: number; s: number; unit: string; value: number } => {
let src = duration;
const parts = [60, 60].reduce<number[]>((previous, multiplier) => {
const remainder = src % multiplier;
previous.push(remainder);
src = (src - remainder) / multiplier;
return previous;
}, []);
const [s, m, h] = [...parts, src];
const significant = [
{ unit: 'h', value: h },
{ unit: 'm', value: m },
].find(({ value }) => value) ?? { unit: 's', value: s };
return {
h,
m,
s,
...significant,
};
};
export { before, elapsed, last, now };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
self.__BUILD_MANIFEST=function(s,c,a,t,e,n,i,d,f,b,u,k,h,j,r,g,l,_){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,a,t,d,f,h,"static/chunks/717-8bd60b96d67fd464.js",c,e,n,i,j,r,"static/chunks/pages/index-03c43a0be65dfb49.js"],"/_error":["static/chunks/pages/_error-2280fa386d040b66.js"],"/anvil":[s,a,t,d,f,h,c,e,n,i,j,"static/chunks/pages/anvil-5058ba8058633c3d.js"],"/config":[s,a,t,u,"static/chunks/586-4e70511cf6d7632f.js",c,e,n,i,b,k,g,"static/chunks/pages/config-0cb597caf390573f.js"],"/file-manager":["static/chunks/29107295-fbcfe2172188e46f.js",s,a,t,d,"static/chunks/176-7308c25ba374961e.js",c,e,i,b,"static/chunks/pages/file-manager-1ae01a78e266275a.js"],"/init":[s,a,d,f,u,l,c,e,n,i,_,"static/chunks/pages/init-053607258b5d7d64.js"],"/login":[s,a,t,c,e,n,b,k,"static/chunks/pages/login-1b987b077ffc3420.js"],"/manage-element":[s,a,t,d,f,u,l,"static/chunks/111-2605129c170ed35d.js",c,e,n,i,b,k,_,g,"static/chunks/pages/manage-element-6b42a013966413d3.js"],"/server":[s,t,"static/chunks/227-a3756585a7ef09ae.js",c,r,"static/chunks/pages/server-db52258419acacf3.js"],sortedPages:["/","/_app","/_error","/anvil","/config","/file-manager","/init","/login","/manage-element","/server"]}}("static/chunks/382-f51344f6f9208507.js","static/chunks/62-532ed713980da8db.js","static/chunks/438-0147a63d98e89439.js","static/chunks/894-e57948de523bcf96.js","static/chunks/195-fa06e61dd4339031.js","static/chunks/987-1ff0d82724b0e58b.js","static/chunks/157-d1418743accab385.js","static/chunks/182-08683bbe95fbb010.js","static/chunks/434-07ec1dcc649bdd0c.js","static/chunks/248-749f2bec4cb43d28.js","static/chunks/644-c7c6e21c71345aed.js","static/chunks/336-8a7866afcf131f68.js","static/chunks/485-77798bccc4308d0e.js","static/chunks/825-0b3ee47570192a02.js","static/chunks/94-e103c3735f0e061b.js","static/chunks/560-0ed707609765e23a.js","static/chunks/676-6159ce853338cc1f.js","static/chunks/86-447b52c8195dea3d.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); self.__BUILD_MANIFEST=function(s,c,a,t,e,n,i,f,d,b,u,k,h,j,r,g,l,_){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,a,t,f,d,h,"static/chunks/433-a3be905e7a7d3bfc.js",c,e,n,i,j,r,"static/chunks/pages/index-6ff72adab3a682db.js"],"/_error":["static/chunks/pages/_error-2280fa386d040b66.js"],"/anvil":[s,a,t,f,d,h,c,e,n,i,j,"static/chunks/pages/anvil-5ff2efa937105177.js"],"/config":[s,a,t,u,"static/chunks/586-4e70511cf6d7632f.js",c,e,n,i,b,k,g,"static/chunks/pages/config-0cb597caf390573f.js"],"/file-manager":["static/chunks/29107295-fbcfe2172188e46f.js",s,a,t,f,"static/chunks/176-7308c25ba374961e.js",c,e,i,b,"static/chunks/pages/file-manager-1ae01a78e266275a.js"],"/init":[s,a,f,d,u,l,c,e,n,i,_,"static/chunks/pages/init-053607258b5d7d64.js"],"/login":[s,a,t,c,e,n,b,k,"static/chunks/pages/login-1b987b077ffc3420.js"],"/manage-element":[s,a,t,f,d,u,l,"static/chunks/111-2605129c170ed35d.js",c,e,n,i,b,k,_,g,"static/chunks/pages/manage-element-6b42a013966413d3.js"],"/server":[s,t,"static/chunks/528-72edc50189f30fa9.js",c,r,"static/chunks/pages/server-4ac03eba56ccfcbf.js"],sortedPages:["/","/_app","/_error","/anvil","/config","/file-manager","/init","/login","/manage-element","/server"]}}("static/chunks/382-f51344f6f9208507.js","static/chunks/62-532ed713980da8db.js","static/chunks/438-0147a63d98e89439.js","static/chunks/894-e57948de523bcf96.js","static/chunks/195-fa06e61dd4339031.js","static/chunks/987-1ff0d82724b0e58b.js","static/chunks/157-d1418743accab385.js","static/chunks/182-08683bbe95fbb010.js","static/chunks/434-07ec1dcc649bdd0c.js","static/chunks/248-749f2bec4cb43d28.js","static/chunks/644-c7c6e21c71345aed.js","static/chunks/336-8a7866afcf131f68.js","static/chunks/485-77798bccc4308d0e.js","static/chunks/825-0b3ee47570192a02.js","static/chunks/94-8322ed453a3c08f0.js","static/chunks/560-0ed707609765e23a.js","static/chunks/676-6159ce853338cc1f.js","static/chunks/86-447b52c8195dea3d.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -17,10 +17,13 @@ import { Panel, PanelHeader } from '../components/Panels';
import periodicFetch from '../lib/fetchers/periodicFetch'; import periodicFetch from '../lib/fetchers/periodicFetch';
import ProvisionServerDialog from '../components/ProvisionServerDialog'; import ProvisionServerDialog from '../components/ProvisionServerDialog';
import Spinner from '../components/Spinner'; import Spinner from '../components/Spinner';
import { last } from '../lib/time';
type ServerListItem = ServerOverviewMetadata & { type ServerListItem = ServerOverviewMetadata & {
isScreenshotStale?: boolean; isScreenshotStale?: boolean;
loading?: boolean;
screenshot: string; screenshot: string;
timestamp: number;
}; };
const createServerPreviewContainer = ( const createServerPreviewContainer = (
@ -47,15 +50,19 @@ const createServerPreviewContainer = (
anvilName, anvilName,
anvilUUID, anvilUUID,
isScreenshotStale, isScreenshotStale,
loading,
screenshot, screenshot,
serverName, serverName,
serverState,
serverUUID, serverUUID,
timestamp,
}) => ( }) => (
<Preview <Preview
externalPreview={screenshot} externalPreview={screenshot}
externalTimestamp={timestamp}
headerEndAdornment={[ headerEndAdornment={[
<Link <Link
href={`/server?uuid=${serverUUID}&server_name=${serverName}`} href={`/server?uuid=${serverUUID}&server_name=${serverName}&server_state=${serverState}`}
key={`server_list_to_server_${serverUUID}`} key={`server_list_to_server_${serverUUID}`}
> >
{serverName} {serverName}
@ -70,6 +77,7 @@ const createServerPreviewContainer = (
{anvilName} {anvilName}
</Link>, </Link>,
]} ]}
isExternalLoading={loading}
isExternalPreviewStale={isScreenshotStale} isExternalPreviewStale={isScreenshotStale}
isFetchPreview={false} isFetchPreview={false}
isShowControls={false} isShowControls={false}
@ -77,9 +85,10 @@ const createServerPreviewContainer = (
key={`server-preview-${serverUUID}`} key={`server-preview-${serverUUID}`}
onClickPreview={() => { onClickPreview={() => {
router.push( router.push(
`/server?uuid=${serverUUID}&server_name=${serverName}&vnc=1`, `/server?uuid=${serverUUID}&server_name=${serverName}&server_state=${serverState}&vnc=1`,
); );
}} }}
serverState={serverState}
serverUUID={serverUUID} serverUUID={serverUUID}
/> />
), ),
@ -150,15 +159,21 @@ const Dashboard: FC = () => {
?.screenshot || ''; ?.screenshot || '';
const item: ServerListItem = { const item: ServerListItem = {
...serverOverview, ...serverOverview,
loading: true,
screenshot: previousScreenshot, screenshot: previousScreenshot,
timestamp: 0,
}; };
fetchJSON<{ screenshot: string }>( fetchJSON<{ screenshot: string; timestamp: number }>(
`${API_BASE_URL}/server/${serverUUID}?ss=1`, `${API_BASE_URL}/server/${serverUUID}?ss=1`,
) )
.then(({ screenshot }) => { .then(({ screenshot, timestamp }) => {
if (screenshot.length === 0) return;
item.isScreenshotStale = !last(timestamp, 300);
item.loading = false;
item.screenshot = screenshot; item.screenshot = screenshot;
item.isScreenshotStale = false; item.timestamp = timestamp;
const allServersWithScreenshots = [...serverListItems]; const allServersWithScreenshots = [...serverListItems];
@ -174,6 +189,9 @@ const Dashboard: FC = () => {
}) })
.catch(() => { .catch(() => {
item.isScreenshotStale = true; item.isScreenshotStale = true;
})
.finally(() => {
item.loading = false;
}); });
return item; return item;

@ -34,10 +34,12 @@ const Server = (): JSX.Element => {
const [previewMode, setPreviewMode] = useState<boolean>(true); const [previewMode, setPreviewMode] = useState<boolean>(true);
const router = useRouter(); const router = useRouter();
const { server_name, uuid, vnc } = router.query; const { server_name, server_state, uuid, vnc } = router.query;
const isConnectVNC: boolean = (vnc?.toString() || '').length > 0; const isConnectVNC: boolean = (vnc?.toString() || '').length > 0;
const serverUUID: string = uuid?.toString() || '';
const serverName: string = server_name?.toString() || ''; const serverName: string = server_name?.toString() || '';
const serverState: string = server_state?.toString() || '';
const serverUUID: string = uuid?.toString() || '';
useEffect(() => { useEffect(() => {
if (isConnectVNC) { if (isConnectVNC) {
@ -58,6 +60,7 @@ const Server = (): JSX.Element => {
setPreviewMode(false); setPreviewMode(false);
}} }}
serverName={serverName} serverName={serverName}
serverState={serverState}
serverUUID={serverUUID} serverUUID={serverUUID}
/> />
</Box> </Box>

Loading…
Cancel
Save