store practice progress
This commit is contained in:
parent
0eb0f1fb39
commit
6baae906ed
|
@ -44,6 +44,10 @@ object Practice extends LilaController {
|
|||
} map NoCache
|
||||
}
|
||||
|
||||
def complete(chapterId: String, nbMoves: Int) = Auth { implicit ctx => me =>
|
||||
env.api.progress.setNbMoves(me, chapterId, lila.practice.PracticeProgress.NbMoves(nbMoves))
|
||||
}
|
||||
|
||||
def config = Auth { implicit ctx => me =>
|
||||
for {
|
||||
struct <- env.api.structure.get
|
||||
|
|
|
@ -158,10 +158,11 @@ POST /patron/ipn controllers.Plan.payPalIpn
|
|||
GET /features controllers.Plan.features
|
||||
|
||||
# Practice
|
||||
GET /practice controllers.Practice.index
|
||||
GET /practice/:sectionId/:studySlug/:studyId controllers.Practice.show(sectionId: String, studySlug: String, studyId: String)
|
||||
GET /practice/config controllers.Practice.config
|
||||
POST /practice/config controllers.Practice.configSave
|
||||
GET /practice controllers.Practice.index
|
||||
GET /practice/:sectionId/:studySlug/:studyId controllers.Practice.show(sectionId: String, studySlug: String, studyId: String)
|
||||
POST /practice/complete/:chapterId/:moves controllers.Practice.complete(chapterId: String, moves: Int)
|
||||
GET /practice/config controllers.Practice.config
|
||||
POST /practice/config controllers.Practice.configSave
|
||||
|
||||
# Round
|
||||
GET /$gameId<\w{8}> controllers.Round.watcher(gameId: String, color: String = "white")
|
||||
|
|
|
@ -49,13 +49,15 @@ final class PracticeApi(
|
|||
|
||||
object progress {
|
||||
|
||||
import PracticeProgress.NbMoves
|
||||
|
||||
def get(user: User): Fu[PracticeProgress] =
|
||||
coll.uno[PracticeProgress]($id(user.id)) map { _ | PracticeProgress.empty(PracticeProgress.Id(user.id)) }
|
||||
|
||||
private def save(p: PracticeProgress): Funit =
|
||||
coll.update($id(p.id), p, upsert = true).void
|
||||
|
||||
def setNbMoves(user: User, chapterId: Chapter.Id, score: PracticeProgress.NbMoves) =
|
||||
def setNbMoves(user: User, chapterId: Chapter.Id, score: NbMoves) =
|
||||
get(user) flatMap { prog =>
|
||||
save(prog.withNbMoves(chapterId, score))
|
||||
}
|
||||
|
|
|
@ -10,13 +10,17 @@ case class PracticeProgress(
|
|||
createdAt: DateTime,
|
||||
updatedAt: DateTime) {
|
||||
|
||||
import PracticeProgress.NbMoves
|
||||
|
||||
def id = _id
|
||||
|
||||
def apply(chapterId: Chapter.Id): Option[PracticeProgress.NbMoves] =
|
||||
def apply(chapterId: Chapter.Id): Option[NbMoves] =
|
||||
chapters get chapterId
|
||||
|
||||
def withNbMoves(chapterId: Chapter.Id, nbMoves: PracticeProgress.NbMoves) = copy(
|
||||
chapters = chapters - chapterId + (chapterId -> nbMoves),
|
||||
chapters = chapters - chapterId + {
|
||||
chapterId -> NbMoves(math.min(chapters.get(chapterId).fold(999)(_.value), nbMoves.value))
|
||||
},
|
||||
updatedAt = DateTime.now)
|
||||
|
||||
def countDone(chapterIds: List[Chapter.Id]): Int =
|
||||
|
|
|
@ -115,6 +115,10 @@ module.exports = function(opts) {
|
|||
return this.data.orientation;
|
||||
}.bind(this);
|
||||
|
||||
this.turnColor = function() {
|
||||
return this.vm.node.ply % 2 === 0 ? 'white' : 'black';
|
||||
}.bind(this);
|
||||
|
||||
this.togglePlay = function(delay) {
|
||||
this.autoplay.toggle(delay);
|
||||
this.actionMenu.open = false;
|
||||
|
@ -130,7 +134,7 @@ module.exports = function(opts) {
|
|||
|
||||
var showGround = function() {
|
||||
var node = this.vm.node;
|
||||
var color = node.ply % 2 === 0 ? 'white' : 'black';
|
||||
var color = this.turnColor();
|
||||
var dests = readDests(node.dests);
|
||||
var drops = readDrops(node.drops);
|
||||
var movableColor = this.practice ? this.bottomColor() : (
|
||||
|
@ -227,6 +231,7 @@ module.exports = function(opts) {
|
|||
if (pathChanged) {
|
||||
if (this.retro) this.retro.onJump();
|
||||
if (this.practice) this.practice.onJump();
|
||||
if (this.study) this.study.onJump();
|
||||
}
|
||||
if (this.music) this.music.jump(this.vm.node);
|
||||
}.bind(this);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var ctrl = require('./ctrl');
|
||||
var view = require('./view');
|
||||
var studyView = require('./study/studyView');
|
||||
var practiceView = require('./study/practiceView');
|
||||
var studyPractice = require('./study/studyPractice');
|
||||
var legacy = require('./legacy');
|
||||
var m = require('mithril');
|
||||
|
||||
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
m.redraw.strategy("diff"); // prevents double full redraw on page load
|
||||
return controller.study;
|
||||
},
|
||||
view: controller.study.practice ? practiceView.main : studyView.main
|
||||
view: controller.study.practice ? studyPractice.view : studyView.main
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -56,12 +56,8 @@ module.exports = function(root) {
|
|||
};
|
||||
}
|
||||
|
||||
var turnColor = function() {
|
||||
return root.vm.node.ply % 2 === 0 ? 'white' : 'black';
|
||||
};
|
||||
|
||||
var isMyTurn = function() {
|
||||
return turnColor() === root.bottomColor();
|
||||
return root.turnColor() === root.bottomColor();
|
||||
};
|
||||
|
||||
var checkCeval = function() {
|
||||
|
@ -102,7 +98,6 @@ module.exports = function(root) {
|
|||
// because running(false) is called after the jump
|
||||
setTimeout(checkCeval, 50)
|
||||
},
|
||||
turnColor: turnColor,
|
||||
isMyTurn: isMyTurn,
|
||||
comment: comment,
|
||||
running: running,
|
||||
|
|
|
@ -61,8 +61,8 @@ function renderOffTrack(ctrl) {
|
|||
];
|
||||
}
|
||||
|
||||
function renderEnd(ctrl, end) {
|
||||
var color = ctrl.turnColor();
|
||||
function renderEnd(root, end) {
|
||||
var color = root.turnColor();
|
||||
if (end === 'checkmate') color = opposite(color);
|
||||
return m('div.player', [
|
||||
color ? m('div.no-square', m('piece.king.' + color)) : m('div.icon.off', '!'),
|
||||
|
@ -92,7 +92,7 @@ function renderRunning(root) {
|
|||
var ctrl = root.practice;
|
||||
var hint = ctrl.hinting();
|
||||
return m('div.player', [
|
||||
m('div.no-square', m('piece.king.' + ctrl.turnColor())),
|
||||
m('div.no-square', m('piece.king.' + root.turnColor())),
|
||||
m('div.instruction', [
|
||||
ctrl.isMyTurn() ? m('strong', 'Your move') : [
|
||||
m('strong', 'Computer thinking...'),
|
||||
|
@ -117,7 +117,7 @@ module.exports = function(root) {
|
|||
class: 'practice_box ' + (comment ? comment.verdict : '')
|
||||
}, [
|
||||
renderTitle(root.togglePractice),
|
||||
m('div.feedback', !running ? renderOffTrack(ctrl) : (end ? renderEnd(ctrl, end) : renderRunning(root))),
|
||||
m('div.feedback', !running ? renderOffTrack(ctrl) : (end ? renderEnd(root, end) : renderRunning(root))),
|
||||
ctrl.running() ? m('div.comment', comment ? [
|
||||
m('span.verdict', commentText[comment.verdict]),
|
||||
' ',
|
||||
|
|
|
@ -3,6 +3,7 @@ var partial = require('chessground').util.partial;
|
|||
var throttle = require('common').throttle;
|
||||
var memberCtrl = require('./studyMembers').ctrl;
|
||||
var chapterCtrl = require('./studyChapters').ctrl;
|
||||
var practiceCtrl = require('./studyPractice').ctrl;
|
||||
var commentFormCtrl = require('./commentForm').ctrl;
|
||||
var glyphFormCtrl = require('./studyGlyph').ctrl;
|
||||
var studyFormCtrl = require('./studyForm').ctrl;
|
||||
|
@ -14,7 +15,7 @@ var xhr = require('./studyXhr');
|
|||
|
||||
// data.position.path represents the server state
|
||||
// ctrl.vm.path is the client state
|
||||
module.exports = function(data, ctrl, tagTypes, practice) {
|
||||
module.exports = function(data, ctrl, tagTypes, practiceData) {
|
||||
|
||||
var send = ctrl.socket.send;
|
||||
|
||||
|
@ -26,12 +27,14 @@ module.exports = function(data, ctrl, tagTypes, practice) {
|
|||
loading: false,
|
||||
nextChapterId: false,
|
||||
tab: m.prop(data.chapters.length > 1 ? 'chapters' : 'members'),
|
||||
behind: (isManualChapter || practice) ? 0 : false, // false if syncing, else incremental number of missed event
|
||||
behind: (isManualChapter || practiceData) ? 0 : false, // false if syncing, else incremental number of missed event
|
||||
catchingUp: false, // was behind, is syncing back
|
||||
chapterId: isManualChapter ? data.chapter.id : null // only useful when not synchronized
|
||||
};
|
||||
})();
|
||||
|
||||
var practice = practiceData && practiceCtrl(ctrl, practiceData);
|
||||
|
||||
var notif = notifCtrl();
|
||||
var form = studyFormCtrl(function(data, isNew) {
|
||||
send("editStudy", data);
|
||||
|
@ -204,6 +207,7 @@ module.exports = function(data, ctrl, tagTypes, practice) {
|
|||
ctrl.tree.lastMainlineNode(path).ply <= data.chapter.conceal
|
||||
);
|
||||
},
|
||||
onJump: practice ? practice.onJump : function() {},
|
||||
withPosition: function(obj) {
|
||||
obj.chapterId = currentChapterId();
|
||||
obj.path = ctrl.vm.path;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
var m = require('mithril');
|
||||
var classSet = require('common').classSet;
|
||||
var xhr = require('./studyXhr');
|
||||
|
||||
var firstRender = true;
|
||||
|
||||
function selector(practice) {
|
||||
function selector(data) {
|
||||
if (!firstRender && m.redraw.strategy() === 'diff') return {
|
||||
subtree: 'retain'
|
||||
};
|
||||
|
@ -14,7 +15,7 @@ function selector(practice) {
|
|||
}
|
||||
}, [
|
||||
m('option[disabled][selected]', 'Practice list'),
|
||||
practice.structure.map(function(section) {
|
||||
data.structure.map(function(section) {
|
||||
return m('optgroup', {
|
||||
label: section.name
|
||||
}, section.studies.map(function(study) {
|
||||
|
@ -28,17 +29,38 @@ function selector(practice) {
|
|||
|
||||
module.exports = {
|
||||
|
||||
main: function(ctrl) {
|
||||
ctrl: function(root, data) {
|
||||
|
||||
var complete = function(chapterId, nbMoves) {
|
||||
xhr.practiceComplete(chapterId, nbMoves);
|
||||
data.completion[chapterId] = Math.min(data.completion[chapterId] || 999, nbMoves);
|
||||
};
|
||||
|
||||
var isVictory = function() {
|
||||
return root.gameOver() === 'checkmate' && root.turnColor() !== root.bottomColor();
|
||||
};
|
||||
|
||||
var onJump = function() {
|
||||
if (isVictory()) complete(root.study.currentChapter().id, root.vm.node.ply);
|
||||
};
|
||||
|
||||
return {
|
||||
onJump: onJump,
|
||||
data: data
|
||||
}
|
||||
},
|
||||
|
||||
view: function(ctrl) {
|
||||
|
||||
var current = ctrl.currentChapter();
|
||||
var practice = ctrl.practice;
|
||||
var data = ctrl.practice.data;
|
||||
|
||||
return [
|
||||
m('div.title', [
|
||||
m('i.practice.icon.' + practice.study.id),
|
||||
m('i.practice.icon.' + data.study.id),
|
||||
m('div.text', [
|
||||
m('h1', practice.study.name),
|
||||
m('em', practice.study.desc)
|
||||
m('h1', data.study.name),
|
||||
m('em', data.study.desc)
|
||||
])
|
||||
]),
|
||||
m('div', {
|
||||
|
@ -66,7 +88,7 @@ module.exports = {
|
|||
}, [
|
||||
m('span', {
|
||||
'data-icon': 'E',
|
||||
class: 'status ' + (practice.completion[chapter.id] ? 'done' : 'ongoing')
|
||||
class: 'status ' + (data.completion[chapter.id] ? 'done' : 'ongoing')
|
||||
}),
|
||||
m('h3', chapter.name)
|
||||
])
|
||||
|
@ -79,7 +101,7 @@ module.exports = {
|
|||
href: '/practice',
|
||||
title: 'More practice'
|
||||
}),
|
||||
selector(ctrl.practice)
|
||||
selector(data)
|
||||
])
|
||||
];
|
||||
}
|
|
@ -47,5 +47,14 @@ module.exports = {
|
|||
config: xhrConfig,
|
||||
background: true
|
||||
});
|
||||
},
|
||||
|
||||
practiceComplete: function(chapterId, nbMoves) {
|
||||
return m.request({
|
||||
method: 'POST',
|
||||
url: ['/practice/complete', chapterId, nbMoves].join('/'),
|
||||
config: xhrConfig,
|
||||
background: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue