more chessground UI

This commit is contained in:
Thibault Duplessis 2014-10-11 12:17:18 +02:00
parent 5b09390d99
commit a4244c3623
23 changed files with 258 additions and 183 deletions

View file

@ -80,19 +80,16 @@ object Round extends LilaController with TheftPrevention {
def watch(pov: Pov, userTv: Option[UserModel] = None)(implicit ctx: Context): Fu[Result] = negotiate(
html = ctx.userId.flatMap(pov.game.playerByUserId).ifTrue(pov.game.playable) match {
case Some(player) => fuccess(Redirect(routes.Round.player(pov.game fullIdOf player.color)))
case None => env.version(pov.gameId) zip
case None =>
(pov.game.tournamentId ?? TournamentRepo.byId) zip
Env.game.crosstableApi(pov.game) zip
(ctx.isAuth ?? {
Env.chat.api.userChat find s"${pov.gameId}/w" map (_.forUser(ctx.me).some)
}) map {
case (((v, tour), crosstable), chat) =>
Ok(html.round.watcher(pov, v, chat, tour, crosstable, userTv = userTv))
}
Env.game.crosstableApi(pov.game) zip
Env.api.roundApi.watcher(pov, Env.api.version, tv = false) map {
case ((tour, crosstable), data) =>
Ok(html.round.watcher(pov, data, tour, crosstable, userTv = userTv))
}
},
api = apiVersion => Env.round version pov.gameId map { v =>
Ok(env.jsonView.watcherJson(pov, v, tv = false, pref = ctx.pref))
})
api = apiVersion => Env.api.roundApi.watcher(pov, apiVersion, tv = false) map { Ok(_) }
)
private def join(pov: Pov)(implicit ctx: Context): Fu[Result] =
GameRepo initialFen pov.gameId zip

View file

@ -0,0 +1,49 @@
@()(implicit ctx: Context)
@Html(J.stringify(i18nJsObject(
trans.flipBoard,
trans.premoveEnabledClickAnywhereToCancel,
trans.aiNameLevelAiLevel,
trans.waiting,
trans.yourTurn,
trans.abortGame,
trans.proposeATakeback,
trans.offerDraw,
trans.resign,
trans.theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim,
trans.forceResignation,
trans.forceDraw,
trans.threefoldRepetition,
trans.claimADraw,
trans.drawOfferSent,
trans.cancel,
trans.yourOpponentOffersADraw,
trans.accept,
trans.decline,
trans.takebackPropositionSent,
trans.yourOpponentProposesATakeback,
trans.youHaveNbSecondsToMakeYourFirstMove,
trans.thisPlayerUsesChessComputerAssistance,
trans.gameAborted,
trans.checkmate,
trans.whiteResigned,
trans.blackResigned,
trans.stalemate,
trans.whiteLeftTheGame,
trans.blackLeftTheGame,
trans.draw,
trans.timeOut,
trans.premoveEnabledClickAnywhereToCancel,
trans.playingRightNow,
trans.whiteIsVictorious,
trans.blackIsVictorious,
trans.backToTournament,
trans.joinTheGame,
trans.playWithTheSameOpponentAgain,
trans.declineInvitation,
trans.rematch,
trans.rematchOfferSent,
trans.waitingForOpponent,
trans.cancelRematchOffer,
trans.newOpponent,
trans.playWithAnotherOpponent
)))

View file

@ -0,0 +1,10 @@
@()(implicit ctx: Context)
@helper.javascriptRouter("roundRoutes")(
routes.javascript.Lobby.home,
routes.javascript.Auth.signup,
routes.javascript.User.show,
routes.javascript.Tournament.show,
routes.javascript.Pool.show,
routes.javascript.Pool.leave,
routes.javascript.Round.watcher
)(ctx.req)

View file

@ -9,66 +9,13 @@
@jsAt(s"compiled/lichess.round${isProd??(".min")}.js")
*@
@jsAt(s"compiled/lichess.round.js")
@helper.javascriptRouter("roundRoutes")(
routes.javascript.Lobby.home,
routes.javascript.Auth.signup,
routes.javascript.User.show,
routes.javascript.Tournament.show,
routes.javascript.Pool.show,
routes.javascript.Pool.leave
)(ctx.req)
@jsRoutes()
@embedJs {
lichess = lichess || {};
lichess.round = {
data: @Html(play.api.libs.json.Json.stringify(data)),
routes: roundRoutes.controllers,
i18n: @Html(J.stringify(i18nJsObject(
trans.premoveEnabledClickAnywhereToCancel,
trans.aiNameLevelAiLevel,
trans.waiting,
trans.yourTurn,
trans.abortGame,
trans.proposeATakeback,
trans.offerDraw,
trans.resign,
trans.theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim,
trans.forceResignation,
trans.forceDraw,
trans.threefoldRepetition,
trans.claimADraw,
trans.drawOfferSent,
trans.cancel,
trans.yourOpponentOffersADraw,
trans.accept,
trans.decline,
trans.takebackPropositionSent,
trans.yourOpponentProposesATakeback,
trans.youHaveNbSecondsToMakeYourFirstMove,
trans.thisPlayerUsesChessComputerAssistance,
trans.gameAborted,
trans.checkmate,
trans.whiteResigned,
trans.blackResigned,
trans.stalemate,
trans.whiteLeftTheGame,
trans.blackLeftTheGame,
trans.draw,
trans.timeOut,
trans.premoveEnabledClickAnywhereToCancel,
trans.playingRightNow,
trans.whiteIsVictorious,
trans.blackIsVictorious,
trans.backToTournament,
trans.joinTheGame,
trans.playWithTheSameOpponentAgain,
trans.declineInvitation,
trans.rematch,
trans.rematchOfferSent,
trans.waitingForOpponent,
trans.cancelRematchOffer,
trans.newOpponent,
trans.playWithAnotherOpponent
)))
i18n: @jsI18n()
};
}
}

View file

@ -1,21 +1,39 @@
@(pov: Pov, version: Int, chat: Option[lila.chat.UserChat], tour: Option[lila.tournament.Tournament], cross: Option[lila.game.Crosstable], userTv: Option[User] = None)(implicit ctx: Context)
@(pov: Pov, data: play.api.libs.json.JsObject, tour: Option[lila.tournament.Tournament], cross: Option[lila.game.Crosstable], userTv: Option[User] = None)(implicit ctx: Context)
@import pov._
@title = @{ s"${playerText(pov.player)} vs ${playerText(pov.opponent)} in $gameId" }
@moreJs = {
@*
@jsAt(s"compiled/lichess.round${isProd??(".min")}.js")
*@
@jsAt(s"compiled/lichess.round.js")
@jsRoutes()
@embedJs {
lichess = lichess || {};
lichess.round = {
data: @Html(play.api.libs.json.Json.stringify(data)),
routes: roundRoutes.controllers,
i18n: @jsI18n()
};
}
}
@round.layout(
title = title,
side = views.html.game.side(pov, tour, withTourStanding = false, userTv = userTv),
chat = chat.map(c => base.chat(c, trans.spectatorRoom.str())),
chat = none, //chat.map(c => base.chat(c, trans.spectatorRoom.str())),
underchat = views.html.game.watchers().some,
moreJs = moreJs,
openGraph = povOpenGraph(pov)) {
@watcherGame(pov)
@*
@embedJs("var _ld_ = " + roundWatcherJsData(pov, version, false, ctx.pref))
<div class="underboard during_game">
<a class="button" data-icon="B" href="@routes.Round.watcher(gameId, (!color).name)"> @trans.flipBoard()</a>
</div>
@replayButton(pov)
*@
<div id="playing_crosstable">
@cross.map { c =>
@views.html.game.crosstable(c)

View file

@ -38,5 +38,7 @@ signedJs = routes.Round.signedJs(pov.gameId).toString.some) {
</div>
</div>
</div>
@*
@embedJs("var _ld_ = " + roundWatcherJsData(pov, version, false, ctx.pref))
*@
}

View file

@ -7,12 +7,13 @@
@if(bg == "dark") {
@cssTag("dark.css")
}
@cssTag("chessground.css")
@cssTag("common.css")
@cssTag("board.css")
<meta charset="utf-8">
</head>
<body
class="highlight embed-tv @bg @theme merida"
class="is2d highlight embed-tv @bg @theme merida"
style="width: 226px; height: 266px; overflow: hidden;"
data-stream-url="@routes.Tv.streamOut">
<div id="featured_game" title="lichess.org TV">
@ -20,6 +21,7 @@
@game.vstext(g)
</div>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
@jsTag("vendor/chessground.min.js")
@jsTagCompiled("common.js")
@jsTagCompiled("tv.js")
@base.ga()

View file

@ -53,5 +53,7 @@ moreCss = cssTag("tv.css")) {
@cross.map { c =>
@game.crosstable(pov.player.userId.fold(c)(c.fromPov))
}
@*
@embedJs("var _ld_ = " + roundWatcherJsData(pov, version, true, ctx.pref))
*@
}

View file

@ -23,4 +23,18 @@ private[api] final class RoundApi(jsonView: JsonView) {
)
}
}
def watcher(pov: Pov, apiVersion: Int, tv: Boolean)(implicit ctx: Context): Fu[JsObject] =
jsonView.watcherJson(pov, ctx.pref, apiVersion, ctx.me, tv) zip
(pov.game.tournamentId ?? TournamentRepo.byId) map {
case (json, tourOption) => tourOption.fold(json) { tour =>
json + (
"tournament" -> Json.obj(
"id" -> tour.id,
"name" -> tour.name,
"running" -> tour.isRunning
)
)
}
}
}

View file

@ -33,5 +33,5 @@ object PieceSet extends PieceSetObject {
object PieceSet3d extends PieceSetObject {
val all = NonEmptyList("Basic", "Glass", "Metal", "Wood", "Wax", "RedVBlue") map { name => new PieceSet(name) }
val all = NonEmptyList("Basic", "Glass", "Metal", "Wood", "RedVBlue", "Trimmed") map { name => new PieceSet(name) }
}

View file

@ -11,7 +11,7 @@ import lila.pref.Pref
import lila.user.{ User, UserRepo }
import chess.format.Forsyth
import chess.{ Color, Clock }
import chess.{ Color, Clock, Variant }
final class JsonView(
chatApi: lila.chat.ChatApi,
@ -20,6 +20,12 @@ final class JsonView(
canTakeback: Game => Fu[Boolean],
baseAnimationDuration: Duration) {
private def variantJson(v: Variant) = Json.obj(
"key" -> v.key,
"name" -> v.name,
"short" -> v.shortName,
"title" -> v.title)
def playerJson(
pov: Pov,
pref: Pref,
@ -28,17 +34,13 @@ final class JsonView(
getVersion(pov.game.id) zip
(pov.opponent.userId ?? UserRepo.byId) zip
canTakeback(pov.game) zip
getChat(pov.game, playerUser) map {
getPlayerChat(pov.game, playerUser) map {
case (((version, opponentUser), takebackable), chat) =>
import pov._
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
"variant" -> Json.obj(
"key" -> game.variant.key,
"name" -> game.variant.name,
"short" -> game.variant.shortName,
"title" -> game.variant.title),
"variant" -> variantJson(game.variant),
"speed" -> game.speed.key,
"perf" -> PerfPicker.key(game),
"rated" -> game.rated,
@ -108,49 +110,76 @@ final class JsonView(
"takebackable" -> takebackable)
}
def watcherJson(pov: Pov, version: Int, tv: Boolean, pref: Pref) = {
import pov._
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
"variant" -> game.variant.key,
"speed" -> game.speed.key,
"perf" -> PerfPicker.key(game),
"rated" -> game.rated,
"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),
"clock" -> game.clock.map(clockJson),
"player" -> Json.obj(
"color" -> color.name,
"version" -> version,
"spectator" -> true),
"opponent" -> Json.obj(
"color" -> opponent.color.name,
"ai" -> opponent.aiLevel),
"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" -> animationDuration(pov, pref),
"clockTenths" -> pref.clockTenths,
"clockBar" -> pref.clockBar
),
"possibleMoves" -> possibleMoves(pov),
"tv" -> tv
)
}
def watcherJson(
pov: Pov,
pref: Pref,
version: Int,
user: Option[User],
tv: Boolean) =
getWatcherChat(pov.game, user) zip
UserRepo.pair(pov.player.userId, pov.opponent.userId) map {
case (chat, (playerUser, opponentUser)) =>
import pov._
Json.obj(
"game" -> Json.obj(
"id" -> gameId,
"variant" -> variantJson(game.variant),
"variant" -> game.variant.key,
"speed" -> game.speed.key,
"perf" -> PerfPicker.key(game),
"rated" -> game.rated,
"fen" -> (Forsyth >> game.toChess),
"clock" -> game.hasClock,
"clockRunning" -> game.isClockRunning,
"player" -> game.turnColor.name,
"turns" -> game.turns,
"startedAtTurn" -> game.startedAtTurn,
"lastMove" -> game.castleLastMoveTime.lastMoveString,
"check" -> game.check.map(_.key),
"status" -> Json.obj(
"id" -> pov.game.status.id,
"name" -> pov.game.status.name)),
"clock" -> game.clock.map(clockJson),
"player" -> Json.obj(
"color" -> color.name,
"version" -> version,
"spectator" -> true,
"user" -> playerUser.map { userJsonView(_, true) }),
"opponent" -> Json.obj(
"color" -> opponent.color.name,
"ai" -> opponent.aiLevel,
"user" -> opponentUser.map { userJsonView(_, true) }),
"url" -> Json.obj(
"socket" -> s"/$gameId/${color.name}/socket",
"round" -> s"/$gameId/${color.name}"
),
"pref" -> Json.obj(
"animationDelay" -> animationDuration(pov, pref),
"highlight" -> pref.highlight,
"clockTenths" -> pref.clockTenths,
"clockBar" -> pref.clockBar,
"showCaptured" -> pref.captured
),
"tv" -> tv,
"chat" -> chat.map { c =>
JsArray(c.lines map {
case lila.chat.UserLine(username, text, _) => Json.obj(
"u" -> username,
"t" -> text)
})
}
)
}
private def getChat(game: Game, forUser: Option[User]) = game.hasChat optionFu {
chatApi.playerChat find game.id map (_ forUser forUser)
}
private def getPlayerChat(game: Game, forUser: Option[User]): Fu[Option[lila.chat.MixedChat]] =
game.hasChat optionFu {
chatApi.playerChat find game.id map (_ forUser forUser)
}
private def getWatcherChat(game: Game, forUser: Option[User]): Fu[Option[lila.chat.UserChat]] =
forUser ?? { user =>
chatApi.userChat find s"${game.id}/w" map (_ forUser user.some) map (_.some)
}
private def getUsers(game: Game) = UserRepo.pair(
game.whitePlayer.userId,

View file

@ -11,7 +11,4 @@ trait RoundHelper {
def hijackEnabled(game: Game) = game.rated && roundEnv.HijackEnabled
def moretimeSeconds = roundEnv.moretimeSeconds
def roundWatcherJsData(pov: Pov, version: Int, tv: Boolean, pref: Pref) =
roundEnv.jsonView.watcherJson(pov, version, tv, pref)
}

View file

@ -9,10 +9,13 @@ function parseFen($elem) {
var color = $this.data('color');
var ground = $this.data('chessground');
var config = {
viewOnly: true,
fen: $this.data('fen'),
lastMove: lm ? [lm[0] + lm[1], lm[2] + lm[3]] : [],
};
console.log(config);
if (color) config.orientation = color;
console.log(config);
if (ground) ground.set(config);
else $this.data('chessground', Chessground($this[0], config));
});

View file

@ -1,12 +1,12 @@
$(function() {
if (!window.EventSource) {
return;
}
var $featured = $('#featured_game');
var board = function() {
return $featured.find('> .mini_board');
};
parseFen(board());
if (!window.EventSource) {
return;
}
var source = new EventSource($('body').data('stream-url'));
source.addEventListener('message', function(e) {
var data = JSON.parse(e.data);

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit a63645fcb778b90dd1b7d0eb16babf2878d13364
Subproject commit afeb85a7f6fd23d6c57669ae2863fe78ff9fc0e8

View file

@ -729,55 +729,6 @@ body.is3d.Metal .queen.black {
body.is3d.Metal .king.black {
background-image: url(../staunton/Renders/Metal/Scaled/Black-King.png);
}
body.is3d.Wax .pawn.white {
background-image: url(../staunton/Renders/Wax/Scaled/White-Pawn.png);
}
body.is3d.Wax .bishop.white {
background-image: url(../staunton/Renders/Wax/Scaled/White-Bishop.png);
}
body.is3d.Wax .orientation-black .bishop.white {
background-image: url(../staunton/Renders/Wax/Scaled/White-Bishop-Flipped.png);
}
body.is3d.Wax .knight.white,
#top .is3d .cg-piece.Wax {
background-image: url(../staunton/Renders/Wax/Scaled/White-Knight.png);
}
body.is3d.Wax .orientation-black .knight.white {
background-image: url(../staunton/Renders/Wax/Scaled/White-Knight-Flipped.png);
}
body.is3d.Wax .rook.white {
background-image: url(../staunton/Renders/Wax/Scaled/White-Rook.png);
}
body.is3d.Wax .queen.white {
background-image: url(../staunton/Renders/Wax/Scaled/White-Queen.png);
}
body.is3d.Wax .king.white {
background-image: url(../staunton/Renders/Wax/Scaled/White-King.png);
}
body.is3d.Wax .pawn.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-Pawn.png);
}
body.is3d.Wax .bishop.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-Bishop.png);
}
body.is3d.Wax .orientation-white .bishop.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-Bishop-Flipped.png);
}
body.is3d.Wax .knight.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-Knight.png);
}
body.is3d.Wax .orientation-white .knight.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-Knight-Flipped.png);
}
body.is3d.Wax .rook.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-Rook.png);
}
body.is3d.Wax .queen.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-Queen.png);
}
body.is3d.Wax .king.black {
background-image: url(../staunton/Renders/Wax/Scaled/Black-King.png);
}
body.is3d.RedVBlue .pawn.white {
background-image: url(../staunton/Renders/RedVBlue/Scaled/White-Pawn.png);
}
@ -827,6 +778,55 @@ body.is3d.RedVBlue .queen.black {
body.is3d.RedVBlue .king.black {
background-image: url(../staunton/Renders/RedVBlue/Scaled/Black-King.png);
}
body.is3d.Trimmed .pawn.white {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-Pawn.png);
}
body.is3d.Trimmed .bishop.white {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-Bishop.png);
}
body.is3d.Trimmed .orientation-black .bishop.white {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-Bishop-Flipped.png);
}
body.is3d.Trimmed .knight.white,
#top .is3d .cg-piece.Trimmed {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-Knight.png);
}
body.is3d.Trimmed .orientation-black .knight.white {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-Knight-Flipped.png);
}
body.is3d.Trimmed .rook.white {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-Rook.png);
}
body.is3d.Trimmed .queen.white {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-Queen.png);
}
body.is3d.Trimmed .king.white {
background-image: url(../staunton/Renders/Trimmed/Scaled/White-King.png);
}
body.is3d.Trimmed .pawn.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-Pawn.png);
}
body.is3d.Trimmed .bishop.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-Bishop.png);
}
body.is3d.Trimmed .orientation-white .bishop.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-Bishop-Flipped.png);
}
body.is3d.Trimmed .knight.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-Knight.png);
}
body.is3d.Trimmed .orientation-white .knight.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-Knight-Flipped.png);
}
body.is3d.Trimmed .rook.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-Rook.png);
}
body.is3d.Trimmed .queen.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-Queen.png);
}
body.is3d.Trimmed .king.black {
background-image: url(../staunton/Renders/Trimmed/Scaled/Black-King.png);
}
div.lichess_overboard {
position: absolute;
z-index: 199;

View file

@ -1117,6 +1117,7 @@ body.is3d #themepicker div.color_demo {
#themepicker .is3d .no-square {
width: 72px;
height: 72px;
margin: 0px -3px -3px -3px;
}
#themepicker .is3d .cg-piece {
width: 100%;

View file

@ -31,7 +31,7 @@
},
"dependencies": {
"chess.js": "^0.1.0",
"chessground": "^1.5.3",
"chessground": "^1.5.5",
"lodash-node": "^2.4.1",
"mithril": "^0.1.22"
}

View file

@ -140,7 +140,7 @@ function renderPlayTable(ctrl) {
m('div.table_inner', [
m('div.lichess_current_player',
m('div.lichess_player.' + ctrl.chessground.data.turnColor, [
m('div.piece.king.' + ctrl.chessground.data.turnColor),
m('div.no-square', m('div.cg-piece.king.' + ctrl.chessground.data.turnColor)),
m('p', ctrl.trans(ctrl.chessground.data.turnColor == ctrl.data.puzzle.color ? 'yourTurn' : 'waiting'))
])
),

View file

@ -30,7 +30,7 @@
"watchify": "^1.0.2"
},
"dependencies": {
"chessground": "^1.5.3",
"chessground": "^1.5.5",
"lodash-node": "^2.4.1",
"mithril": "^0.1.22"
}

View file

@ -36,6 +36,7 @@ function make(data, fen, userMove) {
config.movable.events = {
after: userMove
};
config.viewOnly = data.player.spectator;
return new chessground.controller(config);
}

View file

@ -22,7 +22,6 @@ function renderMaterial(ctrl, material) {
module.exports = function(ctrl) {
var material = ctrl.data.pref.showCaptured ? chessground.board.getMaterialDiff(ctrl.chessground.data) : false;
console.log('render');
return m('div.lichess_game.cg-512', {
config: function(el, isUpdate, context) {
if (isUpdate) return;
@ -33,7 +32,11 @@ module.exports = function(ctrl) {
m('div.lichess_board_wrap', ctrl.data.blindMode ? null : [
m('div.lichess_board.' + ctrl.data.game.variant.key, chessground.view(ctrl.chessground)),
ctrl.chessground.data.premovable.current ? m('div#premove_alert', ctrl.trans('premoveEnabledClickAnywhereToCancel')) : null,
renderPromotion(ctrl)
renderPromotion(ctrl),
ctrl.data.player.spectator ? m('div.underboard',
m('a.button[data-icon=B]', {
href: ctrl.router.Round.watcher(ctrl.data.game.id, chessground.util.opposite(ctrl.data.player.color))
}, ctrl.trans('flipBoard'))) : null
]),
m('div.lichess_ground',
material ? renderMaterial(ctrl, material[ctrl.data.opponent.color]) : null,