normalize JSON API for use in mobile client

This commit is contained in:
Thibault Duplessis 2014-05-10 11:17:00 +02:00
parent 8b14db829c
commit c007cd23e6
9 changed files with 128 additions and 131 deletions

View file

@ -181,7 +181,9 @@ object Setup extends LilaController with TheftPrevention with play.api.http.Cont
config => op(config)(ctx) flatMap {
case (pov, call) => negotiate(
html = fuccess(redirectPov(pov, call)),
api = fuccess(Created(lila.api.JsonView.pov(pov)) as JSON)
api = Env.round version pov.gameId map { v =>
Created(Env.round.jsonView.playerJson(pov, v, ctx.pref)) as JSON
}
)
}
)

View file

@ -16,10 +16,7 @@ goodies = views.html.game.infoBox(pov, tour),
chat = chat.map(c => base.chat(c, trans.chatRoom.str())),
underchat = underchat.some,
signedJs = hijackEnabled(pov.game) option routes.Round.signedJs(pov.gameId) map (_.toString)) {
<div class="lichess_game clearfix lichess_player_@color not_spectator@if(!ctx.pref.captured) { hide_captured }"
data-socket-url="@routes.Round.websocketPlayer(fullId)"
data-table-url="@routes.Round.tablePlayer(fullId)"
data-end-url="@routes.Round.endPlayer(fullId)">
<div class="lichess_game clearfix lichess_player_@color not_spectator@if(!ctx.pref.captured) { hide_captured }">
<div class="lichess_board_wrap">
<div class="lichess_board with_marks">@Html(lila.app.ui.Board.render(pov))</div>
<div id="premove_alert">@trans.premoveEnabledClickAnywhereToCancel()</div>

View file

@ -2,10 +2,7 @@
@import pov._
<div class="lichess_game clearfix lichess_player_@color"
data-socket-url="@routes.Round.websocketWatcher(gameId, color.name)"
data-table-url="@routes.Round.tableWatcher(gameId, color.name)"
data-end-url="@routes.Round.endWatcher(gameId, color.name)">
<div class="lichess_game clearfix lichess_player_@color">
<div class="lichess_board_wrap">
<div class="lichess_board with_marks">@Html(lila.app.ui.Board.render(pov))</div>
<div id="dont_touch">@trans.youAreViewingThisGameAsASpectator()</div>

View file

@ -35,11 +35,11 @@ All websocket messages, sent or received, are composed of a type `t` and data `d
## Connect to a game as a player
```javascript
var playerId; // obtained from game creation API
var baseUrl; // obtained from game creation API (`url.socket`)
var clientId; // created by the client
var socketVersion = 0; // last message version number seen on this socket. Starts at zero.
var socketUrl = 'http://socket.en.l.org:9021/' + playerId + '/socket?sri=' + clientId + '&version=' + socketVersion;
var socketUrl = 'http://socket.en.l.org:9021' + baseUrl + '?sri=' + clientId + '&version=' + socketVersion;
var socket = new WebSocket(socketUrl);
```

View file

@ -1,23 +0,0 @@
package lila.api
import play.api.http.ContentTypes.JSON
import play.api.libs.json.{ JsObject, Json => J }
import play.api.mvc.Results.Ok
import chess.format.Forsyth
import lila.game.{ Game, Pov }
object JsonView {
def pov(p: Pov) = J.obj(
"game" -> game(p.game),
"player" -> player(p))
private def game(g: Game) = J.obj(
"id" -> g.id,
"fen" -> (Forsyth >> g.toChess))
private def player(p: Pov) = J.obj(
"id" -> p.fullId,
"color" -> p.color.name)
}

View file

@ -139,9 +139,10 @@ final class Env(
private lazy val reminder = new Reminder(db(CollectionReminder))
def nowPlaying = reminder.nowPlaying
private[round] def animationDelay = AnimationDelay
private[round] def moretimeSeconds = Moretime.toSeconds
lazy val jsonView = new JsonView(AnimationDelay)
{
import scala.concurrent.duration._

View file

@ -0,0 +1,101 @@
package lila.round
import scala.concurrent.duration._
import scala.math.{ min, max, round }
import play.api.libs.json.Json
import lila.common.PimpedJson._
import lila.game.{ Pov, Game }
import lila.pref.Pref
final class JsonView(baseAnimationDelay: Duration) {
def playerJson(pov: Pov, version: Int, pref: Pref) = {
import pov._
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
"started" -> game.started,
"finished" -> game.finishedOrAborted,
"clock" -> game.hasClock,
"clockRunning" -> game.isClockRunning,
"player" -> game.turnColor.name,
"turns" -> game.turns,
"startedAtTurn" -> game.startedAtTurn,
"lastMove" -> game.castleLastMoveTime.lastMoveString),
"player" -> Json.obj(
"id" -> playerId,
"color" -> player.color.name,
"version" -> version,
"spectator" -> false
),
"opponent" -> Json.obj(
"color" -> opponent.color.name,
"ai" -> opponent.isAi
),
"url" -> Json.obj(
"socket" -> s"/$fullId/socket",
"end" -> s"/$fullId/end",
"table" -> s"/$fullId/table"
),
"pref" -> Json.obj(
"animationDelay" -> animationDelay(pov),
"autoQueen" -> pref.autoQueen,
"autoThreefold" -> pref.autoThreefold,
"clockTenths" -> pref.clockTenths,
"clockBar" -> pref.clockBar,
"enablePremove" -> pref.premove
),
"possibleMoves" -> possibleMoves(pov),
"tournamentId" -> game.tournamentId
).noNull
}
def watcherJson(pov: Pov, version: Int, tv: Boolean, pref: Pref) = {
import pov._
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
"started" -> game.started,
"finished" -> game.finishedOrAborted,
"clock" -> game.hasClock,
"clockRunning" -> game.isClockRunning,
"player" -> game.turnColor.name,
"turns" -> game.turns,
"startedAtTurn" -> game.startedAtTurn,
"lastMove" -> game.castleLastMoveTime.lastMoveString),
"player" -> Json.obj(
"color" -> color.name,
"version" -> version,
"spectator" -> true),
"opponent" -> Json.obj(
"color" -> opponent.color.name,
"ai" -> opponent.isAi),
"url" -> Json.obj(
"socket" -> s"/$gameId/${color.name}/socket",
"end" -> s"/$gameId/${color.name}/end",
"table" -> s"/$gameId/${color.name}/table"
),
"pref" -> Json.obj(
"animationDelay" -> animationDelay(pov),
"clockTenths" -> pref.clockTenths,
"clockBar" -> pref.clockBar
),
"possibleMoves" -> possibleMoves(pov),
"tv" -> tv
).noNull
}
private def possibleMoves(pov: Pov) = (pov.game playableBy pov.player) option {
pov.game.toChess.situation.destinations map {
case (from, dests) => from.key -> dests.mkString
}
}
private def animationDelay(pov: Pov) = round {
baseAnimationDelay.toMillis * max(0, min(1.2,
((pov.game.estimateTotalTime - 60) / 60) * 0.2
))
}
}

View file

@ -1,14 +1,10 @@
package lila.round
import scala.math.{ min, max, round }
import play.api.libs.json.Json
import lila.game.Game
import lila.game.Pov
import lila.game.{ Pov, Game }
import lila.pref.Pref
import lila.round.Env.{ current => roundEnv }
import lila.user.UserContext
trait RoundHelper {
@ -16,80 +12,9 @@ trait RoundHelper {
def moretimeSeconds = roundEnv.moretimeSeconds
def gameAnimationDelay = roundEnv.animationDelay
def roundPlayerJsData(pov: Pov, version: Int, pref: Pref) =
roundEnv.jsonView.playerJson(pov, version, pref)
def roundPlayerJsData(pov: Pov, version: Int, pref: Pref)(implicit ctx: UserContext) = {
import pov._
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
"started" -> game.started,
"finished" -> game.finishedOrAborted,
"clock" -> game.hasClock,
"clockRunning" -> game.isClockRunning,
"player" -> game.turnColor.name,
"turns" -> game.turns,
"startedAtTurn" -> game.startedAtTurn,
"lastMove" -> game.castleLastMoveTime.lastMoveString),
"player" -> Json.obj(
"id" -> player.id,
"color" -> player.color.name,
"version" -> version,
"spectator" -> false
),
"opponent" -> Json.obj(
"color" -> opponent.color.name,
"ai" -> opponent.isAi
),
"possibleMoves" -> possibleMoves(pov),
"animationDelay" -> animationDelay(pov),
"autoQueen" -> pref.autoQueen,
"autoThreefold" -> pref.autoThreefold,
"clockTenths" -> pref.clockTenths,
"clockBar" -> pref.clockBar,
"enablePremove" -> pref.premove,
"tournamentId" -> game.tournamentId
)
}
def roundWatcherJsData(pov: Pov, version: Int, tv: Boolean, pref: Pref)(implicit ctx: UserContext) = {
import pov._
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
"started" -> game.started,
"finished" -> game.finishedOrAborted,
"clock" -> game.hasClock,
"clockRunning" -> game.isClockRunning,
"player" -> game.turnColor.name,
"turns" -> game.turns,
"startedAtTurn" -> game.startedAtTurn,
"lastMove" -> game.castleLastMoveTime.lastMoveString),
"player" -> Json.obj(
"color" -> player.color.name,
"version" -> version,
"spectator" -> true),
"opponent" -> Json.obj(
"color" -> opponent.color.name,
"ai" -> opponent.isAi),
"possibleMoves" -> possibleMoves(pov),
"animationDelay" -> animationDelay(pov),
"clockTenths" -> pref.clockTenths,
"clockBar" -> pref.clockBar,
"tv" -> tv
)
}
def possibleMoves(pov: Pov) = (pov.game playableBy pov.player) option {
pov.game.toChess.situation.destinations map {
case (from, dests) => from.key -> dests.mkString
}
}
private def animationDelay(pov: Pov) = round {
roundEnv.animationDelay.toMillis *
max(0, min(1.2,
((pov.game.estimateTotalTime - 60) / 60) * 0.2
))
}
def roundWatcherJsData(pov: Pov, version: Int, tv: Boolean, pref: Pref) =
roundEnv.jsonView.watcherJson(pov, version, tv, pref)
}

View file

@ -874,9 +874,6 @@ var storage = {
self.premove = null;
self.holdStart = null;
self.holds = [];
self.options.tableUrl = self.element.data('table-url');
self.options.endUrl = self.element.data('end-url');
self.options.socketUrl = self.element.data('socket-url');
startTournamentClock();
@ -941,7 +938,7 @@ var storage = {
});
lichess.socket = new strongSocket(
self.options.socketUrl,
self.options.url.socket,
self.options.player.version,
$.extend(true, lichess.socketDefaults, {
options: {
@ -1072,7 +1069,7 @@ var storage = {
});
},
premove: function() {
if (self.options.enablePremove) {
if (self.options.pref.enablePremove) {
self.element.queue(function() {
self.applyPremove();
self.element.dequeue();
@ -1193,8 +1190,8 @@ var storage = {
return $(this).clock('getSeconds');
}).get();
times.sort();
return this.options.animationDelay * Math.min(1, times[0] / 120);
} else return this.options.animationDelay;
return this.options.pref.animationDelay * Math.min(1, times[0] / 120);
} else return this.options.pref.animationDelay;
},
highlightLastMove: function(notation) {
var self = this;
@ -1243,7 +1240,7 @@ var storage = {
},
applyPremove: function() {
var self = this;
if (self.options.enablePremove && self.premove && self.isMyTurn()) {
if (self.options.pref.enablePremove && self.premove && self.isMyTurn()) {
var move = self.premove;
self.unsetPremove();
self.apiMove(move.from, move.to, true);
@ -1251,7 +1248,7 @@ var storage = {
},
setPremove: function(move) {
var self = this;
if (!self.options.enablePremove || self.isMyTurn()) return;
if (!self.options.pref.enablePremove || self.isMyTurn()) return;
self.unsetPremove();
if (!self.validMove(move.from, move.to, move.piece)) return;
self.premove = move;
@ -1311,7 +1308,7 @@ var storage = {
var color = self.options.player.color;
// promotion
if ($piece.hasClass('pawn') && ((color == "white" && squareId[1] == 8) || (color == "black" && squareId[1] == 1))) {
var aq = self.options.autoQueen;
var aq = self.options.pref.autoQueen;
if (aq == 3 || (isPremove && aq == 2)) {
moveData.promotion = "queen";
sendMoveRequest(moveData);
@ -1466,7 +1463,7 @@ var storage = {
},
reloadTable: function(callback) {
var self = this;
self.get(self.options.tableUrl, {
self.get(self.options.url.table, {
success: function(html) {
self.$tableInner.html(html);
self.initTable();
@ -1474,8 +1471,8 @@ var storage = {
$('body').trigger('lichess.content_loaded');
self.$tableInner.find('.lichess_claim_draw').each(function() {
var $link = $(this);
if (self.options.autoThreefold == 3) $link.click();
if (self.options.autoThreefold == 2) {
if (self.options.pref.autoThreefold == 3) $link.click();
if (self.options.pref.autoThreefold == 2) {
self.$table.find('.clock_bottom').each(function() {
if ($(this).clock('getSeconds') < 30) $link.click();
});
@ -1487,7 +1484,7 @@ var storage = {
},
loadEnd: function(callback) {
var self = this;
$.getJSON(self.options.endUrl, function(data) {
$.getJSON(self.options.url.end, function(data) {
$(['white', 'black']).each(function() {
if (data.players[this]) self.$table.find('div.username.' + this).html(data.players[this]);
});
@ -1534,8 +1531,8 @@ var storage = {
self.$table.find('div.clock').each(function() {
var $c = $(this);
$c.clock({
showTenths: self.options.clockTenths,
showBar: self.options.clockBar,
showTenths: self.options.pref.clockTenths,
showBar: self.options.pref.clockBar,
time: $c.data('time'),
barTime: $c.data('bar-time'),
emerg: $c.data('emerg'),