more work on puzzle UI
This commit is contained in:
parent
6ca81f503b
commit
40066caf79
|
@ -138,14 +138,15 @@ object Puzzle extends LilaController {
|
||||||
resultInt => ctx.me match {
|
resultInt => ctx.me match {
|
||||||
case Some(me) =>
|
case Some(me) =>
|
||||||
lila.mon.puzzle.round.user()
|
lila.mon.puzzle.round.user()
|
||||||
env.finisher(puzzle, me, Result(resultInt == 1)) >> {
|
|
||||||
for {
|
for {
|
||||||
|
finished <- env.finisher(puzzle, me, Result(resultInt == 1))
|
||||||
|
(round, _) = finished
|
||||||
me2 <- UserRepo byId me.id map (_ | me)
|
me2 <- UserRepo byId me.id map (_ | me)
|
||||||
infos <- env userInfos me2
|
infos <- env userInfos me2
|
||||||
} yield Ok(Json.obj(
|
} yield Ok(Json.obj(
|
||||||
"user" -> lila.puzzle.JsonView.infos(false)(infos)
|
"user" -> lila.puzzle.JsonView.infos(false)(infos),
|
||||||
|
"round" -> lila.puzzle.JsonView.round(round)
|
||||||
))
|
))
|
||||||
}
|
|
||||||
case None =>
|
case None =>
|
||||||
lila.mon.puzzle.round.anon()
|
lila.mon.puzzle.round.anon()
|
||||||
env.finisher.incPuzzleAttempts(puzzle)
|
env.finisher.incPuzzleAttempts(puzzle)
|
||||||
|
|
|
@ -18,7 +18,7 @@ i18n: @jsI18n()
|
||||||
}
|
}
|
||||||
|
|
||||||
@side = {
|
@side = {
|
||||||
<div class="side_box puzzle_box"></div>
|
<div class="puzzle_side"></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@base.layout(
|
@base.layout(
|
||||||
|
|
|
@ -4,19 +4,20 @@ function fullMoveNumber(p) {
|
||||||
return Math.floor(1 + p.history.split(' ').length / 2);
|
return Math.floor(1 + p.history.split(' ').length / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeFenMoveNumber(fen, n) {
|
function changeFenMoveNumber(fen) {
|
||||||
parts = fen.split(' ');
|
parts = fen.split(' ');
|
||||||
parts[parts.length - 1] = n;
|
if (parts[1] === 'b') return fen;
|
||||||
|
parts[5] = parseInt(parts[5]) + 1;
|
||||||
return parts.join(' ');
|
return parts.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
puzzles.find({
|
puzzles.find({
|
||||||
|
// _id: 10107
|
||||||
"_id": {
|
"_id": {
|
||||||
"$lt": 60121
|
"$lt": 60121
|
||||||
}
|
}
|
||||||
}).forEach(function(p) {
|
}).forEach(function(p) {
|
||||||
var newMoveNumber = fullMoveNumber(p);
|
var newFen = changeFenMoveNumber(p.fen);
|
||||||
var newFen = changeFenMoveNumber(p.fen, newMoveNumber);
|
|
||||||
puzzles.update({
|
puzzles.update({
|
||||||
_id: p._id
|
_id: p._id
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -50,7 +50,6 @@ object Query {
|
||||||
|
|
||||||
def user(u: String): Bdoc = F.playerUids $eq u
|
def user(u: String): Bdoc = F.playerUids $eq u
|
||||||
def user(u: User): Bdoc = F.playerUids $eq u.id
|
def user(u: User): Bdoc = F.playerUids $eq u.id
|
||||||
def users(u: Seq[String]) = F.playerUids $in u
|
|
||||||
|
|
||||||
val noAi: Bdoc = $doc(
|
val noAi: Bdoc = $doc(
|
||||||
"p0.ai" $exists false,
|
"p0.ai" $exists false,
|
||||||
|
|
|
@ -30,7 +30,6 @@ private final class GameJson(
|
||||||
val anaDests = lastAnaDests(game, tree)
|
val anaDests = lastAnaDests(game, tree)
|
||||||
Json.obj(
|
Json.obj(
|
||||||
"id" -> game.id,
|
"id" -> game.id,
|
||||||
"speed" -> game.speed.key,
|
|
||||||
"clock" -> game.clock.map(_.show),
|
"clock" -> game.clock.map(_.show),
|
||||||
"perf" -> Json.obj(
|
"perf" -> Json.obj(
|
||||||
"icon" -> perfType.iconChar.toString,
|
"icon" -> perfType.iconChar.toString,
|
||||||
|
@ -43,11 +42,6 @@ private final class GameJson(
|
||||||
"color" -> p.color.name
|
"color" -> p.color.name
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
"winner" -> game.winnerColor.map(_.name),
|
|
||||||
"turns" -> game.turns,
|
|
||||||
"status" -> game.status,
|
|
||||||
"tournamentId" -> game.tournamentId,
|
|
||||||
"createdAt" -> game.createdAt,
|
|
||||||
"treeParts" -> partitionTreeJsonWriter.writes(tree),
|
"treeParts" -> partitionTreeJsonWriter.writes(tree),
|
||||||
"destsCache" -> Json.obj(
|
"destsCache" -> Json.obj(
|
||||||
anaDests.path -> anaDests.dests
|
anaDests.path -> anaDests.dests
|
||||||
|
|
|
@ -58,12 +58,7 @@ final class JsonView(gameJson: GameJson) {
|
||||||
"duration" -> pref.animationFactor * animationDuration.toMillis
|
"duration" -> pref.animationFactor * animationDuration.toMillis
|
||||||
),
|
),
|
||||||
"mode" -> mode,
|
"mode" -> mode,
|
||||||
"round" -> round.map { a =>
|
"round" -> round.map(JsonView.round),
|
||||||
Json.obj(
|
|
||||||
"ratingDiff" -> a.ratingDiff,
|
|
||||||
"win" -> a.result.win
|
|
||||||
)
|
|
||||||
},
|
|
||||||
"attempt" -> round.ifTrue(isMobileApi).map { r =>
|
"attempt" -> round.ifTrue(isMobileApi).map { r =>
|
||||||
Json.obj(
|
Json.obj(
|
||||||
"userRatingDiff" -> r.ratingDiff,
|
"userRatingDiff" -> r.ratingDiff,
|
||||||
|
@ -86,9 +81,9 @@ final class JsonView(gameJson: GameJson) {
|
||||||
|
|
||||||
private def makeBranch(puzzle: Puzzle): Option[tree.Branch] = {
|
private def makeBranch(puzzle: Puzzle): Option[tree.Branch] = {
|
||||||
import chess.format._
|
import chess.format._
|
||||||
val solution: List[Uci.Move] = Line solution puzzle.lines map { uci =>
|
val solution: List[Uci.Move] = (Line solution puzzle.lines).map { uci =>
|
||||||
Uci.Move(uci) err s"Invalid puzzle solution UCI $uci"
|
Uci.Move(uci) err s"Invalid puzzle solution UCI $uci"
|
||||||
}
|
}.init
|
||||||
val init = chess.Game(none, puzzle.fenAfterInitialMove).withTurns(puzzle.initialPly)
|
val init = chess.Game(none, puzzle.fenAfterInitialMove).withTurns(puzzle.initialPly)
|
||||||
val (_, branchList) = solution.foldLeft[(chess.Game, List[tree.Branch])]((init, Nil)) {
|
val (_, branchList) = solution.foldLeft[(chess.Game, List[tree.Branch])]((init, Nil)) {
|
||||||
case ((prev, branches), uci) =>
|
case ((prev, branches), uci) =>
|
||||||
|
@ -118,4 +113,9 @@ object JsonView {
|
||||||
Json.arr(r.puzzleId, r.ratingDiff, r.rating)
|
Json.arr(r.puzzleId, r.ratingDiff, r.rating)
|
||||||
}
|
}
|
||||||
).noNull
|
).noNull
|
||||||
|
|
||||||
|
def round(r: Round): JsObject = Json.obj(
|
||||||
|
"ratingDiff" -> r.ratingDiff,
|
||||||
|
"win" -> r.result.win
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,10 @@ case class Puzzle(
|
||||||
attempts: Int,
|
attempts: Int,
|
||||||
mate: Boolean) {
|
mate: Boolean) {
|
||||||
|
|
||||||
|
// ply after "initial move" when we start solving
|
||||||
def initialPly: Int = {
|
def initialPly: Int = {
|
||||||
fen.split(' ').lastOption flatMap parseIntOption map { move =>
|
fen.split(' ').lastOption flatMap parseIntOption map { move =>
|
||||||
move * 2 - color.fold(2, 1)
|
move * 2 - color.fold(0, 1)
|
||||||
}
|
}
|
||||||
} | 0
|
} | 0
|
||||||
|
|
||||||
|
|
|
@ -1593,7 +1593,7 @@ lichess.notifyApp = (function() {
|
||||||
function startPuzzle(cfg) {
|
function startPuzzle(cfg) {
|
||||||
var puzzle;
|
var puzzle;
|
||||||
cfg.element = document.querySelector('#puzzle');
|
cfg.element = document.querySelector('#puzzle');
|
||||||
cfg.sideElement = document.querySelector('#site_header .puzzle_box');
|
cfg.sideElement = document.querySelector('#site_header .puzzle_side');
|
||||||
lichess.socket = lichess.StrongSocket('/socket', 0, {
|
lichess.socket = lichess.StrongSocket('/socket', 0, {
|
||||||
options: {
|
options: {
|
||||||
name: "puzzle"
|
name: "puzzle"
|
||||||
|
|
|
@ -39,6 +39,12 @@
|
||||||
line-height: 64px;
|
line-height: 64px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
#puzzle .feedback.win .icon {
|
||||||
|
color: #759900;
|
||||||
|
}
|
||||||
|
#puzzle .feedback.fail .icon {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
#puzzle .feedback .instruction > * {
|
#puzzle .feedback .instruction > * {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +66,16 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#puzzle .after .continue {
|
||||||
|
display: flex;
|
||||||
|
height: 50%;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 1.3em;
|
||||||
|
background: #3893E8;
|
||||||
|
color: #fff;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
#puzzle move {
|
#puzzle move {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
@ -119,39 +135,52 @@ body.dark #puzzle .timeline > * {
|
||||||
color: #444;
|
color: #444;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
.puzzle_box {
|
.puzzle_side .side_box.metas {
|
||||||
padding: 0 7px;
|
padding: 0 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.puzzle_box .game_infos {
|
.puzzle_side .game_infos {
|
||||||
margin-top: 14px!important;
|
margin-top: 14px!important;
|
||||||
padding-bottom: 14px!important;
|
padding-bottom: 14px!important;
|
||||||
}
|
}
|
||||||
.puzzle_box .header a {
|
.puzzle_side .header a {
|
||||||
color: #3893E8;
|
color: #3893E8;
|
||||||
}
|
}
|
||||||
.puzzle_box .game_infos.puzzle {
|
.puzzle_side .game_infos.puzzle {
|
||||||
padding-left: 56px;
|
padding-left: 56px;
|
||||||
}
|
}
|
||||||
.puzzle_box .game_infos.puzzle::before {
|
.puzzle_side .game_infos.puzzle::before {
|
||||||
font-size: 47px;
|
font-size: 47px;
|
||||||
}
|
}
|
||||||
.puzzle_box .puzzle .header a {
|
.puzzle_side .puzzle .header a {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.puzzle_box .game_infos.game {
|
.puzzle_side .game_infos.game {
|
||||||
border: none;
|
border: none;
|
||||||
padding-bottom: 0!important;
|
padding-bottom: 0!important;
|
||||||
}
|
}
|
||||||
.puzzle_box .players {
|
.puzzle_side .players {
|
||||||
padding-bottom: 14px;
|
padding-bottom: 14px;
|
||||||
}
|
}
|
||||||
.puzzle_box .players .color-icon::before {
|
.puzzle_side .players .color-icon::before {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.puzzle_side .rating h2 {
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
|
.puzzle_side .rating span.rp {
|
||||||
|
/* font-size: 1.2em; */
|
||||||
|
}
|
||||||
|
.puzzle_side .rating span.rp.up {
|
||||||
|
color: #759900;
|
||||||
|
}
|
||||||
|
.puzzle_side .rating span.rp.down {
|
||||||
|
color: #ac524f;
|
||||||
|
}
|
||||||
|
|
||||||
body.dark #puzzle .timeline a.new {
|
body.dark #puzzle .timeline a.new {
|
||||||
background: #555;
|
background: #555;
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
|
|
|
@ -30,10 +30,12 @@ module.exports = function(opts, i18n) {
|
||||||
|
|
||||||
var initiate = function(fromData) {
|
var initiate = function(fromData) {
|
||||||
data = fromData;
|
data = fromData;
|
||||||
|
console.log(data);
|
||||||
tree = treeBuild(treeOps.reconstruct(data.game.treeParts));
|
tree = treeBuild(treeOps.reconstruct(data.game.treeParts));
|
||||||
var initialPath = treePath.fromNodeList(treeOps.mainlineNodeList(tree.root));
|
var initialPath = treePath.fromNodeList(treeOps.mainlineNodeList(tree.root));
|
||||||
vm.mode = 'play'; // play | try | view
|
vm.mode = 'play'; // play | try | view
|
||||||
vm.loading = false;
|
vm.loading = false;
|
||||||
|
vm.round = null;
|
||||||
vm.justPlayed = null;
|
vm.justPlayed = null;
|
||||||
vm.resultSent = false;
|
vm.resultSent = false;
|
||||||
vm.lastFeedback = 'init';
|
vm.lastFeedback = 'init';
|
||||||
|
@ -50,13 +52,17 @@ module.exports = function(opts, i18n) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
vm.canViewSolution = true;
|
vm.canViewSolution = true;
|
||||||
m.redraw();
|
m.redraw();
|
||||||
}, 5000);
|
// }, 5000);
|
||||||
|
}, 50);
|
||||||
|
|
||||||
socket.setDestsCache(data.game.destsCache);
|
socket.setDestsCache(data.game.destsCache);
|
||||||
moveTest = moveTestBuild(vm, data.puzzle);
|
moveTest = moveTestBuild(vm, data.puzzle);
|
||||||
|
|
||||||
showGround();
|
showGround();
|
||||||
m.redraw();
|
m.redraw();
|
||||||
|
|
||||||
|
if (window.history.pushState)
|
||||||
|
window.history.replaceState(null, null, '/training/' + data.puzzle.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
var showGround = function() {
|
var showGround = function() {
|
||||||
|
@ -72,6 +78,7 @@ module.exports = function(opts, i18n) {
|
||||||
};
|
};
|
||||||
var config = {
|
var config = {
|
||||||
fen: node.fen,
|
fen: node.fen,
|
||||||
|
orientation: data.puzzle.color,
|
||||||
turnColor: color,
|
turnColor: color,
|
||||||
movable: movable,
|
movable: movable,
|
||||||
premovable: {
|
premovable: {
|
||||||
|
@ -91,8 +98,6 @@ module.exports = function(opts, i18n) {
|
||||||
config.movable.color = data.puzzle.color;
|
config.movable.color = data.puzzle.color;
|
||||||
config.premovable.enabled = true;
|
config.premovable.enabled = true;
|
||||||
}
|
}
|
||||||
console.log(dests, config);
|
|
||||||
vm.cgConfig = config;
|
|
||||||
if (!ground) ground = groundBuild(data, config, userMove);
|
if (!ground) ground = groundBuild(data, config, userMove);
|
||||||
ground.set(config);
|
ground.set(config);
|
||||||
if (!dests) getDests();
|
if (!dests) getDests();
|
||||||
|
@ -145,9 +150,7 @@ module.exports = function(opts, i18n) {
|
||||||
ground.playPremove();
|
ground.playPremove();
|
||||||
|
|
||||||
var progress = moveTest();
|
var progress = moveTest();
|
||||||
// console.log(progress, vm.node);
|
|
||||||
if (progress) applyProgress(progress);
|
if (progress) applyProgress(progress);
|
||||||
// preparePremoving();
|
|
||||||
m.redraw();
|
m.redraw();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,7 +196,6 @@ module.exports = function(opts, i18n) {
|
||||||
}
|
}
|
||||||
} else if (progress && progress.orig) {
|
} else if (progress && progress.orig) {
|
||||||
vm.lastFeedback = 'good';
|
vm.lastFeedback = 'good';
|
||||||
// console.log(tree);
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
socket.sendAnaMove(progress);
|
socket.sendAnaMove(progress);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
@ -206,7 +208,9 @@ module.exports = function(opts, i18n) {
|
||||||
vm.loading = true;
|
vm.loading = true;
|
||||||
xhr.round(data.puzzle.id, win).then(function(res) {
|
xhr.round(data.puzzle.id, win).then(function(res) {
|
||||||
data.user = res.user;
|
data.user = res.user;
|
||||||
|
vm.round = res.round;
|
||||||
vm.loading = false;
|
vm.loading = false;
|
||||||
|
m.redraw();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -214,6 +218,7 @@ module.exports = function(opts, i18n) {
|
||||||
vm.loading = true;
|
vm.loading = true;
|
||||||
xhr.nextPuzzle().then(function(d) {
|
xhr.nextPuzzle().then(function(d) {
|
||||||
// pushState(cfg);
|
// pushState(cfg);
|
||||||
|
vm.round = null;
|
||||||
vm.loading = false;
|
vm.loading = false;
|
||||||
initiate(d);
|
initiate(d);
|
||||||
});
|
});
|
||||||
|
@ -310,6 +315,12 @@ module.exports = function(opts, i18n) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var recentHash = function() {
|
||||||
|
return data.user ? data.user.recent.reduce(function(h, r) {
|
||||||
|
return h + r[0];
|
||||||
|
}, '') : '';
|
||||||
|
};
|
||||||
|
|
||||||
initiate(opts.data);
|
initiate(opts.data);
|
||||||
|
|
||||||
keyboard.bind({
|
keyboard.bind({
|
||||||
|
@ -317,8 +328,6 @@ module.exports = function(opts, i18n) {
|
||||||
userJump: userJump
|
userJump: userJump
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
vm: vm,
|
vm: vm,
|
||||||
getData: function() {
|
getData: function() {
|
||||||
|
@ -331,6 +340,7 @@ module.exports = function(opts, i18n) {
|
||||||
userJump: userJump,
|
userJump: userJump,
|
||||||
viewSolution: viewSolution,
|
viewSolution: viewSolution,
|
||||||
nextPuzzle: nextPuzzle,
|
nextPuzzle: nextPuzzle,
|
||||||
|
recentHash: recentHash,
|
||||||
trans: lichess.trans(opts.i18n),
|
trans: lichess.trans(opts.i18n),
|
||||||
socketReceive: socket.receive
|
socketReceive: socket.receive
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ var treeOps = require('tree').ops;
|
||||||
module.exports = function(tree, initialNode, solution, color) {
|
module.exports = function(tree, initialNode, solution, color) {
|
||||||
|
|
||||||
tree.ops.updateAll(solution, function(node) {
|
tree.ops.updateAll(solution, function(node) {
|
||||||
if ((color === 'white') === (node.ply % 2 === 0)) node.puzzle = 'good';
|
if ((color === 'white') === (node.ply % 2 === 1)) node.puzzle = 'good';
|
||||||
});
|
});
|
||||||
|
|
||||||
var solutionNode = treeOps.childById(initialNode, solution.id);
|
var solutionNode = treeOps.childById(initialNode, solution.id);
|
||||||
|
|
|
@ -24,7 +24,8 @@ var m = require('mithril');
|
||||||
// }
|
// }
|
||||||
|
|
||||||
module.exports = function(ctrl) {
|
module.exports = function(ctrl) {
|
||||||
return m('div.feedback.view', [
|
var data = ctrl.getData();
|
||||||
|
return m('div.feedback.after', [
|
||||||
// (!ctrl.hasEverVoted.get() && ctrl.data.puzzle.enabled && ctrl.data.voted === null) ? m('div.please_vote', [
|
// (!ctrl.hasEverVoted.get() && ctrl.data.puzzle.enabled && ctrl.data.voted === null) ? m('div.please_vote', [
|
||||||
// m('p.first', [
|
// m('p.first', [
|
||||||
// m('strong', ctrl.trans('wasThisPuzzleAnyGood')),
|
// m('strong', ctrl.trans('wasThisPuzzleAnyGood')),
|
||||||
|
@ -36,8 +37,11 @@ module.exports = function(ctrl) {
|
||||||
// )
|
// )
|
||||||
// ]) : null,
|
// ]) : null,
|
||||||
// (ctrl.data.puzzle.enabled && ctrl.data.user) ? renderVote(ctrl) : null,
|
// (ctrl.data.puzzle.enabled && ctrl.data.user) ? renderVote(ctrl) : null,
|
||||||
m('a.continue.button.text[data-icon=G]', {
|
m('a.continue', {
|
||||||
onclick: ctrl.nextPuzzle
|
onclick: ctrl.nextPuzzle
|
||||||
}, ctrl.trans.noarg('continueTraining'))
|
}, [
|
||||||
|
m('i[data-icon=G]'),
|
||||||
|
ctrl.trans.noarg('continueTraining')
|
||||||
|
])
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
var m = require('mithril');
|
var m = require('mithril');
|
||||||
|
|
||||||
var historySize = 15;
|
var historySize = 15;
|
||||||
|
var recentHash = '';
|
||||||
|
|
||||||
module.exports = function(data) {
|
module.exports = function(ctrl) {
|
||||||
|
var hash = ctrl.recentHash();
|
||||||
|
if (hash === recentHash) return {subtree: 'retain'};
|
||||||
|
recentHash = hash;
|
||||||
|
var data = ctrl.getData();
|
||||||
if (!data.user) return;
|
if (!data.user) return;
|
||||||
var slots = [];
|
var slots = [];
|
||||||
for (var i = 0; i < historySize; i++) slots[i] = data.user.recent[i] || null;
|
for (var i = 0; i < historySize; i++) slots[i] = data.user.recent[i] || null;
|
||||||
|
|
|
@ -117,7 +117,7 @@ module.exports = function(ctrl) {
|
||||||
]),
|
]),
|
||||||
m('div.underboard', [
|
m('div.underboard', [
|
||||||
m('div.center', [
|
m('div.center', [
|
||||||
historyView(ctrl.getData())
|
historyView(ctrl)
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
];
|
];
|
||||||
|
|
|
@ -5,13 +5,16 @@ function strong(txt) {
|
||||||
return '<strong>' + txt + '</strong>';
|
return '<strong>' + txt + '</strong>';
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(ctrl) {
|
function puzzleBox(ctrl) {
|
||||||
|
var data = ctrl.getData();
|
||||||
|
return m('div.side_box.metas', [
|
||||||
|
puzzleInfos(ctrl, data.puzzle),
|
||||||
|
gameInfos(ctrl, data.game, data.puzzle)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
var puzzle = ctrl.getData().puzzle;
|
function puzzleInfos(ctrl, puzzle) {
|
||||||
var game = ctrl.getData().game;
|
return m('div.game_infos.puzzle[data-icon="-"]', [
|
||||||
|
|
||||||
return [
|
|
||||||
m('div.game_infos.puzzle[data-icon="-"]', [
|
|
||||||
m('div.header', [
|
m('div.header', [
|
||||||
m('a.title', {
|
m('a.title', {
|
||||||
href: '/training/' + puzzle.id
|
href: '/training/' + puzzle.id
|
||||||
|
@ -21,14 +24,18 @@ module.exports = function(ctrl) {
|
||||||
m('p', m.trust(ctrl.trans('playedXTimes',
|
m('p', m.trust(ctrl.trans('playedXTimes',
|
||||||
strong(lichess.numberFormat(puzzle.attempts)))))
|
strong(lichess.numberFormat(puzzle.attempts)))))
|
||||||
])
|
])
|
||||||
]),
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameInfos(ctrl, game, puzzle) {
|
||||||
|
return [
|
||||||
m('div.game_infos.game[data-icon="-"]', {
|
m('div.game_infos.game[data-icon="-"]', {
|
||||||
'data-icon': game.perf.icon
|
'data-icon': game.perf.icon
|
||||||
}, [
|
}, [
|
||||||
m('div.header', [
|
m('div.header', [
|
||||||
'From game ',
|
'From game ',
|
||||||
ctrl.vm.mode === 'view' ? m('a.title', {
|
ctrl.vm.mode === 'view' ? m('a.title', {
|
||||||
href: '/' + game.id
|
href: '/' + game.id + '/' + puzzle.color + '#' + puzzle.initialPly
|
||||||
}, '#' + game.id) : '#' + game.id.slice(0, 5) + '...',
|
}, '#' + game.id) : '#' + game.id.slice(0, 5) + '...',
|
||||||
m('p', [
|
m('p', [
|
||||||
game.clock, ' • ',
|
game.clock, ' • ',
|
||||||
|
@ -45,4 +52,53 @@ module.exports = function(ctrl) {
|
||||||
);
|
);
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function userBox(ctrl) {
|
||||||
|
var data = ctrl.getData();
|
||||||
|
if (!data.user) return;
|
||||||
|
var ratingHtml = data.user.rating;
|
||||||
|
if (ctrl.vm.round) {
|
||||||
|
var diff = ctrl.vm.round.ratingDiff,
|
||||||
|
klass = '';
|
||||||
|
if (diff >= 0) {
|
||||||
|
diff = '+' + diff;
|
||||||
|
klass = 'up';
|
||||||
|
} else if (diff === 0) diff = '+0';
|
||||||
|
else klass = 'down';
|
||||||
|
ratingHtml += ' <span class="rp ' + klass + '">' + diff + '</span>';
|
||||||
|
}
|
||||||
|
return m('div.side_box.rating', [
|
||||||
|
m('h2', m.trust(ctrl.trans('yourPuzzleRatingX', strong(ratingHtml)))),
|
||||||
|
m('div', ratingChart(ctrl))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ratingChart(ctrl) {
|
||||||
|
return m('div.rating_chart', {
|
||||||
|
config: function(el, isUpdate, ctx) {
|
||||||
|
var hash = ctrl.recentHash();
|
||||||
|
if (hash == ctx.hash) return;
|
||||||
|
ctx.hash = hash;
|
||||||
|
var dark = document.body.classList.contains('dark');
|
||||||
|
var points = ctrl.getData().user.recent.map(function(r) {
|
||||||
|
return r[2] + r[1];
|
||||||
|
});
|
||||||
|
jQuery(el).sparkline(points, {
|
||||||
|
type: 'line',
|
||||||
|
width: '213px',
|
||||||
|
height: '80px',
|
||||||
|
lineColor: dark ? '#4444ff' : '#0000ff',
|
||||||
|
fillColor: dark ? '#222255' : '#ccccff'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function(ctrl) {
|
||||||
|
|
||||||
|
return [
|
||||||
|
puzzleBox(ctrl),
|
||||||
|
userBox(ctrl)
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue