study: persist node promotion and deletion

pull/1834/head
Thibault Duplessis 2016-04-20 11:33:59 +07:00
parent f712920ff4
commit 7dbe5dc2ae
7 changed files with 69 additions and 6 deletions

View File

@ -25,7 +25,21 @@ object Node {
def addNodeAt(node: Node, path: Path): Children = path.split match {
case None if has(node.id) => this
case None => copy(nodes = nodes :+ node)
case Some((head, tail)) => updateWith(head, _.withChildren(_.addNodeAt(node, tail)))
case Some((head, tail)) => updateChildren(head, _.addNodeAt(node, tail))
}
def deleteNodeAt(path: Path): Children = path.split match {
case None => this
case Some((head, Path(Nil))) => copy(nodes = nodes.filterNot(_.id == head))
case Some((head, tail)) => updateChildren(head, _.deleteNodeAt(tail))
}
def promoteNodeAt(path: Path): Children = path.split match {
case None => this
case Some((head, Path(Nil))) => get(head).fold(this) { node =>
copy(nodes = node +: nodes.filterNot(node ==))
}
case Some((head, tail)) => updateChildren(head, _.promoteNodeAt(tail))
}
def get(id: UciCharPair): Option[Node] = nodes.find(_.id == id)
@ -37,6 +51,9 @@ object Node {
case Some(child) => update(op(child))
}
def updateChildren(id: UciCharPair, f: Children => Children): Children =
updateWith(id, _ withChildren f)
def update(child: Node) = copy(
nodes = nodes.map {
case n if child.id == n.id => child

View File

@ -31,6 +31,15 @@ private[study] final class SocketHandler(
}
} yield handler.some
private def reading[A](o: JsValue)(f: A => Unit)(implicit reader: Reads[A]): Unit =
o obj "d" flatMap { d => reader.reads(d).asOpt } foreach f
private object datatypes {
case class AtPath(path: String, chapterId: String)
implicit val atPathVarReader = Json.reads[AtPath]
}
import datatypes._
import Handler.AnaRateLimit
private def controller(
@ -52,7 +61,7 @@ private[study] final class SocketHandler(
for {
userId <- member.userId
d o obj "d"
chapterId <- d str "studyChapterId"
chapterId <- d str "chapterId"
} api.addNode(
Location.Ref(studyId, chapterId),
Path(anaMove.path),
@ -62,5 +71,15 @@ private[study] final class SocketHandler(
}
}
}
case ("deleteVariation", o) => AnaRateLimit(uid) {
reading[AtPath](o.pp) { d =>
api.deleteNodeAt(Location.Ref(studyId, d.chapterId), Path(d.path))
}
}
case ("promoteVariation", o) => AnaRateLimit(uid) {
reading[AtPath](o) { d =>
api.promoteNodeAt(Location.Ref(studyId, d.chapterId), Path(d.path))
}
}
}
}

View File

@ -42,6 +42,20 @@ final class StudyApi(
repo.setChapter(location withChapter newChapter)
}
def deleteNodeAt(ref: Location.Ref, path: Path) = sequenceLocation(ref) { location =>
val newChapter = location.chapter.updateRoot { root =>
root.withChildren(_.deleteNodeAt(path))
}
repo.setChapter(location withChapter newChapter.pp)
}
def promoteNodeAt(ref: Location.Ref, path: Path) = sequenceLocation(ref) { location =>
val newChapter = location.chapter.updateRoot { root =>
root.withChildren(_.promoteNodeAt(path))
}
repo.setChapter(location withChapter newChapter)
}
private def sequenceRef(refId: Location.Ref.ID)(f: Location.Ref => Funit): Funit =
Location.Ref.parseId(refId) ?? { ref =>
sequence(ref.studyId) {

View File

@ -19,7 +19,7 @@ private final class StudyRepo(coll: Coll) {
def setChapter(loc: Location) = coll.update(
$id(loc.study.id),
$set(s"chapters.${loc.chapterId}" -> loc.chapter.pp)
$set(s"chapters.${loc.chapterId}" -> loc.chapter)
).void
def setOwnerPath(ref: Location.Ref, path: Path): Funit =

View File

@ -313,11 +313,13 @@ module.exports = function(opts) {
this.tree.deleteNodeAt(path);
if (treePath.contains(path, this.vm.path)) this.jumpToMain(node.ply - 1);
else this.jump();
this.study && this.study.deleteVariation(path);
}.bind(this);
this.promoteVariation = function(path) {
this.tree.promoteVariation(path);
this.jump();
this.study && this.study.promoteVariation(path);
}.bind(this);
this.reset = function() {
@ -442,7 +444,7 @@ module.exports = function(opts) {
this.socket.receive(type, data);
}.bind(this);
this.study = opts.study ? studyCtrl.init(opts.study) : null;
this.study = opts.study ? studyCtrl.init(opts.study, this.socket.send) : null;
this.trans = lichess.trans(opts.i18n);
showGround();

View File

@ -48,7 +48,7 @@ module.exports = function(send, ctrl) {
this.sendAnaMove = function(req) {
clearTimeout(anaMoveTimeout);
withoutStandardVariant(req);
if (ctrl.study) req.studyChapterId = ctrl.study.currentChapterId();
if (ctrl.study) req.chapterId = ctrl.study.currentChapterId();
this.send('anaMove', req);
anaMoveTimeout = setTimeout(this.sendAnaMove.bind(this, req), 3000);
}.bind(this);

View File

@ -1,13 +1,24 @@
module.exports = {
init: function(data) {
init: function(data, send) {
var vm = {
chapterId: data.ownerChapterId
};
function addChapterId(data) {
data.chapterId = vm.chapterId;
return data;
}
return {
currentChapterId: function() {
return vm.chapterId;
},
deleteVariation: function(path) {
send("deleteVariation", addChapterId({path: path}));
},
promoteVariation: function(path) {
send("promoteVariation", addChapterId({path: path}));
}
};
}