aboutsummaryrefslogtreecommitdiff
path: root/diplomacy/web/src/gui/core/page.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'diplomacy/web/src/gui/core/page.jsx')
-rw-r--r--diplomacy/web/src/gui/core/page.jsx277
1 files changed, 75 insertions, 202 deletions
diff --git a/diplomacy/web/src/gui/core/page.jsx b/diplomacy/web/src/gui/core/page.jsx
index 5ca09fd..ad830f1 100644
--- a/diplomacy/web/src/gui/core/page.jsx
+++ b/diplomacy/web/src/gui/core/page.jsx
@@ -18,22 +18,14 @@
import React from "react";
import {ContentConnection} from "../diplomacy/contents/content_connection";
-import {ContentGames} from "../diplomacy/contents/content_games";
-import {ContentGame} from "../diplomacy/contents/content_game";
import {UTILS} from "../../diplomacy/utils/utils";
import {Diplog} from "../../diplomacy/utils/diplog";
-import {STRINGS} from "../../diplomacy/utils/strings";
-import {Game} from "../../diplomacy/engine/game";
-import Octicon, {Person} from '@githubprimer/octicons-react';
-import $ from "jquery";
import {FancyBox} from "./fancybox";
import {DipStorage} from "../diplomacy/utils/dipStorage";
-
-const CONTENTS = {
- connection: ContentConnection,
- games: ContentGames,
- game: ContentGame
-};
+import {PageContext} from "../diplomacy/widgets/page_context";
+import {ContentGames} from "../diplomacy/contents/content_games";
+import {loadGameFromDisk} from "../diplomacy/utils/load_game_from_disk";
+import {ContentGame} from "../diplomacy/contents/content_game";
export class Page extends React.Component {
@@ -50,23 +42,18 @@ export class Page extends React.Component {
error: null,
info: null,
success: null,
- title: null,
// Page content parameters
- contentName: 'connection',
- contentData: null,
+ name: null,
+ body: null,
// Games.
games: {}, // Games found.
myGames: {} // Games locally stored.
};
- this.loadPage = this.loadPage.bind(this);
- this.loadConnection = this.loadConnection.bind(this);
- this.loadGames = this.loadGames.bind(this);
- this.loadGame = this.loadGame.bind(this);
- this.loadGameFromDisk = this.loadGameFromDisk.bind(this);
- this.logout = this.logout.bind(this);
this.error = this.error.bind(this);
this.info = this.info.bind(this);
this.success = this.success.bind(this);
+ this.logout = this.logout.bind(this);
+ this.loadGameFromDisk = this.loadGameFromDisk.bind(this);
this.unloadFancyBox = this.unloadFancyBox.bind(this);
}
@@ -80,26 +67,8 @@ export class Page extends React.Component {
return games;
}
- copyState(updatedFields) {
- return Object.assign({}, this.state, updatedFields || {});
- }
-
- //// Methods to check page type.
-
- __page_is(contentName, contentData) {
- return this.state.contentName === contentName && (!contentData || this.state.contentData === contentData);
- }
-
- pageIsConnection(contentData) {
- return this.__page_is('connection', contentData);
- }
-
- pageIsGames(contentData) {
- return this.__page_is('games', contentData);
- }
-
- pageIsGame(contentData) {
- return this.__page_is('game', contentData);
+ static defaultPage() {
+ return <ContentConnection/>;
}
//// Methods to load a global fancybox.
@@ -114,91 +83,39 @@ export class Page extends React.Component {
//// Methods to load a page.
- loadPage(contentName, contentData, messages) {
- messages = messages || {};
- messages.error = Page.wrapMessage(messages.error);
- messages.info = Page.wrapMessage(messages.info);
- messages.success = Page.wrapMessage(messages.success);
- Diplog.printMessages(messages);
- this.setState(this.copyState({
- error: messages.error,
- info: messages.info,
- success: messages.success,
- contentName: contentName,
- contentData: contentData,
- title: null,
- fancyTitle: null,
- onFancyBox: null
- }));
- }
-
- loadConnection(contentData, messages) {
- this.loadPage('connection', contentData, messages);
+ load(name, body, messages) {
+ const newState = {};
+ if (messages) {
+ for (let key of ['error', 'info', 'success'])
+ newState[key] = Page.wrapMessage(messages[key]);
+ }
+ Diplog.printMessages(newState);
+ newState.name = name;
+ newState.body = body;
+ this.setState(newState);
}
- loadGames(contentData, messages) {
- this.loadPage('games', contentData, messages);
+ loadGames(messages) {
+ this.load(
+ 'games',
+ <ContentGames myGames={this.getMyGames()} gamesFound={this.getGamesFound()}/>,
+ messages
+ );
}
- loadGame(gameInfo, messages) {
- this.loadPage('game', gameInfo, messages);
+ loadGameFromDisk() {
+ loadGameFromDisk(
+ (game) => this.load(
+ `game: ${game.game_id}`,
+ <ContentGame data={game}/>,
+ {success: `Game loaded from disk: ${game.game_id}`}
+ ),
+ this.error
+ );
}
- loadGameFromDisk() {
- const input = $(document.createElement('input'));
- input.attr("type", "file");
- input.trigger('click');
- input.change(event => {
- const file = event.target.files[0];
- if (!file.name.match(/\.json$/i)) {
- this.error(`Invalid JSON filename ${file.name}`);
- } else {
- const reader = new FileReader();
- reader.onload = () => {
- const savedData = JSON.parse(reader.result);
- const gameObject = {};
- gameObject.game_id = `(local) ${savedData.id}`;
- gameObject.map_name = savedData.map;
- gameObject.rules = savedData.rules;
- const state_history = {};
- const message_history = {};
- const order_history = {};
- const result_history = {};
- for (let savedPhase of savedData.phases) {
- const gameState = savedPhase.state;
- const phaseOrders = savedPhase.orders || {};
- const phaseResults = savedPhase.results || {};
- const phaseMessages = {};
- if (savedPhase.messages) {
- for (let message of savedPhase.messages) {
- phaseMessages[message.time_sent] = message;
- }
- }
- if (!gameState.name)
- gameState.name = savedPhase.name;
- state_history[gameState.name] = gameState;
- order_history[gameState.name] = phaseOrders;
- message_history[gameState.name] = phaseMessages;
- result_history[gameState.name] = phaseResults;
- }
- gameObject.state_history = state_history;
- gameObject.message_history = message_history;
- gameObject.order_history = order_history;
- gameObject.state_history = state_history;
- gameObject.result_history = result_history;
- gameObject.messages = [];
- gameObject.role = STRINGS.OBSERVER_TYPE;
- gameObject.status = STRINGS.COMPLETED;
- gameObject.timestamp_created = 0;
- gameObject.deadline = 0;
- gameObject.n_controls = 0;
- gameObject.registration_password = '';
- const game = new Game(gameObject);
- this.loadGame(game);
- };
- reader.readAsText(file);
- }
- });
+ getName() {
+ return this.state.name;
}
//// Methods to sign out channel and go back to connection page.
@@ -211,16 +128,16 @@ export class Page extends React.Component {
this.availableMaps = null;
const message = Page.wrapMessage(`Disconnected from channel and server.`);
Diplog.success(message);
- this.setState(this.copyState({
+ this.setState({
error: null,
info: null,
success: message,
- contentName: 'connection',
- contentData: null,
+ name: null,
+ body: null,
// When disconnected, remove all games previously loaded.
games: {},
myGames: {}
- }));
+ });
}
logout() {
@@ -236,10 +153,6 @@ export class Page extends React.Component {
//// Methods to be used to set page title and messages.
- setTitle(title) {
- this.setState({title: title});
- }
-
error(message) {
message = Page.wrapMessage(message);
Diplog.error(message);
@@ -306,11 +219,14 @@ export class Page extends React.Component {
if (game.client) {
game.client.leave()
.then(() => {
- this.disconnectGame(gameID);
- this.loadGames(null, {info: `Game ${gameID} left.`});
+ this.disconnectGame(gameID).then(() => {
+ this.loadGames({info: `Game ${gameID} left.`});
+ });
})
.catch(error => this.error(`Error when leaving game ${gameID}: ${error.toString()}`));
}
+ } else {
+ this.loadGames({info: `No game to left.`});
}
}
@@ -319,12 +235,13 @@ export class Page extends React.Component {
const game = this.state.myGames[gameID];
if (game.client)
game.client.clearAllCallbacks();
- this.channel.getGamesInfo({games: [gameID]})
+ return this.channel.getGamesInfo({games: [gameID]})
.then(gamesInfo => {
this.updateMyGames(gamesInfo);
})
.catch(error => this.error(`Error while leaving game ${gameID}: ${error.toString()}`));
}
+ return null;
}
addToMyGames(game) {
@@ -335,7 +252,7 @@ export class Page extends React.Component {
if (gamesFound.hasOwnProperty(game.game_id))
gamesFound[game.game_id] = game;
DipStorage.addUserGame(this.channel.username, game.game_id);
- this.setState({myGames: myGames, games: gamesFound});
+ this.setState({myGames: myGames, games: gamesFound}, () => this.loadGames());
}
removeFromMyGames(gameID) {
@@ -343,7 +260,7 @@ export class Page extends React.Component {
const games = Object.assign({}, this.state.myGames);
delete games[gameID];
DipStorage.removeUserGame(this.channel.username, gameID);
- this.setState({myGames: games});
+ this.setState({myGames: games}, () => this.loadGames());
}
}
@@ -354,81 +271,37 @@ export class Page extends React.Component {
//// Render method.
render() {
- const content = CONTENTS[this.state.contentName].builder(this, this.state.contentData);
- const hasNavigation = UTILS.javascript.hasArray(content.navigation);
-
- // NB: I currently don't find a better way to update document title from content details.
const successMessage = this.state.success || '-';
const infoMessage = this.state.info || '-';
const errorMessage = this.state.error || '-';
- const title = this.state.title || content.title;
- document.title = title + ' | Diplomacy';
-
return (
- <div className="page container-fluid" id={this.state.contentName}>
- <div className={'top-msg row'}>
- <div title={successMessage !== '-' ? successMessage : ''}
- className={'col-sm-4 msg success ' + (this.state.success ? 'with-msg' : 'no-msg')}
- onClick={() => this.success()}>
- {successMessage}
- </div>
- <div title={infoMessage !== '-' ? infoMessage : ''}
- className={'col-sm-4 msg info ' + (this.state.info ? 'with-msg' : 'no-msg')}
- onClick={() => this.info()}>
- {infoMessage}
- </div>
- <div title={errorMessage !== '-' ? errorMessage : ''}
- className={'col-sm-4 msg error ' + (this.state.error ? 'with-msg' : 'no-msg')}
- onClick={() => this.error()}>
- {errorMessage}
- </div>
- </div>
- {((hasNavigation || this.channel) && (
- <div className={'title row'}>
- <div className={'col align-self-center'}><strong>{title}</strong></div>
- <div className={'col-sm-1'}>
- {(!hasNavigation && (
- <div className={'float-right'}>
- <strong>
- <u className={'mr-2'}>{this.channel.username}</u>
- <Octicon icon={Person}/>
- </strong>
- </div>
- )) || (
- <div className="dropdown float-right">
- <button className="btn btn-secondary dropdown-toggle" type="button"
- id="dropdownMenuButton" data-toggle="dropdown"
- aria-haspopup="true" aria-expanded="false">
- {(this.channel && this.channel.username && (
- <span>
- <u className={'mr-2'}>{this.channel.username}</u>
- <Octicon icon={Person}/>
- </span>
- )) || 'Menu'}
- </button>
- <div className="dropdown-menu dropdown-menu-right"
- aria-labelledby="dropdownMenuButton">
- {content.navigation.map((nav, index) => {
- const navTitle = nav[0];
- const navAction = nav[1];
- return <a key={index} className="dropdown-item"
- onClick={navAction}>{navTitle}</a>;
- })}
- </div>
- </div>
- )}
+ <PageContext.Provider value={this}>
+ <div className="page container-fluid" id={this.state.contentName}>
+ <div className={'top-msg row'}>
+ <div title={successMessage !== '-' ? successMessage : ''}
+ className={'col-sm-4 msg success ' + (this.state.success ? 'with-msg' : 'no-msg')}
+ onClick={() => this.success()}>
+ {successMessage}
+ </div>
+ <div title={infoMessage !== '-' ? infoMessage : ''}
+ className={'col-sm-4 msg info ' + (this.state.info ? 'with-msg' : 'no-msg')}
+ onClick={() => this.info()}>
+ {infoMessage}
+ </div>
+ <div title={errorMessage !== '-' ? errorMessage : ''}
+ className={'col-sm-4 msg error ' + (this.state.error ? 'with-msg' : 'no-msg')}
+ onClick={() => this.error()}>
+ {errorMessage}
</div>
</div>
- )) || (
- <div className={'title'}><strong>{title}</strong></div>
- )}
- {content.component}
- {this.state.onFancyBox && (
- <FancyBox title={this.state.fancyTitle} onClose={this.unloadFancyBox}>
- {this.state.onFancyBox()}
- </FancyBox>
- )}
- </div>
+ {this.state.body || Page.defaultPage()}
+ {this.state.onFancyBox && (
+ <FancyBox title={this.state.fancyTitle} onClose={this.unloadFancyBox}>
+ {this.state.onFancyBox()}
+ </FancyBox>
+ )}
+ </div>
+ </PageContext.Provider>
);
}
}