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:
Daniel Ledda
2020-05-25 22:52:10 +02:00
parent f08105c39f
commit 5342b7a48e
14 changed files with 333 additions and 157 deletions

View File

@@ -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
View File

@@ -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",

View File

@@ -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",

View File

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

View File

@@ -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}
/>
)
}
)}
</>
);
}

View File

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

View File

@@ -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
View File

View 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>

View File

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

View File

@@ -1,6 +1,4 @@
{
"players": [],
"ruleset": "default_en",
"maxHistoryLength": 256,
"rootUrl": "/kadi"
"maxHistoryLength": 256
}

View File

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

View File

@@ -13,11 +13,11 @@
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"isolatedModules": true
},
"lib": [
"dom",
"dom.iterable",
"esnext"
]
}
}

View File

@@ -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()]