Add rival selection support to Jubeat in preparation for rival support.
This commit is contained in:
parent
ccdd219d67
commit
382ad49e6f
@ -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),
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
295
bemani/frontend/static/controllers/jubeat/rivals.react.js
Normal file
295
bemani/frontend/static/controllers/jubeat/rivals.react.js
Normal file
@ -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 (
|
||||
<span>
|
||||
{avail ?
|
||||
<Add
|
||||
title="Add Rival"
|
||||
onClick={function(event) {
|
||||
this.addRival(event, userid);
|
||||
}.bind(this)}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
|
||||
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 (
|
||||
<div>
|
||||
<div className="section">
|
||||
<h3>Rivals</h3>
|
||||
{this.state.profiles.map(function(version) {
|
||||
return (
|
||||
<Nav
|
||||
title={window.versions[version]}
|
||||
active={this.state.version == version}
|
||||
onClick={function(event) {
|
||||
if (this.state.version == version) { return; }
|
||||
this.setState({
|
||||
version: version,
|
||||
offset: 0,
|
||||
});
|
||||
pagenav.navigate(version);
|
||||
}.bind(this)}
|
||||
/>
|
||||
);
|
||||
}.bind(this))}
|
||||
</div>
|
||||
<div className="section">
|
||||
<form onSubmit={this.searchForPlayers}>
|
||||
<label for="search">Name or Jubeat ID:</label>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
className="inline"
|
||||
maxlength="9"
|
||||
value={this.state.term}
|
||||
onChange={function(event) {
|
||||
var value = event.target.value.toUpperCase();
|
||||
var intRegex = /^[-&\\.\\*A-Z0-9]*$/;
|
||||
// Normally, names are <= 8 characters, but we allow Jubeat IDs here too
|
||||
if (value.length <= 9 && intRegex.test(value)) {
|
||||
this.setState({term: value});
|
||||
}
|
||||
}.bind(this)}
|
||||
name="search"
|
||||
/>
|
||||
<input type="submit" value="search" />
|
||||
{ this.state.searching ?
|
||||
<img className="loading" src={Link.get('static', 'loading-16.gif')} /> :
|
||||
null
|
||||
}
|
||||
</form>
|
||||
{resultlength > 0 ?
|
||||
<table className="list players">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Jubeat ID</th>
|
||||
<th className="action"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{
|
||||
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 (
|
||||
<tr>
|
||||
<td><Rival userid={userid} player={player} /></td>
|
||||
<td>{ player.extid }</td>
|
||||
<td className="edit">{this.addRivals(userid)}</td>
|
||||
</tr>
|
||||
);
|
||||
}.bind(this))
|
||||
}</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
{ this.state.offset > 0 ?
|
||||
<Prev onClick={function(event) {
|
||||
var page = this.state.offset - this.state.limit;
|
||||
if (page < 0) { page = 0; }
|
||||
this.setState({offset: page});
|
||||
}.bind(this)}/> : null
|
||||
}
|
||||
{ (this.state.offset + this.state.limit) < resultlength ?
|
||||
<Next style={ {float: 'right'} } onClick={function(event) {
|
||||
var page = this.state.offset + this.state.limit;
|
||||
if (page >= resultlength) { return; }
|
||||
this.setState({offset: page});
|
||||
}.bind(this)}/> : null
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table> :
|
||||
<div className="placeholder">No players match the specified search.</div>
|
||||
}
|
||||
</div>
|
||||
<div className="section">
|
||||
<h3>Rivals</h3>
|
||||
<table className="list players">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Jubeat ID</th>
|
||||
<th className="action"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.state.rivals[this.state.version].map(function(rival, index) {
|
||||
var player = this.state.players[rival.userid][this.state.version];
|
||||
return (
|
||||
<tr>
|
||||
<td><Rival userid={rival.userid} player={player} /></td>
|
||||
<td>{ player.extid }</td>
|
||||
<td className="edit">
|
||||
<Delete
|
||||
title="Remove Rival"
|
||||
onClick={function(event) {
|
||||
this.removeRival(event, rival.userid);
|
||||
}.bind(this)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}.bind(this))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<div className="section">
|
||||
You have no profile for {window.versions[this.state.version]}!
|
||||
</div>
|
||||
<div className="section">
|
||||
{this.state.profiles.map(function(version) {
|
||||
return (
|
||||
<Nav
|
||||
title={window.versions[version]}
|
||||
active={this.state.version == version}
|
||||
onClick={function(event) {
|
||||
if (this.state.version == version) { return; }
|
||||
this.setState({version: version, offset: 0});
|
||||
pagenav.navigate(version);
|
||||
}.bind(this)}
|
||||
/>
|
||||
);
|
||||
}.bind(this))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(rivals_view, null),
|
||||
document.getElementById('content')
|
||||
);
|
Loading…
Reference in New Issue
Block a user