study: persist node promotion and deletion
parent
f712920ff4
commit
7dbe5dc2ae
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue