diff options
author | notoraptor <notoraptor@users.noreply.github.com> | 2019-07-16 10:39:16 -0400 |
---|---|---|
committer | Philip Paquette <pcpaquette@gmail.com> | 2019-07-16 10:39:16 -0400 |
commit | 09be214dfa0741df2399c3e0c928929138de226b (patch) | |
tree | 3d76cb1d9e6f3cd5452563b476fb251103fd3ec7 /diplomacy/web/src | |
parent | 5c71a0f73717bffefb5e23a9e100adb62fc54a61 (diff) |
[web] Add a button to delete game. (#39)
- [server] Also delete game from RAM.
Diffstat (limited to 'diplomacy/web/src')
5 files changed, 139 insertions, 18 deletions
diff --git a/diplomacy/web/src/gui/core/delete_button.jsx b/diplomacy/web/src/gui/core/delete_button.jsx new file mode 100644 index 0000000..59141fd --- /dev/null +++ b/diplomacy/web/src/gui/core/delete_button.jsx @@ -0,0 +1,43 @@ +import React from "react"; +import {Button} from "./button"; +import PropTypes from "prop-types"; + +export class DeleteButton extends React.Component { + constructor(props) { + super(props); + this.state = {step: 0}; + this.onClick = this.onClick.bind(this); + } + + onClick() { + this.setState({step: this.state.step + 1}, () => { + if (this.state.step === 2) + this.props.onClick(); + }); + } + + render() { + let title = ''; + let color = ''; + if (this.state.step === 0) { + title = this.props.title; + color = 'secondary'; + } else if (this.state.step === 1) { + title = this.props.confirmTitle; + color = 'danger'; + } else if (this.state.step === 2) { + title = this.props.waitingTitle; + color = 'danger'; + } + return ( + <Button title={title} color={color} onClick={this.onClick} small={true} large={true}/> + ); + } +} + +DeleteButton.propTypes = { + title: PropTypes.string.isRequired, + confirmTitle: PropTypes.string.isRequired, + waitingTitle: PropTypes.string.isRequired, + onClick: PropTypes.func.isRequired +}; diff --git a/diplomacy/web/src/gui/core/page.jsx b/diplomacy/web/src/gui/core/page.jsx index ad830f1..560252c 100644 --- a/diplomacy/web/src/gui/core/page.jsx +++ b/diplomacy/web/src/gui/core/page.jsx @@ -55,6 +55,10 @@ export class Page extends React.Component { this.logout = this.logout.bind(this); this.loadGameFromDisk = this.loadGameFromDisk.bind(this); this.unloadFancyBox = this.unloadFancyBox.bind(this); + this._post_remove = this._post_remove.bind(this); + this._add_to_my_games = this._add_to_my_games.bind(this); + this._remove_from_my_games = this._remove_from_my_games.bind(this); + this._remove_from_games = this._remove_from_games.bind(this); } static wrapMessage(message) { @@ -194,6 +198,12 @@ export class Page extends React.Component { this.setState({myGames: myGames, games: gamesFound}); } + getGame(gameID) { + if (this.state.myGames.hasOwnProperty(gameID)) + return this.state.myGames[gameID]; + return this.state.games[gameID]; + } + getMyGames() { return Page.__sort_games(Object.values(this.state.myGames)); } @@ -230,9 +240,40 @@ export class Page extends React.Component { } } + _post_remove(gameID) { + this.disconnectGame(gameID) + .then(() => { + const myGames = this._remove_from_my_games(gameID); + const games = this._remove_from_games(gameID); + this.setState( + {games, myGames}, + () => this.loadGames({info: `Game ${gameID} deleted.`})); + }); + } + + removeGame(gameID) { + const game = this.getGame(gameID); + if (game) { + if (game.client) { + game.client.remove() + .then(() => this._post_remove(gameID)) + .catch(error => this.error(`Error when deleting game ${gameID}: ${error.toString()}`)); + } else { + this.channel.joinGame({game_id: gameID}) + .then(networkGame => { + networkGame.remove() + .then(() => this._post_remove(gameID)) + .catch(error => this.error(`Error when deleting game ${gameID}: ${error.toString()}`)); + }) + .catch(error => this.error(`Error when connecting to game to delete (${gameID}): ${error.toString()}`)); + } + } + } + + disconnectGame(gameID) { - if (this.state.myGames.hasOwnProperty(gameID)) { - const game = this.state.myGames[gameID]; + const game = this.getGame(gameID); + if (game) { if (game.client) game.client.clearAllCallbacks(); return this.channel.getGamesInfo({games: [gameID]}) @@ -244,26 +285,48 @@ export class Page extends React.Component { return null; } - addToMyGames(game) { - // Update state myGames with given game **and** update local storage. + _add_to_my_games(game) { const myGames = Object.assign({}, this.state.myGames); const gamesFound = this.state.games.hasOwnProperty(game.game_id) ? Object.assign({}, this.state.games) : this.state.games; myGames[game.game_id] = game; 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.loadGames()); + return {myGames: myGames, games: gamesFound}; } - removeFromMyGames(gameID) { + _remove_from_my_games(gameID) { if (this.state.myGames.hasOwnProperty(gameID)) { const games = Object.assign({}, this.state.myGames); delete games[gameID]; DipStorage.removeUserGame(this.channel.username, gameID); - this.setState({myGames: games}, () => this.loadGames()); + return games; + } else { + return this.state.myGames; + } + } + + _remove_from_games(gameID) { + if (this.state.games.hasOwnProperty(gameID)) { + const games = Object.assign({}, this.state.games); + delete games[gameID]; + return games; + } else { + return this.state.games; } } + addToMyGames(game) { + // Update state myGames with given game **and** update local storage. + DipStorage.addUserGame(this.channel.username, game.game_id); + this.setState(this._add_to_my_games(game), () => this.loadGames()); + } + + removeFromMyGames(gameID) { + const myGames = this._remove_from_my_games(gameID); + if (myGames !== this.state.myGames) + this.setState({myGames}, () => this.loadGames()); + } + hasMyGame(gameID) { return this.state.myGames.hasOwnProperty(gameID); } diff --git a/diplomacy/web/src/gui/diplomacy/contents/content_games.jsx b/diplomacy/web/src/gui/diplomacy/contents/content_games.jsx index 51ad998..4ae9f2f 100644 --- a/diplomacy/web/src/gui/diplomacy/contents/content_games.jsx +++ b/diplomacy/web/src/gui/diplomacy/contents/content_games.jsx @@ -37,7 +37,7 @@ const TABLE_LOCAL_GAMES = { status: ['Status', 5], phase: ['Phase', 6], join: ['Join', 7], - my_games: ['My Games', 8], + actions: ['Actions', 8], }; export class ContentGames extends React.Component { diff --git a/diplomacy/web/src/gui/diplomacy/utils/inline_game_view.jsx b/diplomacy/web/src/gui/diplomacy/utils/inline_game_view.jsx index 3de649c..b6c9e67 100644 --- a/diplomacy/web/src/gui/diplomacy/utils/inline_game_view.jsx +++ b/diplomacy/web/src/gui/diplomacy/utils/inline_game_view.jsx @@ -19,6 +19,7 @@ import {JoinForm} from "../forms/join_form"; import {STRINGS} from "../../../diplomacy/utils/strings"; import {ContentGame} from "../contents/content_game"; import {Button} from "../../core/button"; +import {DeleteButton} from "../../core/delete_button"; export class InlineGameView { constructor(page, gameData) { @@ -80,19 +81,33 @@ export class InlineGameView { } } - getMyGamesButton() { + getActionButtons() { + const buttons = []; + // Button to add/remove game from "My games" list. if (this.page.hasMyGame(this.game.game_id)) { if (!this.game.client) { // Game in My Games and not joined. We can remove it. - return <Button key={`my-game-remove`} title={'Remove from My Games'} - onClick={() => this.page.removeFromMyGames(this.game.game_id)}/>; + buttons.push(<Button key={`my-game-remove`} title={'Remove from My Games'} + small={true} large={true} + onClick={() => this.page.removeFromMyGames(this.game.game_id)}/>); } } else { // Game not in My Games, we can add it. - return <Button key={`my-game-add`} title={'Add to My Games'} - onClick={() => this.page.addToMyGames(this.game)}/>; + buttons.push(<Button key={`my-game-add`} title={'Add to My Games'} + small={true} large={true} + onClick={() => this.page.addToMyGames(this.game)}/>); } - return ''; + // Button to delete game. + if ([STRINGS.MASTER_TYPE, STRINGS.OMNISCIENT_TYPE].includes(this.game.observer_level)) { + buttons.push( + <DeleteButton key={`game-delete-${this.game.game_id}`} + title={'Delete this game'} + confirmTitle={'Click again to confirm deletion'} + waitingTitle={'Deleting ...'} + onClick={() => this.page.removeGame(this.game.game_id)}/> + ); + } + return buttons; } get(name) { @@ -128,8 +143,8 @@ export class InlineGameView { } if (name === 'join') return this.getJoinUI(); - if (name === 'my_games') - return this.getMyGamesButton(); + if (name === 'actions') + return this.getActionButtons(); if (name === 'game_id') { const date = new Date(this.game.timestamp_created / 1000); const dateString = `${date.toLocaleDateString()} - ${date.toLocaleTimeString()}`; diff --git a/diplomacy/web/src/gui/diplomacy/widgets/message_view.jsx b/diplomacy/web/src/gui/diplomacy/widgets/message_view.jsx index 46153b8..927ff52 100644 --- a/diplomacy/web/src/gui/diplomacy/widgets/message_view.jsx +++ b/diplomacy/web/src/gui/diplomacy/widgets/message_view.jsx @@ -48,7 +48,7 @@ export class MessageView extends React.Component { </div> <div className="message-content col-md"> {messagesLines.map((line, lineIndex) => <div key={lineIndex}>{ - line.replace(/(<([^>]+)>)/ig,"") + line.replace(/(<([^>]+)>)/ig, "") }</div>)} </div> </div> |