From 382ad49e6fc12ae4f7959c00da301cc998ed5200 Mon Sep 17 00:00:00 2001 From: Jennifer Taylor Date: Fri, 8 May 2020 22:40:21 +0000 Subject: [PATCH] Add rival selection support to Jubeat in preparation for rival support. --- bemani/frontend/app.py | 4 + bemani/frontend/jubeat/endpoints.py | 121 ++++++- bemani/frontend/jubeat/jubeat.py | 2 + .../static/controllers/jubeat/rivals.react.js | 295 ++++++++++++++++++ 4 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 bemani/frontend/static/controllers/jubeat/rivals.react.js diff --git a/bemani/frontend/app.py b/bemani/frontend/app.py index c968427..c1bfaa7 100644 --- a/bemani/frontend/app.py +++ b/bemani/frontend/app.py @@ -404,6 +404,10 @@ def navigation() -> Dict[str, Any]: 'label': 'Game Options', 'uri': url_for('jubeat_pages.viewsettings'), }, + { + 'label': 'Rivals', + 'uri': url_for('jubeat_pages.viewrivals'), + }, { 'label': 'Personal Profile', 'uri': url_for('jubeat_pages.viewplayer', userid=g.userID), diff --git a/bemani/frontend/jubeat/endpoints.py b/bemani/frontend/jubeat/endpoints.py index b0b689c..2e0b583 100644 --- a/bemani/frontend/jubeat/endpoints.py +++ b/bemani/frontend/jubeat/endpoints.py @@ -3,7 +3,7 @@ import re from typing import Any, Dict from flask import Blueprint, request, Response, url_for, abort, g # type: ignore -from bemani.common import GameConstants +from bemani.common import ID, GameConstants from bemani.data import UserID from bemani.frontend.app import loginrequired, jsonify, render_react from bemani.frontend.jubeat.jubeat import JubeatFrontend @@ -338,3 +338,122 @@ def updatename() -> Dict[str, Any]: 'version': version, 'name': name, } + + +@jubeat_pages.route('/rivals') +@loginrequired +def viewrivals() -> Response: + frontend = JubeatFrontend(g.data, g.config, g.cache) + rivals, playerinfo = frontend.get_rivals(g.userID) + return render_react( + 'Jubeat Rivals', + 'jubeat/rivals.react.js', + { + 'userid': str(g.userID), + 'rivals': rivals, + 'players': playerinfo, + 'versions': {version: name for (game, version, name) in frontend.all_games()}, + }, + { + 'refresh': url_for('jubeat_pages.listrivals'), + 'search': url_for('jubeat_pages.searchrivals'), + 'player': url_for('jubeat_pages.viewplayer', userid=-1), + 'addrival': url_for('jubeat_pages.addrival'), + 'removerival': url_for('jubeat_pages.removerival'), + }, + ) + + +@jubeat_pages.route('/rivals/list') +@jsonify +@loginrequired +def listrivals() -> Dict[str, Any]: + frontend = JubeatFrontend(g.data, g.config, g.cache) + rivals, playerinfo = frontend.get_rivals(g.userID) + + return { + 'rivals': rivals, + 'players': playerinfo, + } + + +@jubeat_pages.route('/rivals/search', methods=['POST']) +@jsonify +@loginrequired +def searchrivals() -> Dict[str, Any]: + frontend = JubeatFrontend(g.data, g.config, g.cache) + version = int(request.get_json()['version']) + name = request.get_json()['term'] + + # Try to treat the term as an extid + extid = ID.parse_extid(name) + + matches = set() + profiles = g.data.remote.user.get_all_profiles(GameConstants.JUBEAT, version) + for (userid, profile) in profiles: + if profile.get_int('extid') == extid or profile.get_str('name').lower() == name.lower(): + matches.add(userid) + + playerinfo = frontend.get_all_player_info(list(matches), allow_remote=True) + return { + 'results': playerinfo, + } + + +@jubeat_pages.route('/rivals/add', methods=['POST']) +@jsonify +@loginrequired +def addrival() -> Dict[str, Any]: + frontend = JubeatFrontend(g.data, g.config, g.cache) + version = int(request.get_json()['version']) + other_userid = UserID(int(request.get_json()['userid'])) + userid = g.userID + + # Add this rival link + profile = g.data.remote.user.get_profile(GameConstants.JUBEAT, version, other_userid) + if profile is None: + raise Exception('Unable to find profile for rival!') + + g.data.local.user.put_link( + GameConstants.JUBEAT, + version, + userid, + 'rival', + other_userid, + {}, + ) + + # Now return updated rival info + rivals, playerinfo = frontend.get_rivals(userid) + + return { + 'rivals': rivals, + 'players': playerinfo, + } + + +@jubeat_pages.route('/rivals/remove', methods=['POST']) +@jsonify +@loginrequired +def removerival() -> Dict[str, Any]: + frontend = JubeatFrontend(g.data, g.config, g.cache) + version = int(request.get_json()['version']) + other_userid = UserID(int(request.get_json()['userid'])) + userid = g.userID + + # Remove this rival link + g.data.local.user.destroy_link( + GameConstants.JUBEAT, + version, + userid, + 'rival', + other_userid, + ) + + # Now return updated rival info + rivals, playerinfo = frontend.get_rivals(userid) + + return { + 'rivals': rivals, + 'players': playerinfo, + } diff --git a/bemani/frontend/jubeat/jubeat.py b/bemani/frontend/jubeat/jubeat.py index 0e2f7f8..0b905d7 100644 --- a/bemani/frontend/jubeat/jubeat.py +++ b/bemani/frontend/jubeat/jubeat.py @@ -17,6 +17,8 @@ class JubeatFrontend(FrontendBase): JubeatBase.CHART_TYPE_EXTREME, ] + valid_rival_types = ['rival'] + def all_games(self) -> Iterator[Tuple[str, int, str]]: yield from JubeatFactory.all_games() diff --git a/bemani/frontend/static/controllers/jubeat/rivals.react.js b/bemani/frontend/static/controllers/jubeat/rivals.react.js new file mode 100644 index 0000000..f9746fd --- /dev/null +++ b/bemani/frontend/static/controllers/jubeat/rivals.react.js @@ -0,0 +1,295 @@ +/*** @jsx React.DOM */ + +var valid_versions = Object.keys(window.rivals); +var pagenav = new History(valid_versions); + +var rivals_view = React.createClass({ + + getInitialState: function(props) { + var profiles = Object.keys(window.rivals); + var version = pagenav.getInitialState(profiles[profiles.length - 1]); + return { + rivals: window.rivals, + players: window.players, + profiles: profiles, + version: version, + term: "", + results: {}, + searching: false, + offset: 0, + limit: 5, + }; + }, + + componentDidMount: function() { + pagenav.onChange(function(version) { + this.setState({version: version, offset: 0}); + }.bind(this)); + this.refreshRivals(); + }, + + refreshRivals: function() { + AJAX.get( + Link.get('refresh'), + function(response) { + this.setState({ + rivals: response.rivals, + players: response.players, + }); + setTimeout(this.refreshRivals, 5000); + }.bind(this) + ); + }, + + searchForPlayers: function(event) { + this.setState({searching: true}); + AJAX.post( + Link.get('search'), + { + version: this.state.version, + term: this.state.term, + }, + function(response) { + this.setState({ + results: response.results, + searching: false, + }); + }.bind(this) + ); + event.preventDefault(); + }, + + addRival: function(event, userid) { + AJAX.post( + Link.get('addrival'), + { + version: this.state.version, + userid: userid, + }, + function(response) { + this.setState({ + rivals: response.rivals, + players: response.players, + }); + }.bind(this) + ); + event.preventDefault(); + }, + + removeRival: function(event, userid) { + AJAX.post( + Link.get('removerival'), + { + version: this.state.version, + userid: userid, + }, + function(response) { + this.setState({ + rivals: response.rivals, + players: response.players, + }); + }.bind(this) + ); + event.preventDefault(); + }, + + addRivals: function(userid) { + if (userid == window.userid) { + return null; + } + + var avail = true; + var count = 0; + var current_rivals = this.state.rivals[this.state.version]; + for (var i = 0; i < current_rivals.length; i++) { + count++; + if (current_rivals[i].userid == userid) { + avail = false; + } + } + + if (count >= 3) { avail = false; } + + return ( + + {avail ? + : + null + } + + ); + }, + + render: function() { + if (this.state.rivals[this.state.version]) { + var rivals = this.state.rivals[this.state.version]; + var resultlength = 0; + Object.keys(this.state.results).map(function(userid, index) { + var player = this.state.results[userid][this.state.version]; + if (player) { resultlength++; } + }.bind(this)); + return ( +
+
+

Rivals

+ {this.state.profiles.map(function(version) { + return ( +
+
+
+ +
+ + + { this.state.searching ? + : + null + } +
+ {resultlength > 0 ? + + + + + + + + + { + Object.keys(this.state.results).map(function(userid, index) { + if (index < this.state.offset || index >= this.state.offset + this.state.limit) { + return null; + } + var player = this.state.results[userid][this.state.version]; + if (!player) { return null; } + + return ( + + + + + + ); + }.bind(this)) + } + + + + + +
NameJubeat ID
{ player.extid }{this.addRivals(userid)}
+ { this.state.offset > 0 ? + : null + } + { (this.state.offset + this.state.limit) < resultlength ? + = resultlength) { return; } + this.setState({offset: page}); + }.bind(this)}/> : null + } +
: +
No players match the specified search.
+ } +
+
+

Rivals

+ + + + + + + + + + {this.state.rivals[this.state.version].map(function(rival, index) { + var player = this.state.players[rival.userid][this.state.version]; + return ( + + + + + + ); + }.bind(this))} + +
NameJubeat ID
{ player.extid } + +
+
+
+ ); + } else { + return ( +
+
+ You have no profile for {window.versions[this.state.version]}! +
+
+ {this.state.profiles.map(function(version) { + return ( +
+
+ ); + } + }, +}); + +ReactDOM.render( + React.createElement(rivals_view, null), + document.getElementById('content') +);