study remote socket WIP
parent
0ab7df8b1d
commit
8739f079b3
|
@ -4,6 +4,7 @@ import chess.Centis
|
|||
import io.lettuce.core._
|
||||
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import ornicar.scalalib.Zero
|
||||
import play.api.libs.json._
|
||||
import scala.concurrent.duration._
|
||||
|
@ -112,6 +113,13 @@ final class RemoteSocket(
|
|||
conn.async.subscribe(channel)
|
||||
}
|
||||
|
||||
// final class Ask[R] {
|
||||
|
||||
// def apply[R, A](find: R => A): Future[A] = {
|
||||
// promise = Promise[R]
|
||||
// }
|
||||
// val handler: Protocol.In => Option[R]
|
||||
|
||||
lifecycle.addStopHook { () =>
|
||||
logger.info("Stopping the Redis pool...")
|
||||
Future {
|
||||
|
|
|
@ -39,30 +39,6 @@ final class SocketHandler(
|
|||
key = "study_invite.user"
|
||||
)
|
||||
|
||||
private def moveOrDrop(studyId: Study.Id, m: AnaAny, opts: MoveOpts, sri: Sri, member: StudySocket.Member) =
|
||||
AnaRateLimit(sri, member) {
|
||||
m.branch match {
|
||||
case scalaz.Success(branch) if branch.ply < Node.MAX_PLIES =>
|
||||
member push makeMessage("node", m json branch)
|
||||
for {
|
||||
userId <- member.userId
|
||||
chapterId <- m.chapterId
|
||||
if opts.write
|
||||
} api.addNode(
|
||||
userId,
|
||||
studyId,
|
||||
Position.Ref(Chapter.Id(chapterId), Path(m.path)),
|
||||
Node.fromBranch(branch) withClock opts.clock,
|
||||
sri,
|
||||
opts
|
||||
)
|
||||
case scalaz.Success(branch) =>
|
||||
member push makeMessage("stepFailure", s"ply ${branch.ply}/${Node.MAX_PLIES}")
|
||||
case scalaz.Failure(err) =>
|
||||
member push makeMessage("stepFailure", err.toString)
|
||||
}
|
||||
}
|
||||
|
||||
def makeController(
|
||||
socket: StudySocket,
|
||||
studyId: Study.Id,
|
||||
|
@ -70,47 +46,6 @@ final class SocketHandler(
|
|||
member: StudySocket.Member,
|
||||
user: Option[User]
|
||||
): Handler.Controller = ({
|
||||
case ("talk", o) => o str "d" foreach { text =>
|
||||
member.userId foreach { api.talk(_, studyId, text) }
|
||||
}
|
||||
case ("anaMove", o) => AnaMove parse o foreach {
|
||||
moveOrDrop(studyId, _, MoveOpts parse o, sri, member)
|
||||
}
|
||||
case ("anaDrop", o) => AnaDrop parse o foreach {
|
||||
moveOrDrop(studyId, _, MoveOpts parse o, sri, member)
|
||||
}
|
||||
case ("deleteNode", o) => AnaRateLimit(sri, member) {
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
jumpTo <- (o \ "d" \ "jumpTo").asOpt[String] map Path.apply
|
||||
userId <- member.userId
|
||||
} api.setPath(Who(userId, sri), studyId, position.ref.withPath(jumpTo)) >>
|
||||
api.deleteNodeAt(userId, studyId, position.ref, sri)
|
||||
}
|
||||
}
|
||||
case ("promote", o) => AnaRateLimit(sri, member) {
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
toMainline <- (o \ "d" \ "toMainline").asOpt[Boolean]
|
||||
userId <- member.userId
|
||||
} api.promote(userId, studyId, position.ref, toMainline, sri)
|
||||
}
|
||||
}
|
||||
case ("forceVariation", o) => AnaRateLimit(sri, member) {
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
force <- (o \ "d" \ "force").asOpt[Boolean]
|
||||
userId <- member.userId
|
||||
} api.forceVariation(userId, studyId, position.ref, force, sri)
|
||||
}
|
||||
}
|
||||
case ("setRole", o) => AnaRateLimit(sri, member) {
|
||||
reading[SetRole](o) { d =>
|
||||
member.userId foreach { userId =>
|
||||
api.setRole(userId, studyId, d.userId, d.role)
|
||||
}
|
||||
}
|
||||
}
|
||||
case ("invite", o) => for {
|
||||
byUserId <- member.userId
|
||||
username <- o str "d"
|
||||
|
@ -119,126 +54,6 @@ final class SocketHandler(
|
|||
onError = err => member push makeMessage("error", err))
|
||||
}
|
||||
|
||||
case ("kick", o) => for {
|
||||
byUserId <- member.userId
|
||||
username <- o str "d"
|
||||
} api.kick(byUserId, studyId, username)
|
||||
|
||||
case ("leave", _) => member.userId foreach { userId =>
|
||||
api.kick(userId, studyId, userId)
|
||||
}
|
||||
|
||||
case ("shapes", o) =>
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
shapes <- (o \ "d" \ "shapes").asOpt[List[Shape]]
|
||||
userId <- member.userId
|
||||
} api.setShapes(userId, studyId, position.ref, Shapes(shapes take 32), sri)
|
||||
}
|
||||
|
||||
case ("addChapter", o) =>
|
||||
reading[ChapterMaker.Data](o) { data =>
|
||||
member.userId foreach { byUserId =>
|
||||
val sticky = o.obj("d").flatMap(_.boolean("sticky")) | true
|
||||
api.addChapter(byUserId, studyId, data, sticky = sticky, sri)
|
||||
}
|
||||
}
|
||||
|
||||
case ("setChapter", o) => for {
|
||||
byUserId <- member.userId
|
||||
chapterId <- o.get[Chapter.Id]("d")
|
||||
} api.setChapter(byUserId, studyId, chapterId, sri)
|
||||
|
||||
case ("editChapter", o) =>
|
||||
reading[ChapterMaker.EditData](o) { data =>
|
||||
member.userId foreach {
|
||||
api.editChapter(_, studyId, data, sri)
|
||||
}
|
||||
}
|
||||
|
||||
case ("descStudy", o) => for {
|
||||
desc <- o str "d"
|
||||
userId <- member.userId
|
||||
} api.descStudy(userId, studyId, desc, sri)
|
||||
|
||||
case ("descChapter", o) =>
|
||||
reading[ChapterMaker.DescData](o) { data =>
|
||||
member.userId foreach {
|
||||
api.descChapter(_, studyId, data, sri)
|
||||
}
|
||||
}
|
||||
|
||||
case ("deleteChapter", o) => for {
|
||||
byUserId <- member.userId
|
||||
id <- o.get[Chapter.Id]("d")
|
||||
} api.deleteChapter(byUserId, studyId, id, sri)
|
||||
|
||||
case ("clearAnnotations", o) => for {
|
||||
byUserId <- member.userId
|
||||
id <- o.get[Chapter.Id]("d")
|
||||
} api.clearAnnotations(byUserId, studyId, id, sri)
|
||||
|
||||
case ("sortChapters", o) => for {
|
||||
byUserId <- member.userId
|
||||
ids <- o.get[List[Chapter.Id]]("d")
|
||||
} api.sortChapters(byUserId, studyId, ids, sri)
|
||||
|
||||
case ("editStudy", o) => for {
|
||||
byUserId <- member.userId
|
||||
data <- (o \ "d").asOpt[Study.Data]
|
||||
} api.editStudy(byUserId, studyId, data)
|
||||
|
||||
case ("setTag", o) =>
|
||||
reading[actorApi.SetTag](o) { setTag =>
|
||||
member.userId foreach { byUserId =>
|
||||
api.setTag(byUserId, studyId, setTag, sri)
|
||||
}
|
||||
}
|
||||
|
||||
case ("setComment", o) =>
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
userId <- member.userId
|
||||
text <- (o \ "d" \ "text").asOpt[String]
|
||||
} api.setComment(userId, studyId, position.ref, Comment sanitize text, sri)
|
||||
}
|
||||
|
||||
case ("deleteComment", o) =>
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
userId <- member.userId
|
||||
id <- (o \ "d" \ "id").asOpt[String]
|
||||
} api.deleteComment(userId, studyId, position.ref, Comment.Id(id), sri)
|
||||
}
|
||||
|
||||
case ("setGamebook", o) =>
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
userId <- member.userId
|
||||
gamebook <- (o \ "d" \ "gamebook").asOpt[Gamebook].map(_.cleanUp)
|
||||
} api.setGamebook(userId, studyId, position.ref, gamebook, sri)
|
||||
}
|
||||
|
||||
case ("toggleGlyph", o) =>
|
||||
reading[AtPosition](o) { position =>
|
||||
for {
|
||||
userId <- member.userId
|
||||
glyph <- (o \ "d" \ "id").asOpt[Int] flatMap Glyph.find
|
||||
} api.toggleGlyph(userId, studyId, position.ref, glyph, sri)
|
||||
}
|
||||
|
||||
case ("explorerGame", o) =>
|
||||
reading[actorApi.ExplorerGame](o) { data =>
|
||||
member.userId foreach { byUserId =>
|
||||
api.explorerGame(byUserId, studyId, data, sri)
|
||||
}
|
||||
}
|
||||
|
||||
case ("requestAnalysis", o) => for {
|
||||
byUserId <- member.userId
|
||||
chapterId <- o.get[Chapter.Id]("d")
|
||||
} api.analysisRequest(studyId, chapterId, byUserId)
|
||||
|
||||
}: Handler.Controller) orElse evalCacheHandler(sri, member, user) orElse lila.chat.Socket.in(
|
||||
chatId = Chat.Id(studyId.value),
|
||||
member = member,
|
||||
|
|
|
@ -3,6 +3,7 @@ package lila.study
|
|||
import akka.actor.ActorSelection
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import actorApi.Who
|
||||
import chess.Centis
|
||||
import chess.format.pgn.{ Tags, Glyph }
|
||||
import lila.chat.Chat
|
||||
|
@ -12,7 +13,6 @@ import lila.socket.Socket.Sri
|
|||
import lila.tree.Eval
|
||||
import lila.tree.Node.{ Shapes, Comment, Gamebook }
|
||||
import lila.user.User
|
||||
import actorApi.Who
|
||||
|
||||
final class StudyApi(
|
||||
studyRepo: StudyRepo,
|
||||
|
@ -96,12 +96,10 @@ final class StudyApi(
|
|||
import akka.pattern.ask
|
||||
import makeTimeout.short
|
||||
addChapter(
|
||||
byUserId = user.id,
|
||||
studyId = study.id,
|
||||
data = data.form.toChapterData,
|
||||
sticky = study.settings.sticky,
|
||||
sri = Sri("") // the user is not in the study yet
|
||||
) >> byIdWithChapter(studyId)
|
||||
sticky = study.settings.sticky
|
||||
)(Who(user.id, Sri(""))) >> byIdWithChapter(studyId)
|
||||
case _ => fuccess(none)
|
||||
} orElse importGame(data.copy(form = data.form.copy(asStr = none)), user)
|
||||
}) addEffect {
|
||||
|
@ -158,7 +156,7 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def setPath(who: Who, studyId: Study.Id, position: Position.Ref) = sequenceStudy(studyId) { study =>
|
||||
def setPath(studyId: Study.Id, position: Position.Ref)(who: Who): Funit = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
chapterRepo.byId(position.chapterId).map {
|
||||
_ filter { c =>
|
||||
|
@ -175,16 +173,16 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def addNode(userId: User.ID, studyId: Study.Id, position: Position.Ref, node: Node, sri: Sri, opts: MoveOpts, relay: Option[Chapter.Relay] = None) =
|
||||
def addNode(studyId: Study.Id, position: Position.Ref, node: Node, opts: MoveOpts, relay: Option[Chapter.Relay] = None)(who: Who) =
|
||||
sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
doAddNode(userId, study, Position(chapter, position.path), node, sri, opts, relay).void
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
doAddNode(study, Position(chapter, position.path), node, opts, relay)(who).void
|
||||
}
|
||||
}
|
||||
|
||||
private def doAddNode(userId: User.ID, study: Study, position: Position, rawNode: Node, sri: Sri, opts: MoveOpts, relay: Option[Chapter.Relay]): Funit = {
|
||||
private def doAddNode(study: Study, position: Position, rawNode: Node, opts: MoveOpts, relay: Option[Chapter.Relay])(who: Who): Funit = {
|
||||
val node = rawNode.withoutChildren
|
||||
def failReload = reloadSriBecauseOf(study, sri, position.chapter.id)
|
||||
def failReload = reloadSriBecauseOf(study, who.sri, position.chapter.id)
|
||||
if (position.chapter.isOverweight) {
|
||||
logger.info(s"Overweight chapter ${study.id}/${position.chapter.id}")
|
||||
fuccess(failReload)
|
||||
|
@ -203,13 +201,13 @@ final class StudyApi(
|
|||
position.ref,
|
||||
node,
|
||||
chapter.setup.variant,
|
||||
sri,
|
||||
who.sri,
|
||||
sticky = opts.sticky,
|
||||
relay = relay
|
||||
))
|
||||
sendStudyEnters(study, userId)
|
||||
sendStudyEnters(study, who.u)
|
||||
if (opts.promoteToMainline && !Path.isMainline(chapter.root, newPosition.path))
|
||||
promote(userId, study.id, position.ref + node, toMainline = true, sri)
|
||||
promote(study.id, position.ref + node, toMainline = true)(who)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,31 +225,31 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def deleteNodeAt(userId: User.ID, studyId: Study.Id, position: Position.Ref, sri: Sri) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
def deleteNodeAt(studyId: Study.Id, position: Position.Ref)(who: Who) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
chapter.updateRoot { root =>
|
||||
root.withChildren(_.deleteNodeAt(position.path))
|
||||
} match {
|
||||
case Some(newChapter) =>
|
||||
chapterRepo.update(newChapter) >>-
|
||||
sendTo(study, StudySocket.DeleteNode(position, sri))
|
||||
sendTo(study, StudySocket.DeleteNode(position, who.sri))
|
||||
case None =>
|
||||
fufail(s"Invalid delNode $studyId $position") >>-
|
||||
reloadSriBecauseOf(study, sri, chapter.id)
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def clearAnnotations(userId: User.ID, studyId: Study.Id, chapterId: Chapter.Id, sri: Sri) = sequenceStudyWithChapter(studyId, chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
def clearAnnotations(studyId: Study.Id, chapterId: Chapter.Id)(who: Who) = sequenceStudyWithChapter(studyId, chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
chapterRepo.update(chapter.updateRoot { root =>
|
||||
root.withChildren(_.updateAllWith(_.clearAnnotations).some)
|
||||
} | chapter) >>- sendTo(study, StudySocket.UpdateChapter(sri, chapter.id))
|
||||
} | chapter) >>- sendTo(study, StudySocket.UpdateChapter(who.sri, chapter.id))
|
||||
}
|
||||
}
|
||||
|
||||
def promote(userId: User.ID, studyId: Study.Id, position: Position.Ref, toMainline: Boolean, sri: Sri) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
def promote(studyId: Study.Id, position: Position.Ref, toMainline: Boolean)(who: Who) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
chapter.updateRoot { root =>
|
||||
root.withChildren { children =>
|
||||
if (toMainline) children.promoteToMainlineAt(position.path)
|
||||
|
@ -260,7 +258,7 @@ final class StudyApi(
|
|||
} match {
|
||||
case Some(newChapter) =>
|
||||
chapterRepo.update(newChapter) >>-
|
||||
sendTo(study, StudySocket.Promote(position, toMainline, sri)) >>
|
||||
sendTo(study, StudySocket.Promote(position, toMainline, who.sri)) >>
|
||||
newChapter.root.children.nodesOn {
|
||||
newChapter.root.mainlinePath.intersect(position.path)
|
||||
}.collect {
|
||||
|
@ -269,15 +267,15 @@ final class StudyApi(
|
|||
}.sequenceFu.void
|
||||
case None =>
|
||||
fufail(s"Invalid promoteToMainline $studyId $position") >>-
|
||||
reloadSriBecauseOf(study, sri, chapter.id)
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def forceVariation(userId: User.ID, studyId: Study.Id, position: Position.Ref, force: Boolean, sri: Sri): Funit =
|
||||
def forceVariation(studyId: Study.Id, position: Position.Ref, force: Boolean)(who: Who): Funit =
|
||||
sequenceStudyWithChapter(studyId, position.chapterId) { sc =>
|
||||
Contribute(userId, sc.study) {
|
||||
doForceVariation(sc, position.path, force, sri)
|
||||
Contribute(who.u, sc.study) {
|
||||
doForceVariation(sc, position.path, force, who.sri)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,8 +289,8 @@ final class StudyApi(
|
|||
reloadSriBecauseOf(sc.study, sri, sc.chapter.id)
|
||||
}
|
||||
|
||||
def setRole(byUserId: User.ID, studyId: Study.Id, userId: User.ID, roleStr: String) = sequenceStudy(studyId) { study =>
|
||||
(study isOwner byUserId) ?? {
|
||||
def setRole(studyId: Study.Id, userId: User.ID, roleStr: String)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
(study isOwner who.u) ?? {
|
||||
val role = StudyMember.Role.byId.getOrElse(roleStr, StudyMember.Role.Read)
|
||||
study.members.get(userId) ifTrue study.isPublic foreach { member =>
|
||||
if (!member.role.canWrite && role.canWrite)
|
||||
|
@ -312,8 +310,8 @@ final class StudyApi(
|
|||
)
|
||||
}
|
||||
|
||||
def kick(byUserId: User.ID, studyId: Study.Id, userId: User.ID) = sequenceStudy(studyId) { study =>
|
||||
(study.isMember(userId) && (study.isOwner(byUserId) ^ (byUserId == userId))) ?? {
|
||||
def kick(studyId: Study.Id, userId: User.ID)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
(study.isMember(userId) && (study.isOwner(who.u) ^ (who.u == userId))) ?? {
|
||||
if (study.isPublic && study.canContribute(userId))
|
||||
bus.publish(lila.hub.actorApi.study.StudyMemberLostWriteAccess(userId, studyId.value), 'study)
|
||||
studyRepo.removeMember(study, userId)
|
||||
|
@ -333,18 +331,18 @@ final class StudyApi(
|
|||
indexStudy(study)
|
||||
}
|
||||
|
||||
def setShapes(userId: User.ID, studyId: Study.Id, position: Position.Ref, shapes: Shapes, sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
Contribute(userId, study) {
|
||||
def setShapes(studyId: Study.Id, position: Position.Ref, shapes: Shapes)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
chapterRepo.byIdAndStudy(position.chapterId, study.id) flatMap {
|
||||
_ ?? { chapter =>
|
||||
chapter.setShapes(shapes, position.path) match {
|
||||
case Some(newChapter) =>
|
||||
studyRepo.updateNow(study)
|
||||
chapterRepo.setShapes(shapes)(newChapter, position.path) >>-
|
||||
sendTo(study, StudySocket.SetShapes(position, shapes, sri))
|
||||
sendTo(study, StudySocket.SetShapes(position, shapes, who.sri))
|
||||
case None =>
|
||||
fufail(s"Invalid setShapes $position $shapes") >>-
|
||||
reloadSriBecauseOf(study, sri, chapter.id)
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -364,15 +362,15 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def setTag(userId: User.ID, studyId: Study.Id, setTag: actorApi.SetTag, sri: Sri) = sequenceStudyWithChapter(studyId, setTag.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
doSetTags(study, chapter, PgnTags(chapter.tags + setTag.tag), sri)
|
||||
def setTag(studyId: Study.Id, setTag: actorApi.SetTag)(who: Who) = sequenceStudyWithChapter(studyId, setTag.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
doSetTags(study, chapter, PgnTags(chapter.tags + setTag.tag), who.sri)
|
||||
}
|
||||
}
|
||||
|
||||
def setTags(userId: User.ID, studyId: Study.Id, chapterId: Chapter.Id, tags: Tags, sri: Sri) = sequenceStudyWithChapter(studyId, chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
doSetTags(study, chapter, tags, sri)
|
||||
def setTags(studyId: Study.Id, chapterId: Chapter.Id, tags: Tags, sri: Sri)(who: Who) = sequenceStudyWithChapter(studyId, chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
doSetTags(study, chapter, tags, who.sri)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,15 +386,15 @@ final class StudyApi(
|
|||
} >>- indexStudy(study)
|
||||
}
|
||||
|
||||
def setComment(userId: User.ID, studyId: Study.Id, position: Position.Ref, text: Comment.Text, sri: Sri) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
lightUser(userId) ?? { author =>
|
||||
def setComment(studyId: Study.Id, position: Position.Ref, text: Comment.Text)(who: Who) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
lightUser(who.u) ?? { author =>
|
||||
val comment = Comment(
|
||||
id = Comment.Id.make,
|
||||
text = text,
|
||||
by = Comment.Author.User(author.id, author.titleName)
|
||||
)
|
||||
doSetComment(userId, study, Position(chapter, position.path), comment, sri)
|
||||
doSetComment(who.u, study, Position(chapter, position.path), comment, who.sri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -419,91 +417,91 @@ final class StudyApi(
|
|||
reloadSriBecauseOf(study, sri, position.chapter.id)
|
||||
}
|
||||
|
||||
def deleteComment(userId: User.ID, studyId: Study.Id, position: Position.Ref, id: Comment.Id, sri: Sri) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
def deleteComment(studyId: Study.Id, position: Position.Ref, id: Comment.Id)(who: Who) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
chapter.deleteComment(id, position.path) match {
|
||||
case Some(newChapter) =>
|
||||
chapterRepo.update(newChapter) >>-
|
||||
sendTo(study, StudySocket.DeleteComment(position, id, sri)) >>-
|
||||
sendTo(study, StudySocket.DeleteComment(position, id, who.sri)) >>-
|
||||
indexStudy(study)
|
||||
case None =>
|
||||
fufail(s"Invalid deleteComment $studyId $position $id") >>-
|
||||
reloadSriBecauseOf(study, sri, chapter.id)
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def toggleGlyph(userId: User.ID, studyId: Study.Id, position: Position.Ref, glyph: Glyph, sri: Sri) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
def toggleGlyph(studyId: Study.Id, position: Position.Ref, glyph: Glyph)(who: Who) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
chapter.toggleGlyph(glyph, position.path) match {
|
||||
case Some(newChapter) =>
|
||||
studyRepo.updateNow(study)
|
||||
newChapter.root.nodeAt(position.path) ?? { node =>
|
||||
chapterRepo.setGlyphs(node.glyphs)(newChapter, position.path) >>-
|
||||
newChapter.root.nodeAt(position.path).foreach { node =>
|
||||
sendTo(study, StudySocket.SetGlyphs(position, node.glyphs, sri))
|
||||
sendTo(study, StudySocket.SetGlyphs(position, node.glyphs, who.sri))
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
fufail(s"Invalid toggleGlyph $studyId $position $glyph") >>-
|
||||
reloadSriBecauseOf(study, sri, chapter.id)
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setGamebook(userId: User.ID, studyId: Study.Id, position: Position.Ref, gamebook: Gamebook, sri: Sri) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
def setGamebook(studyId: Study.Id, position: Position.Ref, gamebook: Gamebook)(who: Who) = sequenceStudyWithChapter(studyId, position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
chapter.setGamebook(gamebook, position.path) match {
|
||||
case Some(newChapter) =>
|
||||
studyRepo.updateNow(study)
|
||||
chapterRepo.setGamebook(gamebook)(newChapter, position.path) >>- {
|
||||
indexStudy(study)
|
||||
sendStudyEnters(study, userId)
|
||||
sendStudyEnters(study, who.u)
|
||||
}
|
||||
case None =>
|
||||
fufail(s"Invalid setGamebook $studyId $position") >>-
|
||||
reloadSriBecauseOf(study, sri, chapter.id)
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def explorerGame(userId: User.ID, studyId: Study.Id, data: actorApi.ExplorerGame, sri: Sri) = sequenceStudyWithChapter(studyId, data.position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
if (data.insert) explorerGameHandler.insert(userId, study, Position(chapter, data.position.path), data.gameId) flatMap {
|
||||
def explorerGame(studyId: Study.Id, data: actorApi.ExplorerGame)(who: Who) = sequenceStudyWithChapter(studyId, data.position.chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
if (data.insert) explorerGameHandler.insert(who.u, study, Position(chapter, data.position.path), data.gameId) flatMap {
|
||||
case None =>
|
||||
fufail(s"Invalid explorerGame insert $studyId $data") >>-
|
||||
reloadSriBecauseOf(study, sri, chapter.id)
|
||||
reloadSriBecauseOf(study, who.sri, chapter.id)
|
||||
case Some((chapter, path)) =>
|
||||
studyRepo.updateNow(study)
|
||||
chapter.root.nodeAt(path) ?? { parent =>
|
||||
chapterRepo.setChildren(parent.children)(chapter, path) >>- {
|
||||
sendStudyEnters(study, userId)
|
||||
sendStudyEnters(study, who.u)
|
||||
sendTo(study, StudySocket.ReloadAll)
|
||||
}
|
||||
}
|
||||
}
|
||||
else explorerGameHandler.quote(data.gameId) flatMap {
|
||||
_ ?? {
|
||||
doSetComment(userId, study, Position(chapter, data.position.path), _, sri)
|
||||
doSetComment(who.u, study, Position(chapter, data.position.path), _, who.sri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def addChapter(byUserId: User.ID, studyId: Study.Id, data: ChapterMaker.Data, sticky: Boolean, sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
def addChapter(studyId: Study.Id, data: ChapterMaker.Data, sticky: Boolean)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
chapterRepo.countByStudyId(study.id) flatMap { count =>
|
||||
if (count >= Study.maxChapters) funit
|
||||
else chapterRepo.nextOrderByStudy(study.id) flatMap { order =>
|
||||
chapterMaker(study, data, order, byUserId) flatMap { chapter =>
|
||||
chapterMaker(study, data, order, who.u) flatMap { chapter =>
|
||||
data.initial ?? {
|
||||
chapterRepo.firstByStudy(study.id) flatMap {
|
||||
_.filter(_.isEmptyInitial) ?? chapterRepo.delete
|
||||
}
|
||||
} >> doAddChapter(study, chapter, sticky, sri)
|
||||
} >> doAddChapter(study, chapter, sticky, who.sri)
|
||||
} addFailureEffect {
|
||||
case ChapterMaker.ValidationException(error) =>
|
||||
sendTo(study, StudySocket.ValidationError(sri, error))
|
||||
sendTo(study, StudySocket.ValidationError(who.sri, error))
|
||||
case u => println(u)
|
||||
}
|
||||
}
|
||||
|
@ -511,9 +509,9 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def importPgns(byUser: User, studyId: Study.Id, datas: List[ChapterMaker.Data], sticky: Boolean, sri: Sri) =
|
||||
def importPgns(studyId: Study.Id, datas: List[ChapterMaker.Data], sticky: Boolean)(who: Who) =
|
||||
lila.common.Future.applySequentially(datas) { data =>
|
||||
addChapter(byUser.id, studyId, data, sticky, sri)
|
||||
addChapter(studyId, data, sticky)(who)
|
||||
}
|
||||
|
||||
def doAddChapter(study: Study, chapter: Chapter, sticky: Boolean, sri: Sri) =
|
||||
|
@ -525,8 +523,8 @@ final class StudyApi(
|
|||
studyRepo.updateNow(study) >>-
|
||||
indexStudy(study)
|
||||
|
||||
def setChapter(byUserId: User.ID, studyId: Study.Id, chapterId: Chapter.Id, sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
study.canContribute(byUserId) ?? doSetChapter(study, chapterId, sri)
|
||||
def setChapter(studyId: Study.Id, chapterId: Chapter.Id)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
study.canContribute(who.u) ?? doSetChapter(study, chapterId, who.sri)
|
||||
}
|
||||
|
||||
private def doSetChapter(study: Study, chapterId: Chapter.Id, sri: Sri) =
|
||||
|
@ -540,8 +538,8 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def editChapter(byUserId: User.ID, studyId: Study.Id, data: ChapterMaker.EditData, sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
def editChapter(studyId: Study.Id, data: ChapterMaker.EditData)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
chapterRepo.byIdAndStudy(data.id, studyId) flatMap {
|
||||
_ ?? { chapter =>
|
||||
val name = Chapter fixName data.name
|
||||
|
@ -573,7 +571,7 @@ final class StudyApi(
|
|||
(newChapter.practice != chapter.practice) ||
|
||||
(newChapter.gamebook != chapter.gamebook) ||
|
||||
(newChapter.description != chapter.description)
|
||||
if (shouldReload) sendTo(study, StudySocket.UpdateChapter(sri, chapter.id))
|
||||
if (shouldReload) sendTo(study, StudySocket.UpdateChapter(who.sri, chapter.id))
|
||||
else reloadChapters(study)
|
||||
}
|
||||
}
|
||||
|
@ -582,8 +580,8 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def descChapter(byUserId: User.ID, studyId: Study.Id, data: ChapterMaker.DescData, sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
def descChapter(studyId: Study.Id, data: ChapterMaker.DescData)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
chapterRepo.byIdAndStudy(data.id, studyId) flatMap {
|
||||
_ ?? { chapter =>
|
||||
val newChapter = chapter.copy(
|
||||
|
@ -591,7 +589,7 @@ final class StudyApi(
|
|||
)
|
||||
(chapter != newChapter) ?? {
|
||||
chapterRepo.update(newChapter) >>- {
|
||||
sendTo(study, StudySocket.DescChapter(sri, newChapter.id, newChapter.description))
|
||||
sendTo(study, StudySocket.DescChapter(who.sri, newChapter.id, newChapter.description))
|
||||
indexStudy(study)
|
||||
}
|
||||
}
|
||||
|
@ -600,20 +598,20 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def deleteChapter(byUserId: User.ID, studyId: Study.Id, chapterId: Chapter.Id, sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
def deleteChapter(studyId: Study.Id, chapterId: Chapter.Id)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
chapterRepo.byIdAndStudy(chapterId, studyId) flatMap {
|
||||
_ ?? { chapter =>
|
||||
chapterRepo.orderedMetadataByStudy(studyId).flatMap { chaps =>
|
||||
// deleting the only chapter? Automatically create an empty one
|
||||
if (chaps.size < 2) {
|
||||
chapterMaker(study, ChapterMaker.Data(Chapter.Name("Chapter 1")), 1, byUserId) flatMap { c =>
|
||||
doAddChapter(study, c, sticky = true, sri) >> doSetChapter(study, c.id, sri)
|
||||
chapterMaker(study, ChapterMaker.Data(Chapter.Name("Chapter 1")), 1, who.u) flatMap { c =>
|
||||
doAddChapter(study, c, sticky = true, who.sri) >> doSetChapter(study, c.id, who.sri)
|
||||
}
|
||||
} // deleting the current chapter? Automatically move to another one
|
||||
else (study.position.chapterId == chapterId).?? {
|
||||
chaps.find(_.id != chapterId) ?? { newChap =>
|
||||
doSetChapter(study, newChap.id, sri)
|
||||
doSetChapter(study, newChap.id, who.sri)
|
||||
}
|
||||
}
|
||||
} >> chapterRepo.delete(chapter.id) >>- reloadChapters(study)
|
||||
|
@ -622,25 +620,25 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def sortChapters(byUserId: User.ID, studyId: Study.Id, chapterIds: List[Chapter.Id], sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
def sortChapters(studyId: Study.Id, chapterIds: List[Chapter.Id])(who: Who) = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
chapterRepo.sort(study, chapterIds) >>- reloadChapters(study)
|
||||
}
|
||||
}
|
||||
|
||||
def descStudy(byUserId: User.ID, studyId: Study.Id, desc: String, sri: Sri) = sequenceStudy(studyId) { study =>
|
||||
Contribute(byUserId, study) {
|
||||
def descStudy(studyId: Study.Id, desc: String)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
Contribute(who.u, study) {
|
||||
val newStudy = study.copy(description = desc.nonEmpty option desc)
|
||||
(study != newStudy) ?? {
|
||||
studyRepo.updateSomeFields(newStudy) >>-
|
||||
sendTo(study, StudySocket.DescStudy(sri, newStudy.description)) >>-
|
||||
sendTo(study, StudySocket.DescStudy(who.sri, newStudy.description)) >>-
|
||||
indexStudy(study)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def editStudy(byUserId: User.ID, studyId: Study.Id, data: Study.Data) = sequenceStudy(studyId) { study =>
|
||||
data.settings.ifTrue(study isOwner byUserId) ?? { settings =>
|
||||
def editStudy(studyId: Study.Id, data: Study.Data)(who: Who) = sequenceStudy(studyId) { study =>
|
||||
data.settings.ifTrue(study isOwner who.u) ?? { settings =>
|
||||
val newStudy = study.copy(
|
||||
name = Study toName data.name,
|
||||
settings = settings,
|
||||
|
@ -670,7 +668,7 @@ final class StudyApi(
|
|||
lightStudyCache.put(study.id, none)
|
||||
}
|
||||
|
||||
def like(who: Who, studyId: Study.Id, v: Boolean): Funit =
|
||||
def like(studyId: Study.Id, v: Boolean)(who: Who): Funit =
|
||||
studyRepo.like(studyId, who.u, v) map { likes =>
|
||||
sendToNew(studyId, _.SetLiking(Study.Liking(likes, v), who))
|
||||
bus.publish(actorApi.StudyLikes(studyId, likes), 'studyLikes)
|
||||
|
@ -696,10 +694,10 @@ final class StudyApi(
|
|||
}
|
||||
}
|
||||
|
||||
def analysisRequest(studyId: Study.Id, chapterId: Chapter.Id, userId: User.ID): Funit =
|
||||
def analysisRequest(studyId: Study.Id, chapterId: Chapter.Id)(who: Who): Funit =
|
||||
sequenceStudyWithChapter(studyId, chapterId) {
|
||||
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
|
||||
serverEvalRequester(study, chapter, userId)
|
||||
case Study.WithChapter(study, chapter) => Contribute(who.u, study) {
|
||||
serverEvalRequester(study, chapter, who.u)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,12 @@ import scala.concurrent.duration._
|
|||
import scala.concurrent.Promise
|
||||
|
||||
import actorApi.Who
|
||||
import chess.format.pgn.Glyph
|
||||
import lila.room.RoomSocket.{ Protocol => RP, _ }
|
||||
import lila.socket.RemoteSocket.{ Protocol => P, _ }
|
||||
import lila.socket.Socket.{ Sri, makeMessage }
|
||||
import lila.socket.{ AnaMove, AnaDrop, AnaAny }
|
||||
import lila.tree.Node.{ Shape, Shapes, Comment, Gamebook }
|
||||
import lila.user.User
|
||||
|
||||
private final class StudyRemoteSocket(
|
||||
|
@ -19,7 +22,18 @@ private final class StudyRemoteSocket(
|
|||
system: ActorSystem
|
||||
) {
|
||||
|
||||
import StudyRemoteSocket._
|
||||
|
||||
implicit def roomIdToStudyId(roomId: RoomId) = Study.Id(roomId.value)
|
||||
implicit def studyIdToRoomId(studyId: Study.Id) = RoomId(studyId.value)
|
||||
|
||||
// private val isPresent = ask[IsPresent](Protocol.In.isPresentReader)
|
||||
|
||||
// def isPresent(studyId: Study.Id, userId: User.ID): Fu[Boolean] = {
|
||||
// val promise = isPresent(studyId, userId)
|
||||
// send(Protocol.Out.isPresent(studyId, userId))
|
||||
// promise.future
|
||||
// }
|
||||
|
||||
lazy val rooms = makeRoomMap(send, system.lilaBus)
|
||||
|
||||
|
@ -27,22 +41,130 @@ private final class StudyRemoteSocket(
|
|||
roomId => _.Study(roomId.value).some)
|
||||
|
||||
private lazy val studyHandler: Handler = {
|
||||
case m @ RP.In.TellRoomSri(roomId, P.In.TellSri(sri, user, tpe, msg)) if messagesHandled(tpe) =>
|
||||
case Protocol.In.TellStudySri(studyId, P.In.TellSri(sri, user, tpe, o)) =>
|
||||
import Protocol.In.Data._
|
||||
def who = user map { Who(_, sri) }
|
||||
(tpe -> msg) match {
|
||||
case ("setPath", o) => reading[AtPosition](o) { position =>
|
||||
who foreach { api.setPath(_, roomId, position.ref) }
|
||||
tpe match {
|
||||
case "setPath" => reading[AtPosition](o) { position =>
|
||||
who foreach api.setPath(studyId, position.ref)
|
||||
}
|
||||
case ("like", o) => for {
|
||||
w <- who
|
||||
v <- (o \ "d" \ "liked").asOpt[Boolean]
|
||||
} api.like(w, roomId, v)
|
||||
case "like" => (o \ "d" \ "liked").asOpt[Boolean] foreach { v =>
|
||||
who foreach api.like(studyId, v)
|
||||
}
|
||||
case "anaMove" => AnaMove parse o foreach { move =>
|
||||
who foreach moveOrDrop(studyId, move, MoveOpts parse o)
|
||||
}
|
||||
case "anaDrop" => AnaDrop parse o foreach { drop =>
|
||||
who foreach moveOrDrop(studyId, drop, MoveOpts parse o)
|
||||
}
|
||||
case "deleteNode" => reading[AtPosition](o) { position =>
|
||||
(o \ "d" \ "jumpTo").asOpt[String] map Path.apply foreach { jumpTo =>
|
||||
who foreach api.setPath(studyId, position.ref.withPath(jumpTo))
|
||||
who foreach api.deleteNodeAt(studyId, position.ref)
|
||||
}
|
||||
}
|
||||
case "promote" => reading[AtPosition](o) { position =>
|
||||
(o \ "d" \ "toMainline").asOpt[Boolean] foreach { toMainline =>
|
||||
who foreach api.promote(studyId, position.ref, toMainline)
|
||||
}
|
||||
}
|
||||
case "forceVariation" => reading[AtPosition](o) { position =>
|
||||
(o \ "d" \ "force").asOpt[Boolean] foreach { force =>
|
||||
who foreach api.forceVariation(studyId, position.ref, force)
|
||||
}
|
||||
}
|
||||
case "setRole" => reading[SetRole](o) { d =>
|
||||
who foreach api.setRole(studyId, d.userId, d.role)
|
||||
}
|
||||
case "kick" => o str "d" foreach { username =>
|
||||
who foreach api.kick(studyId, username)
|
||||
}
|
||||
case "leave" => who foreach { w =>
|
||||
api.kick(studyId, w.u)(w)
|
||||
}
|
||||
case "shapes" => reading[AtPosition](o) { position =>
|
||||
import JsonView.shapeReader
|
||||
(o \ "d" \ "shapes").asOpt[List[Shape]] foreach { shapes =>
|
||||
who foreach api.setShapes(studyId, position.ref, Shapes(shapes take 32))
|
||||
}
|
||||
}
|
||||
case "addChapter" => reading[ChapterMaker.Data](o) { data =>
|
||||
val sticky = o.obj("d").flatMap(_.boolean("sticky")) | true
|
||||
who foreach api.addChapter(studyId, data, sticky = sticky)
|
||||
}
|
||||
case "setChapter" => o.get[Chapter.Id]("d") foreach { chapterId =>
|
||||
who foreach api.setChapter(studyId, chapterId)
|
||||
}
|
||||
case "editChapter" => reading[ChapterMaker.EditData](o) { data =>
|
||||
who foreach api.editChapter(studyId, data)
|
||||
}
|
||||
case "descStudy" => o str "d" foreach { desc =>
|
||||
who foreach api.descStudy(studyId, desc)
|
||||
}
|
||||
case "descChapter" => reading[ChapterMaker.DescData](o) { data =>
|
||||
who foreach api.descChapter(studyId, data)
|
||||
}
|
||||
case "deleteChapter" => o.get[Chapter.Id]("d") foreach { id =>
|
||||
who foreach api.deleteChapter(studyId, id)
|
||||
}
|
||||
case "clearAnnotations" => o.get[Chapter.Id]("d") foreach { id =>
|
||||
who foreach api.clearAnnotations(studyId, id)
|
||||
}
|
||||
case "sortChapters" => o.get[List[Chapter.Id]]("d") foreach { ids =>
|
||||
who foreach api.sortChapters(studyId, ids)
|
||||
}
|
||||
case "editStudy" => (o \ "d").asOpt[Study.Data] foreach { data =>
|
||||
who foreach api.editStudy(studyId, data)
|
||||
}
|
||||
case "setTag" => reading[actorApi.SetTag](o) { setTag =>
|
||||
who foreach api.setTag(studyId, setTag)
|
||||
}
|
||||
case "setComment" => reading[AtPosition](o) { position =>
|
||||
(o \ "d" \ "text").asOpt[String] foreach { text =>
|
||||
who foreach api.setComment(studyId, position.ref, Comment sanitize text)
|
||||
}
|
||||
}
|
||||
case "deleteComment" => reading[AtPosition](o) { position =>
|
||||
(o \ "d" \ "id").asOpt[String] foreach { id =>
|
||||
who foreach api.deleteComment(studyId, position.ref, Comment.Id(id))
|
||||
}
|
||||
}
|
||||
case "setGamebook" => reading[AtPosition](o) { position =>
|
||||
(o \ "d" \ "gamebook").asOpt[Gamebook].map(_.cleanUp) foreach { gamebook =>
|
||||
who foreach api.setGamebook(studyId, position.ref, gamebook)
|
||||
}
|
||||
}
|
||||
case "toggleGlyph" => reading[AtPosition](o) { position =>
|
||||
(o \ "d" \ "id").asOpt[Int] flatMap Glyph.find foreach { glyph =>
|
||||
who foreach api.toggleGlyph(studyId, position.ref, glyph)
|
||||
}
|
||||
}
|
||||
case "explorerGame" => reading[actorApi.ExplorerGame](o) { data =>
|
||||
who foreach api.explorerGame(studyId, data)
|
||||
}
|
||||
case "requestAnalysis" => o.get[Chapter.Id]("d") foreach { chapterId =>
|
||||
who foreach api.analysisRequest(studyId, chapterId)
|
||||
}
|
||||
case t => logger.warn(s"Unhandled study socket message: $t")
|
||||
}
|
||||
}
|
||||
|
||||
private val messagesHandled: Set[String] =
|
||||
Set("like", "setPath")
|
||||
private def moveOrDrop(studyId: Study.Id, m: AnaAny, opts: MoveOpts)(who: Who) = m.branch match {
|
||||
case scalaz.Success(branch) if branch.ply < Node.MAX_PLIES =>
|
||||
send(P.Out.tellSri(who.sri, makeMessage("node", m json branch)))
|
||||
m.chapterId.ifTrue(opts.write) foreach { chapterId =>
|
||||
api.addNode(
|
||||
studyId,
|
||||
Position.Ref(Chapter.Id(chapterId), Path(m.path)),
|
||||
Node.fromBranch(branch) withClock opts.clock,
|
||||
opts
|
||||
)(who)
|
||||
}
|
||||
case scalaz.Success(branch) =>
|
||||
send(P.Out.tellSri(who.sri, makeMessage("stepFailure", s"ply ${branch.ply}/${Node.MAX_PLIES}")))
|
||||
case scalaz.Failure(err) =>
|
||||
send(P.Out.tellSri(who.sri, makeMessage("stepFailure", err.toString)))
|
||||
}
|
||||
|
||||
private lazy val send: String => Unit = remoteSocketApi.makeSender("study-out").apply _
|
||||
|
||||
|
@ -56,30 +178,39 @@ private final class StudyRemoteSocket(
|
|||
import JsonView._
|
||||
import jsonView.membersWrites
|
||||
import lila.tree.Node.{ openingWriter, commentWriter, glyphsWriter, shapesWrites, clockWrites }
|
||||
def version(tpe: String, data: JsObject) = rooms.tell(studyId.value, NotifyVersion(tpe, data))
|
||||
def notify(tpe: String, data: JsObject) = rooms.tell(studyId.value, makeMessage(tpe, data))
|
||||
msg match {
|
||||
case SetPath(pos, who) => tellRoom(studyId, NotifyVersion("path", Json.obj(
|
||||
"p" -> pos,
|
||||
"w" -> who
|
||||
)))
|
||||
case SetLiking(liking, who) => tellRoom(studyId, makeMessage("liking", Json.obj(
|
||||
"l" -> liking,
|
||||
"w" -> who
|
||||
)))
|
||||
case SetPath(pos, who) => version("path", Json.obj("p" -> pos, "w" -> who))
|
||||
case SetLiking(liking, who) => notify("liking", Json.obj("l" -> liking, "w" -> who))
|
||||
}
|
||||
}
|
||||
|
||||
private def tellRoom(studyId: Study.Id, msg: Any): Unit =
|
||||
rooms.tell(studyId.value, msg)
|
||||
private val InviteLimitPerUser = new lila.memo.RateLimit[User.ID](
|
||||
credits = 50,
|
||||
duration = 24 hour,
|
||||
name = "study invites per user",
|
||||
key = "study_invite.user"
|
||||
)
|
||||
}
|
||||
|
||||
object StudyRemoteSocket {
|
||||
|
||||
object Protocol {
|
||||
|
||||
object In {
|
||||
|
||||
case class TellStudySri(studyId: Study.Id, tellSri: P.In.TellSri) extends P.In
|
||||
|
||||
val reader: P.In.Reader = raw => RP.In.reader(raw)
|
||||
|
||||
// val studyReader: P.In.Reader = raw => raw.path match {
|
||||
// case _ => none
|
||||
// }
|
||||
val studyReader: P.In.Reader = raw => raw.path match {
|
||||
case "tell/study/sri" => raw.get(4) {
|
||||
case arr @ Array(studyId, _, _, _) => P.In.tellSriMapper.lift(arr drop 1).flatten map {
|
||||
TellStudySri(Study.Id(studyId), _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Data {
|
||||
import lila.common.PimpedJson._
|
||||
|
@ -91,30 +222,34 @@ private final class StudyRemoteSocket(
|
|||
case class AtPosition(path: String, chapterId: Chapter.Id) {
|
||||
def ref = Position.Ref(chapterId, Path(path))
|
||||
}
|
||||
implicit val chapterIdReader = stringIsoReader(Chapter.idIso)
|
||||
implicit val chapterNameReader = stringIsoReader(Chapter.nameIso)
|
||||
implicit val chapterIdReader: Reads[Chapter.Id] = stringIsoReader(Chapter.idIso)
|
||||
implicit val chapterNameReader: Reads[Chapter.Name] = stringIsoReader(Chapter.nameIso)
|
||||
implicit val atPositionReader: Reads[AtPosition] = (
|
||||
(__ \ "path").read[String] and
|
||||
(__ \ "ch").read[Chapter.Id]
|
||||
)(AtPosition.apply _)
|
||||
case class SetRole(userId: String, role: String)
|
||||
implicit val SetRoleReader = Json.reads[SetRole]
|
||||
implicit val ChapterDataReader = Json.reads[ChapterMaker.Data]
|
||||
implicit val ChapterEditDataReader = Json.reads[ChapterMaker.EditData]
|
||||
implicit val ChapterDescDataReader = Json.reads[ChapterMaker.DescData]
|
||||
implicit val StudyDataReader = Json.reads[Study.Data]
|
||||
implicit val setTagReader = Json.reads[actorApi.SetTag]
|
||||
// implicit val gamebookReader = Json.reads[Gamebook]
|
||||
implicit val explorerGame = Json.reads[actorApi.ExplorerGame]
|
||||
implicit val SetRoleReader: Reads[SetRole] = Json.reads[SetRole]
|
||||
implicit val ChapterDataReader: Reads[ChapterMaker.Data] = Json.reads[ChapterMaker.Data]
|
||||
implicit val ChapterEditDataReader: Reads[ChapterMaker.EditData] = Json.reads[ChapterMaker.EditData]
|
||||
implicit val ChapterDescDataReader: Reads[ChapterMaker.DescData] = Json.reads[ChapterMaker.DescData]
|
||||
implicit val StudyDataReader: Reads[Study.Data] = Json.reads[Study.Data]
|
||||
implicit val setTagReader: Reads[actorApi.SetTag] = Json.reads[actorApi.SetTag]
|
||||
implicit val gamebookReader: Reads[Gamebook] = Json.reads[Gamebook]
|
||||
implicit val explorerGame: Reads[actorApi.ExplorerGame] = Json.reads[actorApi.ExplorerGame]
|
||||
}
|
||||
}
|
||||
|
||||
object Out {
|
||||
}
|
||||
}
|
||||
}
|
||||
// case class IsPresent(roomId: RoomId, userId: User.ID, present: Boolean) extends P.In
|
||||
|
||||
object StudyRemoteSocket {
|
||||
// val isPresentReader: P.In.Reader = raw => raw.path match {
|
||||
// case "room/present" => raw.args.split(" ", 3) match {
|
||||
// case Array(roomId, userId, present) => IsPresent(RoomId(roomId), userId, present).some
|
||||
// case _ => none
|
||||
// }
|
||||
// case _ => none
|
||||
// }
|
||||
}
|
||||
|
||||
sealed trait Out
|
||||
|
||||
|
|
Loading…
Reference in New Issue