This commit is contained in:
Daniel Ledda
2020-12-24 12:38:52 +01:00
parent 652092f741
commit 7a58666702
5 changed files with 164 additions and 103 deletions

View File

@@ -11,6 +11,8 @@ import {SERVER_BASE_NAME} from "./index";
interface AppState { interface AppState {
userContext: IUserContext; userContext: IUserContext;
localeContext: ILocaleContext; localeContext: ILocaleContext;
loading: boolean;
majorFailure: boolean;
} }
interface AppProps {} interface AppProps {}
@@ -40,6 +42,8 @@ class App extends React.Component<AppProps, AppState> {
}; };
this.state = { this.state = {
majorFailure: false,
loading: true,
userContext: { userContext: {
username: "", username: "",
loggedIn: false, loggedIn: false,
@@ -54,20 +58,35 @@ class App extends React.Component<AppProps, AppState> {
} }
componentDidMount(): void { componentDidMount(): void {
axios.get("/api/user", {baseURL: SERVER_BASE_NAME}) this.loadUserData();
.then((res) => { }
const data = res.data as any;
this.updateUserContext(data.username, true); async loadUserData(): Promise<void> {
this.changeLang(data.lang); try {
}) const response = (await axios.get("/api/user", {baseURL: SERVER_BASE_NAME})).data;
.catch(err => console.log(err)); this.updateUserContext(response.username, true);
this.changeLang(response.lang);
}
catch (e) {
this.handleNoUserData();
}
finally {
this.setState({loading: false});
}
}
handleNoUserData(): void {
this.setState({majorFailure: true});
} }
render(): ReactNode { render(): ReactNode {
return ( return (
<LocaleContext.Provider value={this.state.localeContext}> <LocaleContext.Provider value={this.state.localeContext}>
<UserContext.Provider value={this.state.userContext}> <UserContext.Provider value={this.state.userContext}>
<Game/> <Game
loading={this.state.loading}
majorFailure={this.state.majorFailure}
/>
</UserContext.Provider> </UserContext.Provider>
</LocaleContext.Provider> </LocaleContext.Provider>
); );

View File

@@ -5,6 +5,7 @@ import GameSetup, {GameSettings} from "./GameSetup"
import Settings from "../static/settings.json"; import Settings from "../static/settings.json";
import {SERVER_BASE_NAME} from "../index"; import {SERVER_BASE_NAME} from "../index";
import axios from "axios"; import axios from "axios";
import {Ruleset} from "../static/rulesets";
export interface Player { export interface Player {
id: string; id: string;
@@ -17,13 +18,15 @@ interface GameState {
loading: boolean; loading: boolean;
availablePlayers: Player[] availablePlayers: Player[]
debug: boolean; debug: boolean;
availableRulesets: Ruleset[];
} }
interface GameProps {} interface GameProps {
loading: boolean;
majorFailure: boolean;
}
class Game extends React.Component<GameProps, GameState> { class Game extends React.Component<GameProps, GameState> {
state: GameState;
constructor(props: GameProps) { constructor(props: GameProps) {
super(props); super(props);
@@ -38,15 +41,25 @@ class Game extends React.Component<GameProps, GameState> {
loading: true, loading: true,
availablePlayers: [], availablePlayers: [],
debug: Settings.debug, debug: Settings.debug,
availableRulesets: [],
}; };
} }
componentDidMount(): void { componentDidMount(): void {
axios.get(SERVER_BASE_NAME + "/api/players") this.loadData();
.then(response => this.addNewPlayers([...response.data.guests, response.data.mainPlayer])) }
.catch(error => this.handleError(error))
.finally(() => this.setState({ loading: false })); async loadData(): Promise<void> {
console.log(this.state.availablePlayers); try {
await this.loadAllPlayers();
await this.loadAllRulesets();
}
catch (e) {
this.handleRequestError(e);
}
finally {
this.setState({loading: false});
}
} }
addNewPlayers(players: Player[]): void { addNewPlayers(players: Player[]): void {
@@ -55,8 +68,8 @@ class Game extends React.Component<GameProps, GameState> {
this.setState({availablePlayers}); this.setState({availablePlayers});
} }
handleError(error: any): void { handleRequestError(error: any): void {
console.log(error);
}; };
onSetupComplete: (gameSettings: GameSettings) => void = (gameSettings) => { onSetupComplete: (gameSettings: GameSettings) => void = (gameSettings) => {
@@ -66,29 +79,48 @@ class Game extends React.Component<GameProps, GameState> {
}); });
}; };
returnToSetup: () => void = () => { returnToSetup(): void {
this.setState({settingUp: true}); this.setState({settingUp: true});
}; };
async loadAllRulesets(): Promise<void> {
const rulesetsSlug = (await axios.get(SERVER_BASE_NAME + "/api/rulesets/")).data;
this.setState({availableRulesets: rulesetsSlug.rulesets as Ruleset[]});
}
async loadAllPlayers(): Promise<void> {
const response = (await axios.get(SERVER_BASE_NAME + "/api/players")).data;
this.addNewPlayers([...response.guests, response.mainPlayer])
}
selectedRuleset(): Ruleset {
return this.state.availableRulesets.find(
ruleset => ruleset.id === this.state.currentSettings.ruleset) as Ruleset;
}
render(): ReactNode { render(): ReactNode {
return ( return (
<> <>
{!this.state.loading && (this.state.settingUp ? {this.state.settingUp || this.state.loading || this.props.loading ?
( (
<GameSetup <GameSetup
onSetupComplete={this.onSetupComplete} loading={this.state.loading || this.props.loading}
majorFailure={this.props.majorFailure}
onSetupComplete={(s) => this.onSetupComplete(s)}
settings={this.state.currentSettings} settings={this.state.currentSettings}
availablePlayers={this.state.availablePlayers} availablePlayers={this.state.availablePlayers}
availableRulesets={this.state.availableRulesets}
/> />
) : ) :
( (
<KadiBoard <KadiBoard
gameSchema={this.selectedRuleset()}
debug={this.state.debug} debug={this.state.debug}
settings={this.state.currentSettings} settings={this.state.currentSettings}
returnToSetup={this.returnToSetup} returnToSetup={() => this.returnToSetup()}
/> />
) )
)} }
</> </>
); );
} }

View File

@@ -1,13 +1,16 @@
import React, {ChangeEvent, FocusEvent, KeyboardEvent, ReactNode} from "react"; import React, {ChangeEvent, FocusEvent, KeyboardEvent, ReactNode} from "react";
import {getSchemaListings, SchemaListing} from "../static/rulesets"; import {Ruleset} from "../static/rulesets";
import LocaleContext from "../LocaleContext"; import LocaleContext from "../LocaleContext";
import {Button} from "semantic-ui-react"; import {Button} from "semantic-ui-react";
import {Player} from "./Game"; import {Player} from "./Game";
interface GameSetupProps { interface GameSetupProps {
loading: boolean;
onSetupComplete: (settings: GameSettings) => void; onSetupComplete: (settings: GameSettings) => void;
settings: GameSettings; settings: GameSettings;
availablePlayers: Player[]; availablePlayers: Player[];
availableRulesets: Ruleset[];
majorFailure: boolean;
} }
interface GameSetupState { interface GameSetupState {
@@ -22,13 +25,9 @@ export interface GameSettings {
} }
class GameSetup extends React.Component<GameSetupProps, GameSetupState> { class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
private readonly availableRulesets: SchemaListing[];
private changeLang: (lang: string) => void; private changeLang: (lang: string) => void;
state: GameSetupState;
constructor(props: GameSetupProps) { constructor(props: GameSetupProps) {
super(props); super(props);
this.availableRulesets = getSchemaListings();
this.changeLang = () => {}; this.changeLang = () => {};
this.state = { this.state = {
selectedRuleset: this.props.settings.ruleset, selectedRuleset: this.props.settings.ruleset,
@@ -66,7 +65,7 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
return this.props.availablePlayers.find(player => player.nick === playerName)?.id; return this.props.availablePlayers.find(player => player.nick === playerName)?.id;
} }
playerIsNew(player: Player) { playerIsNew(player: Player): boolean {
return player.id === player.nick; return player.id === player.nick;
} }
@@ -81,7 +80,7 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
const Locale = this.context.strings; const Locale = this.context.strings;
const rulesetOptions: ReactNode[] = []; const rulesetOptions: ReactNode[] = [];
for (const rulesetListing of this.availableRulesets) { for (const rulesetListing of this.props.availableRulesets) {
let className = "option"; let className = "option";
if (this.state.selectedRuleset === rulesetListing.id) { if (this.state.selectedRuleset === rulesetListing.id) {
className += " selected"; className += " selected";
@@ -122,36 +121,61 @@ class GameSetup extends React.Component<GameSetupProps, GameSetupState> {
<div className={"gameSetup"}> <div className={"gameSetup"}>
<div className={"setupFormContainer"}> <div className={"setupFormContainer"}>
<div className={"setupForm"}> <div className={"setupForm"}>
<div className={"optionGroup"}> {this.props.majorFailure && (
<div className={"optionGroupTitleContainer"}> <div className={"optionGroup"}>
<span className={"optionGroupTitle"}> <div className={"optionGroupTitleContainer"}>
{Locale.setupScreen.players} <span className={"optionGroupTitle"}>
</span> An Error Occurred
</span>
</div>
<div className={"optionList"}>
The necessary data couldn't be loaded to play. Try reloading the page.
</div>
</div> </div>
<div className={"playerList optionList"}> )}
{playerListing} {this.props.loading && (
<div className={"optionGroup"}>
<div className={"optionGroupTitleContainer"}>
<span className={"optionGroupTitle"}>
Loading...
</span>
</div>
</div> </div>
</div> )}
<div className={"optionGroup"}> {!this.props.loading && !this.props.majorFailure && (
<div className={"optionGroupTitleContainer"}> <>
<span className={"optionGroupTitle"}> <div className={"optionGroup"}>
{Locale.setupScreen.selectRuleset} <div className={"optionGroupTitleContainer"}>
</span> <span className={"optionGroupTitle"}>
</div> {Locale.setupScreen.players}
<div className={"rulesetOptions optionList"}> </span>
{rulesetOptions} </div>
</div> <div className={"playerList optionList"}>
</div> {playerListing}
<div className={"playButtonContainer"}> </div>
<Button </div>
size={"huge"} <div className={"optionGroup"}>
color={"blue"} <div className={"optionGroupTitleContainer"}>
onClick={this.submitSettings} <span className={"optionGroupTitle"}>
disabled={this.state.enteredPlayers.length < 1} {Locale.setupScreen.selectRuleset}
> </span>
{Locale.setupScreen.startGame} </div>
</Button> <div className={"rulesetOptions optionList"}>
</div> {rulesetOptions}
</div>
</div>
<div className={"playButtonContainer"}>
<Button
size={"huge"}
color={"blue"}
onClick={this.submitSettings}
disabled={this.state.enteredPlayers.length < 1}
>
{Locale.setupScreen.startGame}
</Button>
</div>
</>
)}
</div> </div>
</div> </div>
</div> </div>
@@ -233,24 +257,22 @@ const ActivePlayerListItem: React.FunctionComponent<ActivePlayerListItemProps> =
const {removePlayer, playerName, playerIsNew} = props; const {removePlayer, playerName, playerIsNew} = props;
const Locale = React.useContext(LocaleContext).strings; const Locale = React.useContext(LocaleContext).strings;
return ( return (
<> <div
<div className={"option playerOption"}
className={"option playerOption"} >
> <div className={"playerText"}>
<div className={"playerText"}> {playerName}
{playerName} {playerIsNew && (
{playerIsNew && <span className={"newPlayerText"}>
<span className={"newPlayerText"}> {Locale.setupScreen.playerNew}
{Locale.setupScreen.playerNew} </span>
</span> )}
}
</div>
<div
className={"trashButton"}
onClick={removePlayer}
/>
</div> </div>
</> <div
className={"trashButton"}
onClick={removePlayer}
/>
</div>
); );
}; };

View File

@@ -38,6 +38,7 @@ export interface KadiBoardProps {
settings: GameSettings; settings: GameSettings;
returnToSetup: () => void; returnToSetup: () => void;
debug: boolean; debug: boolean;
gameSchema: Ruleset;
} }
interface KadiBoardState { interface KadiBoardState {
@@ -57,12 +58,11 @@ interface ScoreSheet {
class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> { class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
private readonly gameSchema: Ruleset; private readonly gameSchema: Ruleset;
private readonly caretaker: CaretakerSet; private readonly caretaker: CaretakerSet;
state: KadiBoardState;
constructor(props: KadiBoardProps) { constructor(props: KadiBoardProps) {
super(props); super(props);
this.gameSchema = getGameSchemaById(this.props.settings.ruleset); this.gameSchema = this.props.gameSchema;
this.state = { this.state = {
scoreSheet: this.generateNewScoreSheet(this.props.settings.players), scoreSheet: this.generateNewScoreSheet(this.props.settings.players),
@@ -166,25 +166,22 @@ class KadiBoard extends React.Component<KadiBoardProps, KadiBoardState> {
} }
private saveGame: () => void = async () => { private saveGame: () => void = async () => {
this.setState({savingGame: true}, () => { console.log(this.getJSONRepresentationForBoard());
axios.post(SERVER_BASE_NAME + "/api/games", this.setState({savingGame: true}, async () => {
this.getJSONRepresentationForBoard(), try {
{headers: {"Content-Type": "application/json"}} const response = await axios.post(SERVER_BASE_NAME + "/api/games",
) this.getJSONRepresentationForBoard(), {headers: {"Content-Type": "application/json"}});
.then(response => this.onGameSave(response.data)) this.setState({locked: true, saved: true});
.catch(error => this.onSaveError(error)) }
.finally(() => this.setState({ savingGame: false })); catch (e) {
console.log("Error saving:", e);
}
finally {
this.setState({ savingGame: false });
}
}); });
}; };
private onGameSave = (serverResponse: string) => {
this.setState({locked: true, saved: true});
};
private onSaveError = (error: any) => {
console.log("Error saving:", error);
};
private fillOutRandomly(): void { private fillOutRandomly(): void {
for (const player of this.state.players) { for (const player of this.state.players) {
for (const blockId in this.gameSchema.blocks) { for (const blockId in this.gameSchema.blocks) {

View File

@@ -192,13 +192,4 @@ export function getGameSchemaById(schemaId: string): Ruleset {
} }
} }
throw new RangeError("No such GameSchema with id '" + schemaId + "'!"); throw new RangeError("No such GameSchema with id '" + schemaId + "'!");
} }
export interface SchemaListing {
id: string;
label: string;
}
export function getSchemaListings(): SchemaListing[] {
return gameSchemas.map((s) => ({ id: s.id, label: s.label }));
}