From 67e42ead784d07e9e55dc7d97f27ea2accd96e22 Mon Sep 17 00:00:00 2001 From: Daniel Scalzi Date: Fri, 4 Sep 2020 22:41:32 -0400 Subject: [PATCH] Show player count on landing page. The server status needs to be stored in the redux store as it needs to be determined before mounting the Landing component. --- package-lock.json | 63 ++++++++----- package.json | 10 +-- .../distribution/DistributionFactory.ts | 21 +++++ src/common/mojang/net/ServerStatusAPI.ts | 32 +++---- src/renderer/components/Application.tsx | 44 +++++---- src/renderer/components/landing/Landing.css | 16 ++++ src/renderer/components/landing/Landing.tsx | 89 +++++++++++++++++-- .../server-select/ServerSelectOverlay.tsx | 4 +- src/renderer/index.tsx | 1 + src/renderer/redux/actions/appActions.ts | 28 ++++-- src/renderer/redux/reducers/appReducer.ts | 18 ++-- 11 files changed, 247 insertions(+), 79 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0e3059..77ebfd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1362,14 +1362,20 @@ } }, "@eslint/eslintrc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.0.tgz", - "integrity": "sha512-bfL5365QSCmH6cPeFT7Ywclj8C7LiF7sO6mUGzZhtAMV7iID1Euq6740u/SRi4C80NOnVz/CEfK8/HO+nCAPJg==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", + "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { @@ -1385,11 +1391,26 @@ "uri-js": "^4.2.2" } }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true } } }, @@ -1577,9 +1598,9 @@ "dev": true }, "@types/node": { - "version": "12.12.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.54.tgz", - "integrity": "sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w==" + "version": "12.12.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.55.tgz", + "integrity": "sha512-Vd6xQUVvPCTm7Nx1N7XHcpX6t047ltm7TgcsOr4gFHjeYgwZevo+V7I1lfzHnj5BT5frztZ42+RTG4MwYw63dw==" }, "@types/prop-types": { "version": "15.7.3", @@ -4722,13 +4743,13 @@ "dev": true }, "eslint": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.0.tgz", - "integrity": "sha512-qgtVyLZqKd2ZXWnLQA4NtVbOyH56zivOAdBFWE54RFkSZjokzNrcP4Z0eVWsZ+84ByXv+jL9k/wE1ENYe8xRFw==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz", + "integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.1.0", + "@eslint/eslintrc": "^0.1.3", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -6072,18 +6093,18 @@ } }, "got": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/got/-/got-11.5.2.tgz", - "integrity": "sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-11.6.0.tgz", + "integrity": "sha512-ErhWb4IUjQzJ3vGs3+RR12NWlBDDkRciFpAkQ1LPUxi6OnwhGj07gQxjPsyIk69s7qMihwKrKquV6VQq7JNYLA==", "requires": { - "@sindresorhus/is": "^3.0.0", + "@sindresorhus/is": "^3.1.1", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.1", "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.0", + "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" @@ -11440,9 +11461,9 @@ } }, "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", "dev": true, "requires": { "arg": "^4.1.0", @@ -11564,9 +11585,9 @@ } }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", "dev": true }, "unicode-canonical-property-names-ecmascript": { diff --git a/package.json b/package.json index 614abf5..1d863b7 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "electron-updater": "^4.3.4", "fs-extra": "^9.0.1", "github-syntax-dark": "^0.5.0", - "got": "^11.5.2", + "got": "^11.6.0", "jquery": "^3.5.1", "lodash": "^4.17.20", "moment": "^2.27.0", @@ -58,7 +58,7 @@ "@types/jquery": "^3.5.1", "@types/lodash": "^4.14.161", "@types/mocha": "^8.0.3", - "@types/node": "^12.12.54", + "@types/node": "^12.12.55", "@types/react": "^16.9.49", "@types/react-dom": "^16.9.8", "@types/react-redux": "^7.1.9", @@ -77,7 +77,7 @@ "electron-devtools-installer": "^3.1.1", "electron-webpack": "^2.8.2", "electron-webpack-ts": "^4.0.1", - "eslint": "^7.8.0", + "eslint": "^7.8.1", "eslint-plugin-react": "^7.20.6", "helios-distribution-types": "1.0.0-pre.1", "mocha": "^8.1.3", @@ -89,9 +89,9 @@ "react-transition-group": "^4.4.1", "redux": "^4.0.5", "rimraf": "^3.0.2", - "ts-node": "^8.10.2", + "ts-node": "^9.0.0", "tsconfig-paths": "^3.9.0", - "typescript": "^3.9.7", + "typescript": "^4.0.2", "webpack": "^4.44.1" }, "repository": { diff --git a/src/common/distribution/DistributionFactory.ts b/src/common/distribution/DistributionFactory.ts index 0e69d62..91348f3 100644 --- a/src/common/distribution/DistributionFactory.ts +++ b/src/common/distribution/DistributionFactory.ts @@ -53,13 +53,34 @@ export class HeliosDistribution { export class HeliosServer { public readonly modules: HeliosModule[] + public readonly hostname: string + public readonly port: number constructor( public readonly rawServer: Server ) { + const { hostname, port } = this.parseAddress() + this.hostname = hostname + this.port = port this.modules = rawServer.modules.map(m => new HeliosModule(m, rawServer.id)) } + private parseAddress(): { hostname: string, port: number } { + // Srv record lookup here if needed. + if(this.rawServer.address.includes(':')) { + const pieces = this.rawServer.address.split(':') + const port = Number(pieces[1]) + + if(!Number.isInteger(port)) { + throw new Error(`Malformed server address for ${this.rawServer.id}. Port must be an integer!`) + } + + return { hostname: pieces[0], port } + } else { + return { hostname: this.rawServer.address, port: 25565 } + } + } + } export class HeliosModule { diff --git a/src/common/mojang/net/ServerStatusAPI.ts b/src/common/mojang/net/ServerStatusAPI.ts index abbc3f7..18a955d 100644 --- a/src/common/mojang/net/ServerStatusAPI.ts +++ b/src/common/mojang/net/ServerStatusAPI.ts @@ -35,17 +35,17 @@ export interface ServerStatus { * Get the handshake packet. * * @param protocol The client's protocol version. - * @param address The server address. + * @param hostname The server hostname. * @param port The server port. * * @see https://wiki.vg/Server_List_Ping#Handshake */ -function getHandshakePacket(protocol: number, address: string, port: number): Buffer { +function getHandshakePacket(protocol: number, hostname: string, port: number): Buffer { return ServerBoundPacket.build() .writeVarInt(0x00) // Packet Id .writeVarInt(protocol) - .writeString(address) + .writeString(hostname) .writeUnsignedShort(port) .writeVarInt(1) // State, 1 = status .toBuffer() @@ -80,19 +80,19 @@ function unifyStatusResponse(resp: ServerStatus): ServerStatus { return resp } -export function getServerStatus(protocol: number, address: string, port = 25565): Promise { +export function getServerStatus(protocol: number, hostname: string, port = 25565): Promise { return new Promise((resolve, reject) => { - const socket = connect(port, address, () => { - socket.write(getHandshakePacket(protocol, address, port)) + const socket = connect(port, hostname, () => { + socket.write(getHandshakePacket(protocol, hostname, port)) socket.write(getRequestPacket()) }) socket.setTimeout(5000, () => { socket.destroy() - logger.error(`Server Status Socket timed out (${address}:${port})`) - reject(new Error(`Server Status Socket timed out (${address}:${port})`)) + logger.error(`Server Status Socket timed out (${hostname}:${port})`) + reject(new Error(`Server Status Socket timed out (${hostname}:${port})`)) }) const maxTries = 2 @@ -122,7 +122,7 @@ export function getServerStatus(protocol: number, address: string, port = 25565) if(iterations > maxTries) { socket.destroy() - reject(new Error(`Data read from ${address}:${port} exceeded ${maxTries} iterations, closing connection.`)) + reject(new Error(`Data read from ${hostname}:${port} exceeded ${maxTries} iterations, closing connection.`)) return } ++iterations @@ -141,7 +141,7 @@ export function getServerStatus(protocol: number, address: string, port = 25565) const result = inboundPacket.readString() try { - const parsed = JSON.parse(result) + const parsed: ServerStatus = JSON.parse(result) socket.end() resolve(unifyStatusResponse(parsed)) } catch(err) { @@ -164,17 +164,17 @@ export function getServerStatus(protocol: number, address: string, port = 25565) if(err.code === 'ENOTFOUND') { // ENOTFOUND = Unable to resolve. - logger.error(`Server ${address}:${port} not found!`) - resolve(null) + logger.error(`Server ${hostname}:${port} not found!`) + resolve(undefined) return } else if(err.code === 'ECONNREFUSED') { // ECONNREFUSED = Unable to connect to port. - logger.error(`Server ${address}:${port} refused to connect, is the port correct?`) - resolve(null) + logger.error(`Server ${hostname}:${port} refused to connect, is the port correct?`) + resolve(undefined) return } else { - logger.error(`Error trying to pull server status (${address}:${port})`, err) - resolve(null) + logger.error(`Error trying to pull server status (${hostname}:${port})`, err) + resolve(undefined) return } }) diff --git a/src/renderer/components/Application.tsx b/src/renderer/components/Application.tsx index d3a37b3..c5c84fb 100644 --- a/src/renderer/components/Application.tsx +++ b/src/renderer/components/Application.tsx @@ -21,7 +21,7 @@ import { OverlayPushAction, OverlayActionDispatch } from '../redux/actions/overl import { LoggerUtil } from 'common/logging/loggerutil' import { DistributionAPI } from 'common/distribution/DistributionAPI' -import { getServerStatus } from 'common/mojang/net/ServerStatusAPI' +import { getServerStatus, ServerStatus } from 'common/mojang/net/ServerStatusAPI' import { Distribution } from 'helios-distribution-types' import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory' @@ -40,6 +40,7 @@ interface ApplicationProps { overlayQueue: OverlayPushAction[] distribution: HeliosDistribution selectedServer: HeliosServer + selectedServerStatus: ServerStatus } interface ApplicationState { @@ -79,7 +80,7 @@ class Application extends React.Component { // TODO debug remove console.log('loading', this.props.currentView, this.state.workingView) switch(this.state.workingView) { @@ -89,7 +90,11 @@ class Application extends React.Component case View.LANDING: return <> - + case View.LOGIN: return <> @@ -164,10 +169,19 @@ class Application extends React.Component { - //this.props.setView(View.WELCOME) - this.props.pushGenericOverlay({ - title: 'Load Distribution', - description: 'This is a test.', - dismissible: false, - acknowledgeCallback: async () => { - const serverStatus = await getServerStatus(47, 'play.hypixel.net', 25565) - console.log(serverStatus) - } - }) + // this.props.setView(View.WELCOME) + // this.props.pushGenericOverlay({ + // title: 'Load Distribution', + // description: 'This is a test.', + // dismissible: false, + // acknowledgeCallback: async () => { + // const serverStatus = await getServerStatus(47, 'play.hypixel.net', 25565) + // console.log(serverStatus) + // } + // }) // this.props.pushGenericOverlay({ // title: 'Test Title 2', // description: 'Test Description', diff --git a/src/renderer/components/landing/Landing.css b/src/renderer/components/landing/Landing.css index 578870f..fc4f38d 100644 --- a/src/renderer/components/landing/Landing.css +++ b/src/renderer/components/landing/Landing.css @@ -1,3 +1,19 @@ +.serverStatusWrapper-enter { + opacity: 0; +} +.serverStatusWrapper-enter-active { + opacity: 1; + transition: opacity 500ms, transform 500ms; +} +.serverStatusWrapper-exit { + opacity: 1; +} +.serverStatusWrapper-exit-active { + opacity: 0; + transition: opacity 500ms, transform 500ms; +} + + /******************************************************************************* * * * Landing View (Structural Styles) * diff --git a/src/renderer/components/landing/Landing.tsx b/src/renderer/components/landing/Landing.tsx index 93661bc..0aeb889 100644 --- a/src/renderer/components/landing/Landing.tsx +++ b/src/renderer/components/landing/Landing.tsx @@ -1,10 +1,12 @@ import * as React from 'react' import { connect } from 'react-redux' +import { CSSTransition } from 'react-transition-group' import { StoreType } from '../../redux/store' import { AppActionDispatch } from '../..//redux/actions/appActions' import { OverlayActionDispatch } from '../../redux/actions/overlayActions' import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory' +import { ServerStatus, getServerStatus } from 'common/mojang/net/ServerStatusAPI' import { MojangStatus, MojangStatusColor } from 'common/mojang/rest/internal/MojangStatus' import { MojangResponse } from 'common/mojang/rest/internal/MojangResponse' import { MojangRestAPI } from 'common/mojang/rest/MojangRestAPI' @@ -18,16 +20,19 @@ import './Landing.css' interface LandingProps { distribution: HeliosDistribution selectedServer: HeliosServer + selectedServerStatus: ServerStatus } interface LandingState { mojangStatuses: MojangStatus[] + outdatedServerStatus: boolean } const mapState = (state: StoreType): Partial => { return { distribution: state.app.distribution!, - selectedServer: state.app.selectedServer! + selectedServer: state.app.selectedServer!, + selectedServerStatus: state.app.selectedServerStatus! } } const mapDispatch = { @@ -42,11 +47,13 @@ class Landing extends React.Component { private static readonly logger = LoggerUtil.getLogger('LandingTSX') private mojangStatusInterval!: NodeJS.Timeout + private serverStatusInterval!: NodeJS.Timeout constructor(props: InternalLandingProps) { super(props) this.state = { - mojangStatuses: [] + mojangStatuses: [], + outdatedServerStatus: false } } @@ -60,12 +67,21 @@ class Landing extends React.Component { await this.loadMojangStatuses() }, 300000) + this.serverStatusInterval = setInterval(async () => { + Landing.logger.info('Refreshing selected server status..') + this.setState({ + ...this.state, + outdatedServerStatus: true + }) + }, 300000) + } componentWillUnmount(): void { // Clean up intervals. clearInterval(this.mojangStatusInterval) + clearInterval(this.serverStatusInterval) } @@ -92,6 +108,34 @@ class Landing extends React.Component { } + private syncServerStatus = async (): Promise => { + let serverStatus: ServerStatus | undefined + + if(this.props.selectedServer != null) { + const { hostname, port } = this.props.selectedServer + try { + serverStatus = await getServerStatus( + 47, + hostname, + port + ) + } catch(err) { + Landing.logger.error('Error while refreshing server status', err) + } + + } else { + serverStatus = undefined + } + + this.props.setSelectedServerStatus(serverStatus) + } + private finishServerSync = async (): Promise => { + this.setState({ + ...this.state, + outdatedServerStatus: false + }) + } + private getMainMojangStatusColor = (): string => { const essential = this.state.mojangStatuses.filter(s => s.essential) @@ -137,9 +181,14 @@ class Landing extends React.Component { this.props.pushServerSelectOverlay({ servers: this.props.distribution.servers, selectedId: this.props.selectedServer.rawServer.id, - onSelection: (serverId: string) => { + onSelection: async (serverId: string) => { Landing.logger.info('Server Selection Change:', serverId) - this.props.setSelectedServer(this.props.distribution.getServerById(serverId)!) + const next: HeliosServer = this.props.distribution.getServerById(serverId)! + this.props.setSelectedServer(next) + this.setState({ + ...this.state, + outdatedServerStatus: true + }) } }) } @@ -152,6 +201,19 @@ class Landing extends React.Component { } } + private getSelectedServerStatusText = (): string => { + return this.props.selectedServerStatus != null ? 'PLAYERS' : 'SERVER' + } + + private getSelectedServerCount = (): string => { + if(this.props.selectedServerStatus != null) { + const { online, max } = this.props.selectedServerStatus.players + return `${online}/${max}` + } else { + return 'OFFLINE' + } + } + render(): JSX.Element { return <> @@ -252,10 +314,21 @@ class Landing extends React.Component {
-
- SERVER - OFFLINE -
+ + +
+ {this.getSelectedServerStatusText()} + {this.getSelectedServerCount()} +
+
+
MOJANG STATUS diff --git a/src/renderer/components/overlay/server-select/ServerSelectOverlay.tsx b/src/renderer/components/overlay/server-select/ServerSelectOverlay.tsx index f2c9e58..bfaa723 100644 --- a/src/renderer/components/overlay/server-select/ServerSelectOverlay.tsx +++ b/src/renderer/components/overlay/server-select/ServerSelectOverlay.tsx @@ -10,7 +10,7 @@ import '../shared-select/SharedSelect.css' export interface ServerSelectOverlayProps { servers: HeliosServer[] selectedId: string - onSelection: (serverId: string) => void + onSelection: (serverId: string) => Promise } interface ServerSelectOverlayState { @@ -36,7 +36,7 @@ class ServerSelectOverlay extends React.Component => { try { - this.props.onSelection(this.state.selectedId) + await this.props.onSelection(this.state.selectedId) } catch(err) { this.logger.error('Uncaught error in server select confirmation.', err) } diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 3ae66a6..262f785 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -30,6 +30,7 @@ ReactDOM.render( overlayQueue={store.getState().overlayQueue} distribution={store.getState().app.distribution!} selectedServer={store.getState().app.selectedServer!} + selectedServerStatus={store.getState().app.selectedServerStatus!} /> , diff --git a/src/renderer/redux/actions/appActions.ts b/src/renderer/redux/actions/appActions.ts index a4e3aa4..a8459da 100644 --- a/src/renderer/redux/actions/appActions.ts +++ b/src/renderer/redux/actions/appActions.ts @@ -1,37 +1,51 @@ import { Action } from 'redux' import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory' +import { ServerStatus } from 'common/mojang/net/ServerStatusAPI' export enum AppActionType { SetDistribution = 'SET_DISTRIBUTION', - SetSelectedServer = 'SET_SELECTED_SERVER' + SetSelectedServer = 'SET_SELECTED_SERVER', + SetSelectedServerStatus = 'SET_SELECTED_SERVER_STATUS' } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface AppAction extends Action {} export interface SetDistributionAction extends AppAction { - payload: HeliosDistribution + payload?: HeliosDistribution } export interface SetSelectedServerAction extends AppAction { - payload: HeliosServer + payload?: HeliosServer } -export function setDistribution(distribution: HeliosDistribution): SetDistributionAction { +export interface SetSelectedServerStatusAction extends AppAction { + payload?: ServerStatus +} + +export function setDistribution(distribution?: HeliosDistribution): SetDistributionAction { return { type: AppActionType.SetDistribution, payload: distribution } } -export function setSelectedServer(server: HeliosServer): SetSelectedServerAction { +export function setSelectedServer(server?: HeliosServer): SetSelectedServerAction { return { type: AppActionType.SetSelectedServer, payload: server } } +export function setSelectedServerStatus(serverStatus?: ServerStatus): SetSelectedServerStatusAction { + return { + type: AppActionType.SetSelectedServerStatus, + payload: serverStatus + } +} + export const AppActionDispatch = { - setDistribution: (d: HeliosDistribution): SetDistributionAction => setDistribution(d), - setSelectedServer: (s: HeliosServer): SetSelectedServerAction => setSelectedServer(s) + setDistribution: (d?: HeliosDistribution): SetDistributionAction => setDistribution(d), + setSelectedServer: (s?: HeliosServer): SetSelectedServerAction => setSelectedServer(s), + setSelectedServerStatus: (ss?: ServerStatus): SetSelectedServerStatusAction => setSelectedServerStatus(ss) } \ No newline at end of file diff --git a/src/renderer/redux/reducers/appReducer.ts b/src/renderer/redux/reducers/appReducer.ts index 72aa8b1..aec37c6 100644 --- a/src/renderer/redux/reducers/appReducer.ts +++ b/src/renderer/redux/reducers/appReducer.ts @@ -1,15 +1,18 @@ -import { AppActionType, AppAction, SetDistributionAction, SetSelectedServerAction } from '../actions/appActions' +import { AppActionType, AppAction, SetDistributionAction, SetSelectedServerAction, SetSelectedServerStatusAction } from '../actions/appActions' import { Reducer } from 'redux' import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory' +import { ServerStatus } from 'common/mojang/net/ServerStatusAPI' export interface AppState { - distribution: HeliosDistribution | null - selectedServer: HeliosServer | null + distribution?: HeliosDistribution + selectedServer?: HeliosServer + selectedServerStatus?: ServerStatus } const defaultAppState: AppState = { - distribution: null, - selectedServer: null + distribution: undefined, + selectedServer: undefined, + selectedServerStatus: undefined } const AppReducer: Reducer = (state = defaultAppState, action) => { @@ -24,6 +27,11 @@ const AppReducer: Reducer = (state = defaultAppState, actio ...state, selectedServer: (action as SetSelectedServerAction).payload } + case AppActionType.SetSelectedServerStatus: + return { + ...state, + selectedServerStatus: (action as SetSelectedServerStatusAction).payload + } } return state }