diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 6594b0595..ccae2f08b 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -13,6 +13,7 @@ import 'xterm/css/xterm.css'; import useEventListener from '@/plugins/useEventListener'; import { debounce } from 'debounce'; import { usePersistedState } from '@/plugins/usePersistedState'; +import { SocketEvent, SocketRequest } from '@/components/server/events'; const theme = { background: th`colors.black`.toString(), @@ -172,32 +173,35 @@ export default () => { useEventListener('resize', () => fit()); useEffect(() => { + const listeners: Record void> = { + [SocketEvent.STATUS]: handlePowerChangeEvent, + [SocketEvent.CONSOLE_OUTPUT]: handleConsoleOutput, + [SocketEvent.INSTALL_OUTPUT]: handleConsoleOutput, + [SocketEvent.TRANSFER_LOGS]: handleConsoleOutput, + [SocketEvent.TRANSFER_STATUS]: handleTransferStatus, + [SocketEvent.DAEMON_MESSAGE]: line => handleConsoleOutput(line, true), + [SocketEvent.DAEMON_ERROR]: handleDaemonErrorOutput, + }; + if (connected && instance) { // Do not clear the console if the server is being transferred. if (!isTransferring) { terminal.clear(); } - instance.addListener('status', handlePowerChangeEvent); - instance.addListener('console output', handleConsoleOutput); - instance.addListener('install output', handleConsoleOutput); - instance.addListener('transfer logs', handleConsoleOutput); - instance.addListener('transfer status', handleTransferStatus); - instance.addListener('daemon message', line => handleConsoleOutput(line, true)); - instance.addListener('daemon error', handleDaemonErrorOutput); - instance.send('send logs'); + Object.keys(listeners).forEach((key: string) => { + instance.addListener(key, listeners[key]); + }); + instance.send(SocketRequest.SEND_LOGS); } return () => { - instance && instance.removeListener('status', handlePowerChangeEvent) - .removeListener('console output', handleConsoleOutput) - .removeListener('install output', handleConsoleOutput) - .removeListener('transfer logs', handleConsoleOutput) - .removeListener('transfer status', handleTransferStatus) - .removeListener('daemon message', line => handleConsoleOutput(line, true)) - .removeListener('daemon error', handleDaemonErrorOutput); + if (instance) { + Object.keys(listeners).forEach((key: string) => { + instance.removeListener(key, listeners[key]); + }); + } }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ connected, instance ]); return ( diff --git a/resources/scripts/components/server/InstallListener.tsx b/resources/scripts/components/server/InstallListener.tsx index 782b9da3f..c0a7f6ac8 100644 --- a/resources/scripts/components/server/InstallListener.tsx +++ b/resources/scripts/components/server/InstallListener.tsx @@ -1,22 +1,30 @@ import useWebsocketEvent from '@/plugins/useWebsocketEvent'; import { ServerContext } from '@/state/server'; +import { SocketEvent } from '@/components/server/events'; +import useFileManagerSwr from '@/plugins/useFileManagerSwr'; const InstallListener = () => { const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const getServer = ServerContext.useStoreActions(actions => actions.server.getServer); + const { mutate } = useFileManagerSwr(); const setServerFromState = ServerContext.useStoreActions(actions => actions.server.setServerFromState); + useWebsocketEvent(SocketEvent.BACKUP_RESTORE_COMPLETED, () => { + mutate(undefined); + setServerFromState(s => ({ ...s, status: null })); + }); + // Listen for the installation completion event and then fire off a request to fetch the updated // server information. This allows the server to automatically become available to the user if they // just sit on the page. - useWebsocketEvent('install completed', () => { + useWebsocketEvent(SocketEvent.INSTALL_COMPLETED, () => { getServer(uuid).catch(error => console.error(error)); }); // When we see the install started event immediately update the state to indicate such so that the // screens automatically update. - useWebsocketEvent('install started', () => { - setServerFromState(s => ({ ...s, isInstalling: true })); + useWebsocketEvent(SocketEvent.INSTALL_STARTED, () => { + setServerFromState(s => ({ ...s, status: 'installing' })); }); return null; diff --git a/resources/scripts/components/server/ServerDetailsBlock.tsx b/resources/scripts/components/server/ServerDetailsBlock.tsx index 11f41f690..68c57444f 100644 --- a/resources/scripts/components/server/ServerDetailsBlock.tsx +++ b/resources/scripts/components/server/ServerDetailsBlock.tsx @@ -6,6 +6,7 @@ import { bytesToHuman, megabytesToHuman } from '@/helpers'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; import { ServerContext } from '@/state/server'; import CopyOnClick from '@/components/elements/CopyOnClick'; +import { SocketEvent, SocketRequest } from '@/components/server/events'; interface Stats { memory: number; @@ -55,11 +56,11 @@ const ServerDetailsBlock = () => { return; } - instance.addListener('stats', statsListener); - instance.send('send stats'); + instance.addListener(SocketEvent.STATS, statsListener); + instance.send(SocketRequest.SEND_STATS); return () => { - instance.removeListener('stats', statsListener); + instance.removeListener(SocketEvent.STATS, statsListener); }; }, [ instance, connected ]); diff --git a/resources/scripts/components/server/StatGraphs.tsx b/resources/scripts/components/server/StatGraphs.tsx index 725373b30..2623b37b7 100644 --- a/resources/scripts/components/server/StatGraphs.tsx +++ b/resources/scripts/components/server/StatGraphs.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import Chart, { ChartConfiguration } from 'chart.js'; import { ServerContext } from '@/state/server'; import { bytesToMegabytes } from '@/helpers'; @@ -6,6 +6,8 @@ import merge from 'deepmerge'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; import { faMemory, faMicrochip } from '@fortawesome/free-solid-svg-icons'; import tw from 'twin.macro'; +import { SocketEvent } from '@/components/server/events'; +import useWebsocketEvent from '@/plugins/useWebsocketEvent'; const chartDefaults = (ticks?: Chart.TickOptions | undefined): ChartConfiguration => ({ type: 'line', @@ -70,7 +72,6 @@ const chartDefaults = (ticks?: Chart.TickOptions | undefined): ChartConfiguratio export default () => { const status = ServerContext.useStoreState(state => state.status.value); const limits = ServerContext.useStoreState(state => state.server.data!.limits); - const { connected, instance } = ServerContext.useStoreState(state => state.socket); const [ memory, setMemory ] = useState(); const [ cpu, setCpu ] = useState(); @@ -84,7 +85,7 @@ export default () => { new Chart(node.getContext('2d')!, chartDefaults({ callback: (value) => `${value}Mb `, suggestedMax: limits.memory, - })) + })), ); }, []); @@ -100,7 +101,7 @@ export default () => { ); }, []); - const statsListener = (data: string) => { + useWebsocketEvent(SocketEvent.STATS, (data: string) => { let stats: any = {}; try { stats = JSON.parse(data); @@ -125,27 +126,19 @@ export default () => { cpu.update({ lazy: true }); } - }; - - useEffect(() => { - if (!connected || !instance) { - return; - } - - instance.addListener('stats', statsListener); - - return () => { - instance.removeListener('stats', statsListener); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ instance, connected, memory, cpu ]); + }); return (
{status !== 'offline' ? - + :

Server is offline. diff --git a/resources/scripts/components/server/TransferListener.tsx b/resources/scripts/components/server/TransferListener.tsx index b46e655bc..07d953eb8 100644 --- a/resources/scripts/components/server/TransferListener.tsx +++ b/resources/scripts/components/server/TransferListener.tsx @@ -1,5 +1,6 @@ import useWebsocketEvent from '@/plugins/useWebsocketEvent'; import { ServerContext } from '@/state/server'; +import { SocketEvent } from '@/components/server/events'; const TransferListener = () => { const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); @@ -7,7 +8,7 @@ const TransferListener = () => { const setServerFromState = ServerContext.useStoreActions(actions => actions.server.setServerFromState); // Listen for the transfer status event so we can update the state of the server. - useWebsocketEvent('transfer status', (status: string) => { + useWebsocketEvent(SocketEvent.TRANSFER_STATUS, (status: string) => { if (status === 'starting') { setServerFromState(s => ({ ...s, isTransferring: true })); return; diff --git a/resources/scripts/components/server/backups/BackupRow.tsx b/resources/scripts/components/server/backups/BackupRow.tsx index a1226176d..6a3518537 100644 --- a/resources/scripts/components/server/backups/BackupRow.tsx +++ b/resources/scripts/components/server/backups/BackupRow.tsx @@ -11,6 +11,7 @@ import tw from 'twin.macro'; import GreyRowBox from '@/components/elements/GreyRowBox'; import getServerBackups from '@/api/swr/getServerBackups'; import { ServerBackup } from '@/api/server/types'; +import { SocketEvent } from '@/components/server/events'; interface Props { backup: ServerBackup; @@ -20,7 +21,7 @@ interface Props { export default ({ backup, className }: Props) => { const { mutate } = getServerBackups(); - useWebsocketEvent(`backup completed:${backup.uuid}`, data => { + useWebsocketEvent(`${SocketEvent.BACKUP_COMPLETED}:${backup.uuid}` as SocketEvent, data => { try { const parsed = JSON.parse(data); diff --git a/resources/scripts/components/server/events.ts b/resources/scripts/components/server/events.ts index 4f4c35bde..36a2c1194 100644 --- a/resources/scripts/components/server/events.ts +++ b/resources/scripts/components/server/events.ts @@ -1,10 +1,20 @@ export enum SocketEvent { DAEMON_MESSAGE = 'daemon message', + DAEMON_ERROR = 'daemon error', INSTALL_OUTPUT = 'install output', INSTALL_STARTED = 'install started', INSTALL_COMPLETED = 'install completed', CONSOLE_OUTPUT = 'console output', STATUS = 'status', STATS = 'stats', + TRANSFER_LOGS = 'transfer logs', + TRANSFER_STATUS = 'transfer status', BACKUP_COMPLETED = 'backup completed', + BACKUP_RESTORE_COMPLETED = 'backup restore completed', +} + +export enum SocketRequest { + SEND_LOGS = 'send logs', + SEND_STATS = 'send stats', + SET_STATE = 'set state' } diff --git a/resources/scripts/components/server/features/eula/EulaModalFeature.tsx b/resources/scripts/components/server/features/eula/EulaModalFeature.tsx index 6453a5097..586b472db 100644 --- a/resources/scripts/components/server/features/eula/EulaModalFeature.tsx +++ b/resources/scripts/components/server/features/eula/EulaModalFeature.tsx @@ -6,6 +6,7 @@ import Button from '@/components/elements/Button'; import saveFileContents from '@/api/server/files/saveFileContents'; import FlashMessageRender from '@/components/FlashMessageRender'; import useFlash from '@/plugins/useFlash'; +import { SocketEvent, SocketRequest } from '@/components/server/events'; const EulaModalFeature = () => { const [ visible, setVisible ] = useState(false); @@ -25,10 +26,10 @@ const EulaModalFeature = () => { } }; - instance.addListener('console output', listener); + instance.addListener(SocketEvent.CONSOLE_OUTPUT, listener); return () => { - instance.removeListener('console output', listener); + instance.removeListener(SocketEvent.CONSOLE_OUTPUT, listener); }; }, [ connected, instance, status ]); @@ -39,7 +40,7 @@ const EulaModalFeature = () => { saveFileContents(uuid, 'eula.txt', 'eula=true') .then(() => { if (status === 'offline' && instance) { - instance.send('set state', 'restart'); + instance.send(SocketRequest.SET_STATE, 'restart'); } setLoading(false); diff --git a/resources/scripts/plugins/useWebsocketEvent.ts b/resources/scripts/plugins/useWebsocketEvent.ts index 09ffeaf43..0e80a9894 100644 --- a/resources/scripts/plugins/useWebsocketEvent.ts +++ b/resources/scripts/plugins/useWebsocketEvent.ts @@ -1,7 +1,8 @@ import { ServerContext } from '@/state/server'; import { useEffect, useRef } from 'react'; +import { SocketEvent } from '@/components/server/events'; -const useWebsocketEvent = (event: string, callback: (data: string) => void) => { +const useWebsocketEvent = (event: SocketEvent, callback: (data: string) => void) => { const { connected, instance } = ServerContext.useStoreState(state => state.socket); const savedCallback = useRef(null); @@ -10,7 +11,7 @@ const useWebsocketEvent = (event: string, callback: (data: string) => void) => { }, [ callback ]); return useEffect(() => { - const eventListener = (event: any) => savedCallback.current(event); + const eventListener = (event: SocketEvent) => savedCallback.current(event); if (connected && instance) { instance.addListener(event, eventListener); }