diff --git a/striker-ui/components/Display/FullSize.tsx b/striker-ui/components/Display/FullSize.tsx index 6d82ef01..8a7c50e1 100644 --- a/striker-ui/components/Display/FullSize.tsx +++ b/striker-ui/components/Display/FullSize.tsx @@ -16,14 +16,12 @@ import { useState, useEffect, FC, useMemo, useRef, useCallback } from 'react'; import { TEXT } from '../../lib/consts/DEFAULT_THEME'; -import ContainedButton from '../ContainedButton'; import IconButton from '../IconButton'; import keyCombinations from './keyCombinations'; import { Panel, PanelHeader } from '../Panels'; import Spinner from '../Spinner'; import { HeaderText } from '../Text'; import useIsFirstRender from '../../hooks/useIsFirstRender'; -import useProtectedState from '../../hooks/useProtectedState'; const PREFIX = 'FullSize'; @@ -68,6 +66,7 @@ type FullSizeOptionalProps = { }; type FullSizeProps = FullSizeOptionalProps & { + vncReconnectTimerStart: number; serverUUID: string; serverName: string | string[] | undefined; }; @@ -78,6 +77,8 @@ const FULL_SIZE_DEFAULT_PROPS: Required< Pick = { onClickCloseButton: undefined, }; +// Unit: seconds +const DEFAULT_VNC_RECONNECT_TIMER_START = 5; const buildServerVncUrl = (hostname: string, serverUuid: string) => `ws://${hostname}/ws/server/vnc/${serverUuid}`; @@ -86,16 +87,19 @@ const FullSize: FC = ({ onClickCloseButton, serverUUID, serverName, + vncReconnectTimerStart = DEFAULT_VNC_RECONNECT_TIMER_START, }): JSX.Element => { const isFirstRender = useIsFirstRender(); const [anchorEl, setAnchorEl] = useState(null); - - const [rfbConnectArgs, setRfbConnectArgs] = useProtectedState< - RfbConnectArgs | undefined + const [rfbConnectArgs, setRfbConnectArgs] = useState< + Partial | undefined >(undefined); - const [vncConnecting, setVncConnecting] = useProtectedState(false); - const [vncError, setVncError] = useProtectedState(false); + const [vncConnecting, setVncConnecting] = useState(false); + const [vncError, setVncError] = useState(false); + const [vncReconnectTimer, setVncReconnectTimer] = useState( + vncReconnectTimerStart, + ); const rfb = useRef(null); const rfbScreen = useRef(null); @@ -124,46 +128,18 @@ const FullSize: FC = ({ } }; - // 'connect' event emits when a connection successfully completes. - const rfbConnectEventHandler = useCallback(() => { - setVncConnecting(false); - }, [setVncConnecting]); - - // 'disconnect' event emits when a connection fails, - // OR when a user closes the existing connection. - const rfbDisconnectEventHandler = useCallback( - ({ detail: { clean } }) => { - if (!clean) { - setVncConnecting(false); - setVncError(true); - } - }, - [setVncConnecting, setVncError], - ); - const connectServerVnc = useCallback(() => { setVncConnecting(true); setVncError(false); setRfbConnectArgs({ - onConnect: rfbConnectEventHandler, - onDisconnect: rfbDisconnectEventHandler, - rfb, - rfbScreen, url: buildServerVncUrl(window.location.hostname, serverUUID), }); - }, [ - rfbConnectEventHandler, - rfbDisconnectEventHandler, - serverUUID, - setRfbConnectArgs, - setVncConnecting, - setVncError, - ]); + }, [serverUUID]); const disconnectServerVnc = useCallback(() => { setRfbConnectArgs(undefined); - }, [setRfbConnectArgs]); + }, []); const reconnectServerVnc = useCallback(() => { if (!rfb?.current) return; @@ -174,6 +150,39 @@ const FullSize: FC = ({ connectServerVnc(); }, [connectServerVnc]); + const updateVncReconnectTimer = useCallback((): void => { + const intervalId = setInterval((): void => { + setVncReconnectTimer((previous) => { + const current = previous - 1; + + if (current < 1) { + clearInterval(intervalId); + } + + return current; + }); + }, 1000); + }, []); + + // 'connect' event emits when a connection successfully completes. + const rfbConnectEventHandler = useCallback(() => { + setVncConnecting(false); + }, []); + + // 'disconnect' event emits when a connection fails, + // OR when a user closes the existing connection. + const rfbDisconnectEventHandler = useCallback( + ({ detail: { clean } }) => { + if (!clean) { + setVncConnecting(false); + setVncError(true); + + updateVncReconnectTimer(); + } + }, + [updateVncReconnectTimer], + ); + const showScreen = useMemo( () => !vncConnecting && !vncError, [vncConnecting, vncError], @@ -232,6 +241,14 @@ const FullSize: FC = ({ [keyboardMenuElement, showScreen, vncDisconnectElement], ); + useEffect(() => { + if (vncReconnectTimer === 0) { + setVncReconnectTimer(vncReconnectTimerStart); + + reconnectServerVnc(); + } + }, [reconnectServerVnc, vncReconnectTimer, vncReconnectTimerStart]); + useEffect(() => { if (isFirstRender) { connectServerVnc(); @@ -250,8 +267,10 @@ const FullSize: FC = ({ className={classes.displayBox} > @@ -259,25 +278,20 @@ const FullSize: FC = ({ {vncConnecting && ( <> - Connecting to {serverName}... + + Connecting to {serverName}. + )} {vncError && ( <> - - - There was a problem connecting to the server, please try - again - - - { - reconnectServerVnc(); - }} - > - Reconnect - + + There was a problem connecting to the server. + + + Retrying in {vncReconnectTimer}. + )} diff --git a/striker-ui/components/Display/VncDisplay.tsx b/striker-ui/components/Display/VncDisplay.tsx index c2167abf..8bd6a74a 100644 --- a/striker-ui/components/Display/VncDisplay.tsx +++ b/striker-ui/components/Display/VncDisplay.tsx @@ -55,15 +55,35 @@ const rfbDisconnect: RfbDisconnectFunction = (rfb) => { }; const VncDisplay = (props: VncDisplayProps): JSX.Element => { - const { rfb, rfbConnectPartialArgs, rfbScreen } = props; + const { + onConnect, + onDisconnect, + rfb, + rfbConnectArgs, + rfbScreen, + url: initUrl, + } = props; useEffect(() => { - if (rfbConnectPartialArgs) { - rfbConnect({ rfb, rfbScreen, ...rfbConnectPartialArgs }); + if (rfbConnectArgs) { + const { url = initUrl } = rfbConnectArgs; + + if (!url) return; + + const args: RfbConnectArgs = { + onConnect, + onDisconnect, + rfb, + rfbScreen, + url, + ...rfbConnectArgs, + }; + + rfbConnect(args); } else { rfbDisconnect(rfb); } - }, [rfb, rfbConnectPartialArgs, rfbScreen]); + }, [initUrl, onConnect, onDisconnect, rfb, rfbConnectArgs, rfbScreen]); useEffect( () => () => { diff --git a/striker-ui/types/VncDisplay.d.ts b/striker-ui/types/VncDisplay.d.ts index d28af9e5..a5cfca4f 100644 --- a/striker-ui/types/VncDisplay.d.ts +++ b/striker-ui/types/VncDisplay.d.ts @@ -26,8 +26,7 @@ type RfbConnectFunction = (args: RfbConnectArgs) => void; type RfbDisconnectFunction = (rfb: RfbRef) => void; -type VncDisplayProps = { - rfb: RfbRef; - rfbConnectPartialArgs?: Omit; - rfbScreen: RfbScreenRef; -}; +type VncDisplayProps = Pick & + Partial & { + rfbConnectArgs?: Partial; + };