From 5342b7a48e9310a06f41abed8e1c13e62e53b0a8 Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Mon, 25 May 2020 22:52:10 +0200 Subject: [PATCH] New guests can be added, returning players recognised, and games are saved properly, locking the board. Changed webpack config to compile typescript with babel. --- .babelrc | 7 ++- package-lock.json | 68 +++++++++++++++++++++++ package.json | 12 ++-- public/index.html | 4 +- src/Components/Game.tsx | 37 ++++++++++-- src/Components/GameSetup.tsx | 105 ++++++++++++++++++++++++----------- src/Components/KadiBoard.tsx | 90 +++++++++++++++++------------- src/filetypes.d.ts | 0 src/index.tsx | 13 ++--- src/static/css/game.css | 17 ++++-- src/static/settings.json | 4 +- src/static/strings.ts | 27 +++++---- tsconfig.json | 14 ++--- webpack.config.js | 92 +++++++++++++++--------------- 14 files changed, 333 insertions(+), 157 deletions(-) mode change 100755 => 100644 src/filetypes.d.ts diff --git a/.babelrc b/.babelrc index cae5209..eaef4e3 100755 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,8 @@ { - "presets": ["@babel/env", "@babel/preset-react"] + "presets": [ + "@babel/typescript", + "@babel/react", + "@babel/env" + ], + "plugins": ["@babel/proposal-class-properties"] } diff --git a/package-lock.json b/package-lock.json index b821a94..0c5ccf2 100755 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 12341ad..587e238 100755 --- a/package.json +++ b/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", diff --git a/public/index.html b/public/index.html index 3a4647d..0f5a32a 100755 --- a/public/index.html +++ b/public/index.html @@ -2,7 +2,7 @@ - + You need to enable JavaScript to run this app. - + \ No newline at end of file diff --git a/src/Components/Game.tsx b/src/Components/Game.tsx index ecead83..4e97003 100755 --- a/src/Components/Game.tsx +++ b/src/Components/Game.tsx @@ -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 { 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 { render(): ReactNode { return ( <> - {this.state.settingUp ? + {!this.state.loading && (this.state.settingUp ? ( ) : ( @@ -56,7 +85,7 @@ class Game extends React.Component { returnToSetup={this.returnToSetup} /> ) - } + )} ); } diff --git a/src/Components/GameSetup.tsx b/src/Components/GameSetup.tsx index d118fc5..163c66f 100755 --- a/src/Components/GameSetup.tsx +++ b/src/Components/GameSetup.tsx @@ -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 { 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 { 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 { }; 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 { } 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(( -
- {playerName} - this.removePlayer(i)} - /> -
+ this.removePlayer(i)} + playerName={this.state.enteredPlayers[i].nick} + /> )); } playerListing.push(( @@ -105,6 +114,7 @@ class GameSetup extends React.Component { playersListEmpty={playerListing.length === 0} submitNewPlayer={this.addPlayer} userEditing={this.state.editingPlayerName} + availablePlayers={this.props.availablePlayers} /> )); @@ -137,7 +147,7 @@ class GameSetup extends React.Component { size={"huge"} color={"blue"} onClick={this.submitSettings} - disabled={this.state.enteredPlayerIds.length < 1} + disabled={this.state.enteredPlayers.length < 1} > {Locale.setupScreen.startGame} @@ -150,7 +160,15 @@ class GameSetup extends React.Component { } GameSetup.contextType = LocaleContext; -const AddPlayerField: React.FunctionComponent = ({playersListEmpty, submitNewPlayer, userEditing}) => { +interface AddPlayerFieldProps { + playersListEmpty: boolean; + submitNewPlayer: (name: string, keepEditing: boolean) => void; + userEditing: boolean; + availablePlayers: Player[]; +} + +const AddPlayerField: React.FunctionComponent = (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 = ({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 = (props) => { + const {removePlayer, playerName, playerIsNew} = props; + const Locale = React.useContext(LocaleContext).strings; + return ( + <> +
+
+ {playerName} + {playerIsNew && + + {Locale.setupScreen.playerNew} + + } +
+
+
+ + ); +}; + export default GameSetup; \ No newline at end of file diff --git a/src/Components/KadiBoard.tsx b/src/Components/KadiBoard.tsx index ad5ff4e..4f0481e 100755 --- a/src/Components/KadiBoard.tsx +++ b/src/Components/KadiBoard.tsx @@ -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 { 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 => { - const newScoreSheet = this.state.scoreSheet; - KadiBoard.updateScoreSheetFromCellResponse(newScoreSheet, response); - this.setState({ scoreSheet: newScoreSheet }); - this.caretaker.save(); + 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 { 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 { } private saveGame: () => void = async () => { - this.setState({savingGame: true}); - axios.post(Settings.rootUrl + "/api/savegame", - this.getJSONRepresentationForBoard(), - {headers: {"Content-Type": "application/json"}} - ) - .then(response => this.onGameSave(response.data)) - .catch(error => this.onSaveError(error)) - .finally(() => this.setState({ savingGame: false })); + 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 { 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 { } 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( { - - + player.nick)} /> {rows}
+
@@ -228,7 +240,7 @@ class KadiBoard extends React.Component {
@@ -272,7 +284,9 @@ class KadiBoard extends React.Component { loading={this.state.savingGame} > - {Locale.buttons.saveGameButton} + {this.state.saved ? + Locale.buttons.saveGameButton.gameSaved : + Locale.buttons.saveGameButton.saveGame}
@@ -284,10 +298,10 @@ class KadiBoard extends React.Component { KadiBoard.contextType = LocaleContext; interface ColumnHeadersRowProps { - playerIds: string[]; + playerNames: string[]; } -const ColumnHeadersRow: React.FunctionComponent = ({ playerIds }) => { +const ColumnHeadersRow: React.FunctionComponent = ({ playerNames }) => { const Locale = useContext(LocaleContext).strings; const columnHeaders: ReactNode[] = [( @@ -295,7 +309,7 @@ const ColumnHeadersRow: React.FunctionComponent = ({ play {Locale.headers.rowLabels} )]; - for (const playerId of playerIds) { + for (const playerId of playerNames) { columnHeaders.push( {playerId} diff --git a/src/filetypes.d.ts b/src/filetypes.d.ts old mode 100755 new mode 100644 diff --git a/src/index.tsx b/src/index.tsx index 1acefcd..96ff8b2 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,17 +1,16 @@ 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(( - - - + + + ), - document.getElementById('root') + document.getElementById('root') ); // If you want your app to work offline and load faster, you can change diff --git a/src/static/css/game.css b/src/static/css/game.css index 8aa3635..4f029da 100755 --- a/src/static/css/game.css +++ b/src/static/css/game.css @@ -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; diff --git a/src/static/settings.json b/src/static/settings.json index 0924150..fe33823 100755 --- a/src/static/settings.json +++ b/src/static/settings.json @@ -1,6 +1,4 @@ { - "players": [], "ruleset": "default_en", - "maxHistoryLength": 256, - "rootUrl": "/kadi" + "maxHistoryLength": 256 } \ No newline at end of file diff --git a/src/static/strings.ts b/src/static/strings.ts index d4bf683..c62d0d1 100755 --- a/src/static/strings.ts +++ b/src/static/strings.ts @@ -46,14 +46,12 @@ export const IntlStrings = { undoButton: "Undo", redoButton: "Redo", returnToSetupButton: "Back to setup", - saveGameButton: "Save game", + saveGameButton: { + saveGame: "Save game", + gameSaved: "Success!", + }, savingGame: "Saving...", }, - languageNames: { - en: "English", - de: "German", - it: "Italian" - }, setupScreen: { selectLanguage: "Change language:", selectRuleset: "Choose a ruleset to play with:", @@ -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,8 +80,11 @@ export const IntlStrings = { undoButton: "Rückgängig", redoButton: "Wiederholen", returnToSetupButton: "Zurück zu Einstellungen", - saveGameButton: "Spiel speichern", - savingGame: "Wird gespeichert...", + saveGameButton: { + saveGame: "Spiel speichern", + gameSaved: "Gespeichert!", + savingGame: "Wird gespeichert...", + }, }, setupScreen: { selectLanguage: "Sprache ändern:", @@ -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,8 +114,11 @@ export const IntlStrings = { undoButton: "Annullo", redoButton: "Ripristino", returnToSetupButton: "Torna a impostazioni", - saveGameButton: "Salva gioco", - savingGame: "Salva...", + saveGameButton: { + saveGame: "Salva gioco", + gameSaved: "Salvato!", + savingGame: "Salva...", + }, }, setupScreen: { selectLanguage: "Cambia lingua:", @@ -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; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index dd66595..f3461b9 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,11 +13,11 @@ "forceConsistentCasingInFileNames": true, "moduleResolution": "node", "resolveJsonModule": true, - "isolatedModules": true, - "lib": [ - "dom", - "dom.iterable", - "esnext" - ] - } + "isolatedModules": true + }, + "lib": [ + "dom", + "dom.iterable", + "esnext" + ] } \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 8433349..f479e22 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,51 +1,51 @@ const path = require("path"); const webpack = require("webpack"); +const Settings = require("./package.json"); +const SERVER_ROOT = Settings.serverRoot; module.exports = { - entry: "./src/index.tsx", - mode: "production", - module: { - rules: [ - { - test: /\.tsx?$/, - exclude: /(node_modules|bower_components|\.d\.ts$)/, - use: [ - { - loader: "babel-loader", - options: { presets: ["@babel/env"] }, - }, - { loader: "ts-loader" }, - ], - }, - { - test: /\.d\.ts$/, - loader: 'ignore-loader' - }, - { - test: /\.css$/, - use: ["style-loader", "css-loader"] - }, - { - test: /\.(png|jpe?g|gif|ttf|woff2?|eot|svg)$/i, - use: [ - { - loader: 'file-loader', - }, - ], - } - ] - }, - resolve: { extensions: [".tsx", ".ts", ".js", "*"] }, - output: { - path: path.resolve(__dirname, "dist/"), - publicPath: "/kadi/static/game/", - filename: "bundle.js" - }, - devServer: { - contentBase: path.join(__dirname, "public/"), - port: 3000, - publicPath: "http://localhost:3000/", - hotOnly: true - }, - plugins: [new webpack.HotModuleReplacementPlugin()] + entry: ["@babel/polyfill", "./src/index.tsx"], + mode: "production", + module: { + rules: [ + { + test: /\.tsx?$/, + exclude: /(node_modules|bower_components|\.d\.ts$)/, + use: [ + "react-hot-loader/webpack", + "babel-loader", + ], + }, + { + test: /\.d\.ts$/, + loader: 'ignore-loader' + }, + { + test: /\.css$/, + use: ["style-loader", "css-loader"] + }, + { + test: /\.(png|jpe?g|gif|ttf|woff2?|eot|svg)$/i, + use: [ + { + loader: 'file-loader', + }, + ], + } + ] + }, + resolve: { extensions: [".tsx", ".ts", ".js", "*"] }, + output: { + path: path.resolve(__dirname, "dist/"), + publicPath: SERVER_ROOT + "/static/game/", + filename: "bundle.js" + }, + devServer: { + contentBase: path.join(__dirname, "public/"), + contentBasePublicPath: SERVER_ROOT + "/", + port: 3000, + publicPath: "http://localhost:3000" + SERVER_ROOT + "/static/game/", + hotOnly: true + }, + plugins: [new webpack.HotModuleReplacementPlugin()] };