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.
This commit is contained in:
Daniel Scalzi 2020-09-04 22:41:32 -04:00
parent cb68c34abe
commit 67e42ead78
No known key found for this signature in database
GPG Key ID: D18EA3FB4B142A57
11 changed files with 247 additions and 79 deletions

63
package-lock.json generated
View File

@ -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": {

View File

@ -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": {

View File

@ -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 {

View File

@ -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<ServerStatus | null> {
export function getServerStatus(protocol: number, hostname: string, port = 25565): Promise<ServerStatus | undefined> {
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
}
})

View File

@ -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<unknown>[]
distribution: HeliosDistribution
selectedServer: HeliosServer
selectedServerStatus: ServerStatus
}
interface ApplicationState {
@ -79,7 +80,7 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
}
}
getViewElement(): JSX.Element {
private getViewElement = (): JSX.Element => {
// 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<ApplicationProps & typeof mapDispatch,
</>
case View.LANDING:
return <>
<Landing distribution={this.props.distribution} selectedServer={this.props.selectedServer} />
<Landing
distribution={this.props.distribution}
selectedServer={this.props.selectedServer}
selectedServerStatus={this.props.selectedServerStatus}
/>
</>
case View.LOGIN:
return <>
@ -164,10 +169,19 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
return
} else {
const distro = new HeliosDistribution(rawDisto)
this.props.setDistribution(distro)
// TODO TEMP USE CONFIG
// TODO TODO TODO TODO
this.props.setSelectedServer(distro.servers[0])
const selectedServer: HeliosServer = distro.servers[0]
const { hostname, port } = selectedServer
let selectedServerStatus
try {
selectedServerStatus = await getServerStatus(47, hostname, port)
} catch(err) {
Application.logger.error('Failed to refresh server status', selectedServerStatus)
}
this.props.setDistribution(distro)
this.props.setSelectedServer(selectedServer)
this.props.setSelectedServerStatus(selectedServerStatus)
}
// TODO Setup hook for distro refresh every ~ 5 mins.
@ -187,15 +201,15 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
// TODO temp
setTimeout(() => {
// 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: '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',

View File

@ -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) *

View File

@ -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<LandingProps> => {
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<InternalLandingProps, LandingState> {
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<InternalLandingProps, LandingState> {
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<InternalLandingProps, LandingState> {
}
private syncServerStatus = async (): Promise<void> => {
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<void> => {
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<InternalLandingProps, LandingState> {
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<InternalLandingProps, LandingState> {
}
}
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<InternalLandingProps, LandingState> {
<div id="left">
<div className="bot_wrapper">
<div id="content">
<CSSTransition
in={!this.state.outdatedServerStatus}
timeout={500}
classNames="serverStatusWrapper"
unmountOnExit
onEnter={this.syncServerStatus}
onExited={this.finishServerSync}
>
<div id="server_status_wrapper">
<span className="bot_label" id="landingPlayerLabel">SERVER</span>
<span id="player_count">OFFLINE</span>
<span className="bot_label" id="landingPlayerLabel">{this.getSelectedServerStatusText()}</span>
<span id="player_count">{this.getSelectedServerCount()}</span>
</div>
</CSSTransition>
<div className="bot_divider"></div>
<div id="mojangStatusWrapper">
<span className="bot_label">MOJANG STATUS</span>

View File

@ -10,7 +10,7 @@ import '../shared-select/SharedSelect.css'
export interface ServerSelectOverlayProps {
servers: HeliosServer[]
selectedId: string
onSelection: (serverId: string) => void
onSelection: (serverId: string) => Promise<void>
}
interface ServerSelectOverlayState {
@ -36,7 +36,7 @@ class ServerSelectOverlay extends React.Component<InternalServerSelectOverlayPro
private onSelectClick = async (): Promise<void> => {
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)
}

View File

@ -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!}
/>
</Provider>
</AppContainer>,

View File

@ -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 const AppActionDispatch = {
setDistribution: (d: HeliosDistribution): SetDistributionAction => setDistribution(d),
setSelectedServer: (s: HeliosServer): SetSelectedServerAction => setSelectedServer(s)
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),
setSelectedServerStatus: (ss?: ServerStatus): SetSelectedServerStatusAction => setSelectedServerStatus(ss)
}

View File

@ -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<AppState, AppAction> = (state = defaultAppState, action) => {
@ -24,6 +27,11 @@ const AppReducer: Reducer<AppState, AppAction> = (state = defaultAppState, actio
...state,
selectedServer: (action as SetSelectedServerAction).payload
}
case AppActionType.SetSelectedServerStatus:
return {
...state,
selectedServerStatus: (action as SetSelectedServerStatusAction).payload
}
}
return state
}