New guests can be added, returning players recognised, and games are saved properly, locking the board. Changed webpack config to compile typescript with babel.
This commit is contained in:
7
.babelrc
7
.babelrc
@@ -1,3 +1,8 @@
|
||||
{
|
||||
"presets": ["@babel/env", "@babel/preset-react"]
|
||||
"presets": [
|
||||
"@babel/typescript",
|
||||
"@babel/react",
|
||||
"@babel/env"
|
||||
],
|
||||
"plugins": ["@babel/proposal-class-properties"]
|
||||
}
|
||||
|
||||
68
package-lock.json
generated
68
package-lock.json
generated
@@ -147,6 +147,20 @@
|
||||
"semver": "^5.5.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.6.tgz",
|
||||
"integrity": "sha512-6N9IeuyHvMBRyjNYOMJHrhwtu4WJMrYf8hVbEHD3pbbbmNOk1kmXSQs7bA4dYDUaIx4ZEzdnvo6NwC3WHd/Qow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-function-name": "^7.9.5",
|
||||
"@babel/helper-member-expression-to-functions": "^7.8.3",
|
||||
"@babel/helper-optimise-call-expression": "^7.8.3",
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/helper-replace-supers": "^7.9.6",
|
||||
"@babel/helper-split-export-declaration": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/helper-create-regexp-features-plugin": {
|
||||
"version": "7.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz",
|
||||
@@ -366,6 +380,16 @@
|
||||
"@babel/plugin-syntax-async-generators": "^7.8.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-class-properties": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz",
|
||||
"integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.8.3",
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-proposal-dynamic-import": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz",
|
||||
@@ -537,6 +561,15 @@
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-syntax-typescript": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz",
|
||||
"integrity": "sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-arrow-functions": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz",
|
||||
@@ -885,6 +918,17 @@
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-typescript": {
|
||||
"version": "7.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.6.tgz",
|
||||
"integrity": "sha512-8OvsRdvpt3Iesf2qsAn+YdlwAJD7zJ+vhFZmDCa4b8dTp7MmHtKk5FF2mCsGxjZwuwsy/yIIay/nLmxST1ctVQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.9.6",
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-syntax-typescript": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-unicode-regex": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz",
|
||||
@@ -895,6 +939,15 @@
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
}
|
||||
},
|
||||
"@babel/polyfill": {
|
||||
"version": "7.8.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.8.7.tgz",
|
||||
"integrity": "sha512-LeSfP9bNZH2UOZgcGcZ0PIHUt1ZuHub1L3CVmEyqLxCeDLm4C5Gi8jRH8ZX2PNpDhQCo0z6y/+DIs2JlliXW8w==",
|
||||
"requires": {
|
||||
"core-js": "^2.6.5",
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"@babel/preset-env": {
|
||||
"version": "7.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.6.tgz",
|
||||
@@ -990,6 +1043,16 @@
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.9.0"
|
||||
}
|
||||
},
|
||||
"@babel/preset-typescript": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz",
|
||||
"integrity": "sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3",
|
||||
"@babel/plugin-transform-typescript": "^7.9.0"
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz",
|
||||
@@ -2263,6 +2326,11 @@
|
||||
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
|
||||
"dev": true
|
||||
},
|
||||
"core-js": {
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
|
||||
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz",
|
||||
|
||||
12
package.json
12
package.json
@@ -2,11 +2,12 @@
|
||||
"name": "kadi-board",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"homepage": "/kadi/game/",
|
||||
"homepage": "/kadi/game",
|
||||
"serverRoot": "/kadi",
|
||||
"license": "ISC",
|
||||
"author": "Daniel Ledda",
|
||||
"scripts": {
|
||||
"build-dev": "webpack --mode development && postbuild",
|
||||
"build-dev": "webpack --mode development && npm postbuild",
|
||||
"build": "webpack --mode production",
|
||||
"postbuild": "rsync -avu --delete dist/ ../kadi_backend/static/game",
|
||||
"start": "webpack-dev-server --mode development",
|
||||
@@ -15,17 +16,20 @@
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/polyfill": "^7.8.7",
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
"@babel/preset-env": "^7.9.6",
|
||||
"@babel/preset-react": "^7.9.4",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@types/node": "^13.11.1",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.7",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"font-loader": "^0.1.2",
|
||||
"ignore-loader": "^0.1.2",
|
||||
"babel-loader": "^8.1.0",
|
||||
"css-loader": "^3.5.3",
|
||||
"file-loader": "^6.0.0",
|
||||
"font-loader": "^0.1.2",
|
||||
"ignore-loader": "^0.1.2",
|
||||
"react-hot-loader": "^4.12.21",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^7.0.3",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="static/favicon.ico" />
|
||||
<link rel="icon" href="static/game/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
@@ -17,6 +17,6 @@
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<script src="static/bundle.js"></script>
|
||||
<script src="static/game/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,11 +3,19 @@ import "../static/css/game.css";
|
||||
import KadiBoard from "./KadiBoard";
|
||||
import GameSetup, {GameSettings} from "./GameSetup"
|
||||
import Settings from "../static/settings.json";
|
||||
import {SupportedLang} from "../static/enums";
|
||||
import {SERVER_BASE_NAME} from "../index";
|
||||
import axios from "axios";
|
||||
|
||||
export interface Player {
|
||||
id: string;
|
||||
nick: string;
|
||||
}
|
||||
|
||||
interface GameState {
|
||||
currentSettings: GameSettings;
|
||||
settingUp: boolean;
|
||||
loading: boolean;
|
||||
availablePlayers: Player[]
|
||||
}
|
||||
|
||||
interface GameProps {}
|
||||
@@ -19,16 +27,36 @@ class Game extends React.Component<GameProps, GameState> {
|
||||
super(props);
|
||||
|
||||
const startupSettings: GameSettings = {
|
||||
playerIds: Settings.players,
|
||||
players: [],
|
||||
ruleset: Settings.ruleset,
|
||||
};
|
||||
|
||||
this.state = {
|
||||
currentSettings: startupSettings,
|
||||
settingUp: true,
|
||||
loading: true,
|
||||
availablePlayers: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
axios.get(SERVER_BASE_NAME + "/api/players")
|
||||
.then(response => this.addNewPlayers([...response.data.guests, response.data.mainPlayer]))
|
||||
.catch(error => this.handleError(error))
|
||||
.finally(() => this.setState({ loading: false }));
|
||||
console.log(this.state.availablePlayers);
|
||||
}
|
||||
|
||||
addNewPlayers(players: Player[]) {
|
||||
const availablePlayers: Player[] = this.state.availablePlayers;
|
||||
availablePlayers.push(...players);
|
||||
this.setState({availablePlayers});
|
||||
}
|
||||
|
||||
handleError(error: any): void {
|
||||
|
||||
};
|
||||
|
||||
onSetupComplete: (gameSettings: GameSettings) => void = (gameSettings) => {
|
||||
this.setState({
|
||||
currentSettings: gameSettings,
|
||||
@@ -43,11 +71,12 @@ class Game extends React.Component<GameProps, GameState> {
|
||||
render(): ReactNode {
|
||||
return (
|
||||
<>
|
||||
{this.state.settingUp ?
|
||||
{!this.state.loading && (this.state.settingUp ?
|
||||
(
|
||||
<GameSetup
|
||||
onSetupComplete={this.onSetupComplete}
|
||||
settings={this.state.currentSettings}
|
||||
availablePlayers={this.state.availablePlayers}
|
||||
/>
|
||||
) :
|
||||
(
|
||||
@@ -56,7 +85,7 @@ class Game extends React.Component<GameProps, GameState> {
|
||||
returnToSetup={this.returnToSetup}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
import React, {ChangeEvent, FocusEvent, KeyboardEvent, ReactNode} from "react";
|
||||
import {getSchemaListings, SchemaListing} from "../static/rulesets";
|
||||
import {LanguageNames} from "../static/strings";
|
||||
import LocaleContext from "../LocaleContext";
|
||||
import {SupportedLang} from "../static/enums";
|
||||
import {Button} from "semantic-ui-react";
|
||||
import {Player} from "./Game";
|
||||
|
||||
interface GameSetupProps {
|
||||
onSetupComplete: (settings: GameSettings) => void;
|
||||
settings: GameSettings;
|
||||
availablePlayers: Player[];
|
||||
}
|
||||
|
||||
interface GameSetupState {
|
||||
selectedRuleset: string;
|
||||
enteredPlayerIds: string[];
|
||||
enteredPlayers: Player[];
|
||||
editingPlayerName: boolean;
|
||||
}
|
||||
|
||||
export interface GameSettings {
|
||||
ruleset: string;
|
||||
playerIds: string[];
|
||||
players: Player[];
|
||||
}
|
||||
|
||||
class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
|
||||
private readonly availableRulesets: SchemaListing[];
|
||||
private changeLang: (lang: string) => void;
|
||||
state: GameSetupState;
|
||||
|
||||
constructor(props: GameSetupProps) {
|
||||
super(props);
|
||||
|
||||
@@ -33,7 +32,7 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
|
||||
this.changeLang = () => {};
|
||||
this.state = {
|
||||
selectedRuleset: this.props.settings.ruleset,
|
||||
enteredPlayerIds: this.props.settings.playerIds,
|
||||
enteredPlayers: this.props.settings.players,
|
||||
editingPlayerName: false,
|
||||
};
|
||||
}
|
||||
@@ -43,23 +42,38 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
|
||||
};
|
||||
|
||||
removePlayer: (index: number) => void = (index) => {
|
||||
const newPlayers = this.state.enteredPlayerIds.slice();
|
||||
const newPlayers = this.state.enteredPlayers.slice();
|
||||
newPlayers.splice(index, 1);
|
||||
this.setState({enteredPlayerIds: newPlayers});
|
||||
this.setState({enteredPlayers: newPlayers});
|
||||
};
|
||||
|
||||
addPlayer: (playerSubmission: string, keepEditing: boolean) => void = (playerSubmission, keepEditing) => {
|
||||
const newPlayers = this.state.enteredPlayerIds.slice();
|
||||
if (!newPlayers.find(enteredPlayer => enteredPlayer == playerSubmission)) {
|
||||
newPlayers.push(playerSubmission);
|
||||
const newPlayers = this.state.enteredPlayers.slice();
|
||||
if (!this.alreadyPlaying(playerSubmission)) {
|
||||
newPlayers.push({
|
||||
id: this.playerNameToId(playerSubmission) ?? playerSubmission,
|
||||
nick: playerSubmission
|
||||
});
|
||||
}
|
||||
this.setState({enteredPlayerIds: newPlayers, editingPlayerName: keepEditing});
|
||||
this.setState({enteredPlayers: newPlayers, editingPlayerName: keepEditing});
|
||||
};
|
||||
|
||||
alreadyPlaying(playerName: string): boolean {
|
||||
return !!this.state.enteredPlayers.find(player => player.nick === playerName);
|
||||
}
|
||||
|
||||
playerNameToId(playerName: string): string | undefined {
|
||||
return this.props.availablePlayers.find(player => player.nick === playerName)?.id;
|
||||
}
|
||||
|
||||
playerIsNew(player: Player) {
|
||||
return player.id === player.nick;
|
||||
}
|
||||
|
||||
submitSettings: () => void = () => {
|
||||
this.props.onSetupComplete({
|
||||
ruleset: this.state.selectedRuleset,
|
||||
playerIds: this.state.enteredPlayerIds,
|
||||
players: this.state.enteredPlayers,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -85,19 +99,14 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
|
||||
}
|
||||
|
||||
const playerListing: ReactNode[] = [];
|
||||
for (let i = 0; i < this.state.enteredPlayerIds.length; i++) {
|
||||
const playerName = this.state.enteredPlayerIds[i];
|
||||
for (let i = 0; i < this.state.enteredPlayers.length; i++) {
|
||||
playerListing.push((
|
||||
<div
|
||||
key={playerName + "_list"}
|
||||
className={"option playerOption"}
|
||||
>
|
||||
{playerName}
|
||||
<span
|
||||
className={"trashButton"}
|
||||
onClick={() => this.removePlayer(i)}
|
||||
<ActivePlayerListItem
|
||||
key={i}
|
||||
playerIsNew={this.playerIsNew(this.state.enteredPlayers[i])}
|
||||
removePlayer={() => this.removePlayer(i)}
|
||||
playerName={this.state.enteredPlayers[i].nick}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
playerListing.push((
|
||||
@@ -105,6 +114,7 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
|
||||
playersListEmpty={playerListing.length === 0}
|
||||
submitNewPlayer={this.addPlayer}
|
||||
userEditing={this.state.editingPlayerName}
|
||||
availablePlayers={this.props.availablePlayers}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -137,7 +147,7 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
|
||||
size={"huge"}
|
||||
color={"blue"}
|
||||
onClick={this.submitSettings}
|
||||
disabled={this.state.enteredPlayerIds.length < 1}
|
||||
disabled={this.state.enteredPlayers.length < 1}
|
||||
>
|
||||
{Locale.setupScreen.startGame}
|
||||
</Button>
|
||||
@@ -150,7 +160,15 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
|
||||
}
|
||||
GameSetup.contextType = LocaleContext;
|
||||
|
||||
const AddPlayerField: React.FunctionComponent<AddPlayerFieldProps> = ({playersListEmpty, submitNewPlayer, userEditing}) => {
|
||||
interface AddPlayerFieldProps {
|
||||
playersListEmpty: boolean;
|
||||
submitNewPlayer: (name: string, keepEditing: boolean) => void;
|
||||
userEditing: boolean;
|
||||
availablePlayers: Player[];
|
||||
}
|
||||
|
||||
const AddPlayerField: React.FunctionComponent<AddPlayerFieldProps> = (props) => {
|
||||
const {playersListEmpty, submitNewPlayer, userEditing, availablePlayers} = props;
|
||||
const Locale = React.useContext(LocaleContext).strings;
|
||||
|
||||
const [beingEdited, updateBeingEdited] = React.useState(false);
|
||||
@@ -205,10 +223,35 @@ const AddPlayerField: React.FunctionComponent<AddPlayerFieldProps> = ({playersLi
|
||||
);
|
||||
};
|
||||
|
||||
interface AddPlayerFieldProps {
|
||||
playersListEmpty: boolean;
|
||||
submitNewPlayer: (name: string, keepEditing: boolean) => void;
|
||||
userEditing: boolean;
|
||||
interface ActivePlayerListItemProps {
|
||||
removePlayer: () => any;
|
||||
playerName: string;
|
||||
playerIsNew: boolean;
|
||||
}
|
||||
|
||||
const ActivePlayerListItem: React.FunctionComponent<ActivePlayerListItemProps> = (props) => {
|
||||
const {removePlayer, playerName, playerIsNew} = props;
|
||||
const Locale = React.useContext(LocaleContext).strings;
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={"option playerOption"}
|
||||
>
|
||||
<div className={"playerText"}>
|
||||
{playerName}
|
||||
{playerIsNew &&
|
||||
<span className={"newPlayerText"}>
|
||||
{Locale.setupScreen.playerNew}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
className={"trashButton"}
|
||||
onClick={removePlayer}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GameSetup;
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, {ReactElement, ReactNode, useContext} from "react";
|
||||
import PlayerScoreCard, {CellLocation, PlayerScoreCardJSONRepresentation} from "../Classes/PlayerScoreCard";
|
||||
import {BlockDef, GameSchema, getGameSchemaById} from "../static/rulesets";
|
||||
import {formatUnicorn} from "../static/strings";
|
||||
import {GameSchema, getGameSchemaById} from "../static/rulesets";
|
||||
import LocaleContext from "../LocaleContext";
|
||||
import {CellFlag} from "../static/enums";
|
||||
import {ScoreCellValue} from "../Classes/ScoreCell";
|
||||
@@ -14,6 +13,8 @@ import logo from "../static/images/kadi.png";
|
||||
import KadiGrandTotalRow from "./KadiGrandTotalRow";
|
||||
import KadiBlockRenderer from "./KadiBlockRenderer";
|
||||
import {KadiCellDisplayValue} from "./KadiCell";
|
||||
import {Player} from "./Game";
|
||||
import {SERVER_BASE_NAME} from "../index";
|
||||
|
||||
|
||||
export interface CellScores {
|
||||
@@ -40,9 +41,11 @@ export interface KadiBoardProps {
|
||||
|
||||
interface KadiBoardState {
|
||||
scoreSheet: ScoreSheet;
|
||||
playerIds: string[];
|
||||
players: Player[];
|
||||
showResults: boolean;
|
||||
savingGame: boolean;
|
||||
locked: boolean;
|
||||
saved: boolean;
|
||||
}
|
||||
|
||||
interface ScoreSheet {
|
||||
@@ -60,33 +63,37 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
this.gameSchema = getGameSchemaById(this.props.settings.ruleset);
|
||||
|
||||
this.state = {
|
||||
scoreSheet: this.generateNewScoreSheet(this.props.settings.playerIds),
|
||||
playerIds: this.props.settings.playerIds,
|
||||
scoreSheet: this.generateNewScoreSheet(this.props.settings.players),
|
||||
players: this.props.settings.players,
|
||||
showResults: true,
|
||||
savingGame: false,
|
||||
locked: false,
|
||||
saved: false,
|
||||
};
|
||||
|
||||
this.caretaker = new CaretakerSet(
|
||||
Settings.maxHistoryLength,
|
||||
...this.state.playerIds.map(
|
||||
pid => this.state.scoreSheet[pid]
|
||||
...this.state.players.map(
|
||||
player => this.state.scoreSheet[player.id]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private generateNewScoreSheet(playerIds: string[]): ScoreSheet {
|
||||
private generateNewScoreSheet(players: Player[]): ScoreSheet {
|
||||
const scoreSheet: ScoreSheet = {};
|
||||
for (const playerId of playerIds) {
|
||||
scoreSheet[playerId] = new PlayerScoreCard(playerId, this.gameSchema);
|
||||
for (const player of players) {
|
||||
scoreSheet[player.id] = new PlayerScoreCard(player.id, this.gameSchema);
|
||||
}
|
||||
return scoreSheet;
|
||||
}
|
||||
|
||||
private onCellEdit = (response: CellEventResponse): void => {
|
||||
if (!this.state.locked) {
|
||||
const newScoreSheet = this.state.scoreSheet;
|
||||
KadiBoard.updateScoreSheetFromCellResponse(newScoreSheet, response);
|
||||
this.setState({ scoreSheet: newScoreSheet });
|
||||
this.caretaker.save();
|
||||
}
|
||||
};
|
||||
|
||||
private static updateScoreSheetFromCellResponse(scoreSheet: ScoreSheet, response: CellEventResponse): void {
|
||||
@@ -137,12 +144,16 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
JSONScoreCards.push(this.state.scoreSheet[playerId].getJSONRepresentation());
|
||||
}
|
||||
return JSON.stringify({
|
||||
gameType: this.gameSchema.id,
|
||||
//rulesetUsed: this.gameSchema.id,
|
||||
players: this.state.players,
|
||||
results: JSONScoreCards
|
||||
});
|
||||
}
|
||||
|
||||
private canSave(): boolean {
|
||||
if (this.state.saved) {
|
||||
return false;
|
||||
}
|
||||
for (const playerId in this.state.scoreSheet) {
|
||||
if (!this.state.scoreSheet[playerId].filledOut()) {
|
||||
return false;
|
||||
@@ -152,18 +163,19 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
}
|
||||
|
||||
private saveGame: () => void = async () => {
|
||||
this.setState({savingGame: true});
|
||||
axios.post(Settings.rootUrl + "/api/savegame",
|
||||
this.setState({savingGame: true}, () => {
|
||||
axios.post(SERVER_BASE_NAME + "/api/games",
|
||||
this.getJSONRepresentationForBoard(),
|
||||
{headers: {"Content-Type": "application/json"}}
|
||||
)
|
||||
.then(response => this.onGameSave(response.data))
|
||||
.catch(error => this.onSaveError(error))
|
||||
.finally(() => this.setState({ savingGame: false }));
|
||||
});
|
||||
};
|
||||
|
||||
private onGameSave = (serverResponse: string) => {
|
||||
console.log("Response:", serverResponse);
|
||||
this.setState({locked: true, saved: true});
|
||||
};
|
||||
|
||||
private onSaveError = (error: any) => {
|
||||
@@ -179,13 +191,13 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
for (const cell of block.cells) {
|
||||
scores[cell.id] = {};
|
||||
}
|
||||
this.state.playerIds.forEach(pid => {
|
||||
scores.totals[pid] = this.getBlockTotalByPlayerId(block.id, pid);
|
||||
scores.bonuses[pid] = this.playerHasBonusForBlock(pid, block.id);
|
||||
scores.subtotals[pid] = this.getBlockSubtotalByPlayerId(block.id, pid);
|
||||
this.state.players.forEach(player => {
|
||||
scores.totals[player.id] = this.getBlockTotalByPlayerId(block.id, player.id);
|
||||
scores.bonuses[player.id] = this.playerHasBonusForBlock(player.id, block.id);
|
||||
scores.subtotals[player.id] = this.getBlockSubtotalByPlayerId(block.id, player.id);
|
||||
for (const cell of block.cells) {
|
||||
scores[cell.id][pid] = this.getCellDisplayValueByPlayerIdAndLocation(
|
||||
pid, { blockId: block.id, cellId: cell.id });
|
||||
scores[cell.id][player.id] = this.getCellDisplayValueByPlayerIdAndLocation(
|
||||
player.id, { blockId: block.id, cellId: cell.id });
|
||||
}
|
||||
});
|
||||
rows.push(
|
||||
@@ -200,8 +212,8 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
}
|
||||
|
||||
const grandTotals: CellScores = {};
|
||||
this.state.playerIds.forEach(pid =>
|
||||
grandTotals[pid] = this.getTotalForPlayer(pid)
|
||||
this.state.players.forEach(player =>
|
||||
grandTotals[player.id] = this.getTotalForPlayer(player.id)
|
||||
);
|
||||
rows.push(
|
||||
<KadiGrandTotalRow
|
||||
@@ -217,7 +229,7 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
<table className="kadiTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={this.state.playerIds.length + 1}>
|
||||
<th colSpan={this.state.players.length + 1}>
|
||||
<Header inverted={true} >
|
||||
<Image spaced={true} size={"small"} src={logo} />
|
||||
<Header.Content>
|
||||
@@ -228,7 +240,7 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<ColumnHeadersRow playerIds={this.state.playerIds} />
|
||||
<ColumnHeadersRow playerNames={this.state.players.map(player => player.nick)} />
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -272,7 +284,9 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
loading={this.state.savingGame}
|
||||
>
|
||||
<Icon name={"save"} />
|
||||
{Locale.buttons.saveGameButton}
|
||||
{this.state.saved ?
|
||||
Locale.buttons.saveGameButton.gameSaved :
|
||||
Locale.buttons.saveGameButton.saveGame}
|
||||
</Button>
|
||||
</div>
|
||||
</Container>
|
||||
@@ -284,10 +298,10 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
|
||||
KadiBoard.contextType = LocaleContext;
|
||||
|
||||
interface ColumnHeadersRowProps {
|
||||
playerIds: string[];
|
||||
playerNames: string[];
|
||||
}
|
||||
|
||||
const ColumnHeadersRow: React.FunctionComponent<ColumnHeadersRowProps> = ({ playerIds }) => {
|
||||
const ColumnHeadersRow: React.FunctionComponent<ColumnHeadersRowProps> = ({ playerNames }) => {
|
||||
const Locale = useContext(LocaleContext).strings;
|
||||
|
||||
const columnHeaders: ReactNode[] = [(
|
||||
@@ -295,7 +309,7 @@ const ColumnHeadersRow: React.FunctionComponent<ColumnHeadersRowProps> = ({ play
|
||||
{Locale.headers.rowLabels}
|
||||
</td>
|
||||
)];
|
||||
for (const playerId of playerIds) {
|
||||
for (const playerId of playerNames) {
|
||||
columnHeaders.push(
|
||||
<td className="playerNameCell" key={"header" + playerId}>
|
||||
{playerId}
|
||||
|
||||
0
src/filetypes.d.ts
vendored
Executable file → Normal file
0
src/filetypes.d.ts
vendored
Executable file → Normal file
@@ -1,10 +1,9 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "semantic-ui-css/semantic.min.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
import "./filetypes.d";
|
||||
|
||||
export const SERVER_BASE_NAME = "/kadi";
|
||||
export {serverRoot as SERVER_BASE_NAME} from "../package.json";
|
||||
|
||||
ReactDOM.render((
|
||||
<React.StrictMode>
|
||||
|
||||
@@ -377,15 +377,24 @@ div.globalTotalField {
|
||||
.playerOption {
|
||||
cursor: default;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.playerOption .trashButton {
|
||||
span.newPlayerText {
|
||||
color: #ebb600;
|
||||
float: right;
|
||||
font-style: italic;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.playerOption .playerText {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.trashButton {
|
||||
cursor: pointer;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
position: absolute;
|
||||
background-size: contain;
|
||||
background-image: url(../images/trash.png);
|
||||
background-repeat: no-repeat;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{
|
||||
"players": [],
|
||||
"ruleset": "default_en",
|
||||
"maxHistoryLength": 256,
|
||||
"rootUrl": "/kadi"
|
||||
"maxHistoryLength": 256
|
||||
}
|
||||
@@ -46,13 +46,11 @@ export const IntlStrings = {
|
||||
undoButton: "Undo",
|
||||
redoButton: "Redo",
|
||||
returnToSetupButton: "Back to setup",
|
||||
saveGameButton: "Save game",
|
||||
savingGame: "Saving...",
|
||||
saveGameButton: {
|
||||
saveGame: "Save game",
|
||||
gameSaved: "Success!",
|
||||
},
|
||||
languageNames: {
|
||||
en: "English",
|
||||
de: "German",
|
||||
it: "Italian"
|
||||
savingGame: "Saving...",
|
||||
},
|
||||
setupScreen: {
|
||||
selectLanguage: "Change language:",
|
||||
@@ -61,6 +59,7 @@ export const IntlStrings = {
|
||||
noPlayersEntered: "No players! Click here to add one...",
|
||||
clickToAddPlayer: "Click here to add a player...",
|
||||
players: "Players:",
|
||||
playerNew: "new!",
|
||||
},
|
||||
},
|
||||
de: {
|
||||
@@ -81,9 +80,12 @@ export const IntlStrings = {
|
||||
undoButton: "Rückgängig",
|
||||
redoButton: "Wiederholen",
|
||||
returnToSetupButton: "Zurück zu Einstellungen",
|
||||
saveGameButton: "Spiel speichern",
|
||||
saveGameButton: {
|
||||
saveGame: "Spiel speichern",
|
||||
gameSaved: "Gespeichert!",
|
||||
savingGame: "Wird gespeichert...",
|
||||
},
|
||||
},
|
||||
setupScreen: {
|
||||
selectLanguage: "Sprache ändern:",
|
||||
selectRuleset: "Wähle ein Regelwerk aus:",
|
||||
@@ -91,6 +93,7 @@ export const IntlStrings = {
|
||||
noPlayersEntered: "Leer! Hier tippen und hinzufügen...",
|
||||
clickToAddPlayer: "Zum Hinzufügen hier tippen...",
|
||||
players: "Mitspieler:",
|
||||
playerNew: "neu!",
|
||||
},
|
||||
},
|
||||
it: {
|
||||
@@ -111,9 +114,12 @@ export const IntlStrings = {
|
||||
undoButton: "Annullo",
|
||||
redoButton: "Ripristino",
|
||||
returnToSetupButton: "Torna a impostazioni",
|
||||
saveGameButton: "Salva gioco",
|
||||
saveGameButton: {
|
||||
saveGame: "Salva gioco",
|
||||
gameSaved: "Salvato!",
|
||||
savingGame: "Salva...",
|
||||
},
|
||||
},
|
||||
setupScreen: {
|
||||
selectLanguage: "Cambia lingua:",
|
||||
selectRuleset: "Sceglia il regolamento:",
|
||||
@@ -121,6 +127,7 @@ export const IntlStrings = {
|
||||
noPlayersEntered: "Nessuno! Inserire un nome qui...",
|
||||
clickToAddPlayer: "Clicca per inserire un altro nome...",
|
||||
players: "Giocatori:",
|
||||
playerNew: "nuovo!",
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
@@ -13,11 +13,11 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const Settings = require("./package.json");
|
||||
const SERVER_ROOT = Settings.serverRoot;
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/index.tsx",
|
||||
entry: ["@babel/polyfill", "./src/index.tsx"],
|
||||
mode: "production",
|
||||
module: {
|
||||
rules: [
|
||||
@@ -10,11 +12,8 @@ module.exports = {
|
||||
test: /\.tsx?$/,
|
||||
exclude: /(node_modules|bower_components|\.d\.ts$)/,
|
||||
use: [
|
||||
{
|
||||
loader: "babel-loader",
|
||||
options: { presets: ["@babel/env"] },
|
||||
},
|
||||
{ loader: "ts-loader" },
|
||||
"react-hot-loader/webpack",
|
||||
"babel-loader",
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -38,13 +37,14 @@ module.exports = {
|
||||
resolve: { extensions: [".tsx", ".ts", ".js", "*"] },
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist/"),
|
||||
publicPath: "/kadi/static/game/",
|
||||
publicPath: SERVER_ROOT + "/static/game/",
|
||||
filename: "bundle.js"
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.join(__dirname, "public/"),
|
||||
contentBasePublicPath: SERVER_ROOT + "/",
|
||||
port: 3000,
|
||||
publicPath: "http://localhost:3000/",
|
||||
publicPath: "http://localhost:3000" + SERVER_ROOT + "/static/game/",
|
||||
hotOnly: true
|
||||
},
|
||||
plugins: [new webpack.HotModuleReplacementPlugin()]
|
||||
|
||||
Reference in New Issue
Block a user