lila/modules/study/src/main/JsonView.scala

212 lines
7.3 KiB
Scala

package lila.study
import chess.format.{ FEN, Uci }
import chess.Pos
import play.api.libs.json._
import scala.util.chaining._
import lila.common.Json._
import lila.socket.Socket.Sri
import lila.tree.Node.Shape
import lila.user.User
final class JsonView(
studyRepo: StudyRepo,
lightUserApi: lila.user.LightUserApi
)(implicit ec: scala.concurrent.ExecutionContext) {
import JsonView._
def apply(
study: Study,
chapters: List[Chapter.Metadata],
currentChapter: Chapter,
me: Option[User]
) = {
def allowed(selection: Settings.UserSelection): Boolean =
Settings.UserSelection.allows(selection, study, me.map(_.id))
me.?? { studyRepo.liked(study, _) } map { liked =>
studyWrites.writes(study) ++ Json
.obj(
"liked" -> liked,
"features" -> Json.obj(
"cloneable" -> allowed(study.settings.cloneable),
"chat" -> allowed(study.settings.chat),
"sticky" -> study.settings.sticky,
"description" -> study.settings.description
),
"topics" -> study.topicsOrEmpty,
"chapters" -> chapters.map(chapterMetadataWrites.writes),
"chapter" -> Json
.obj(
"id" -> currentChapter.id,
"ownerId" -> currentChapter.ownerId,
"setup" -> currentChapter.setup,
"tags" -> currentChapter.tags,
"features" -> Json.obj(
"computer" -> allowed(study.settings.computer),
"explorer" -> allowed(study.settings.explorer)
)
)
.add("description", currentChapter.description)
.add("serverEval", currentChapter.serverEval)
.add("relay", currentChapter.relay)(relayWrites)
.pipe(addChapterMode(currentChapter))
)
.add("description", study.description)
}
}
def chapterConfig(c: Chapter) =
Json
.obj(
"id" -> c.id,
"name" -> c.name,
"orientation" -> c.setup.orientation
)
.add("description", c.description) pipe addChapterMode(c)
def pagerData(s: Study.WithChaptersAndLiked) =
Json.obj(
"id" -> s.study.id.value,
"name" -> s.study.name.value,
"liked" -> s.liked,
"likes" -> s.study.likes.value,
"updatedAt" -> s.study.updatedAt,
"owner" -> lightUserApi.sync(s.study.ownerId),
"chapters" -> s.chapters.take(4),
"members" -> s.study.members.members.values.take(4)
)
private def addChapterMode(c: Chapter)(js: JsObject): JsObject =
js.add("practice", c.isPractice)
.add("gamebook", c.isGamebook)
.add("conceal", c.conceal)
implicit private[study] val memberRoleWrites = Writes[StudyMember.Role] { r =>
JsString(r.id)
}
implicit private[study] val memberWrites: Writes[StudyMember] = Writes[StudyMember] { m =>
Json.obj("user" -> lightUserApi.syncFallback(m.id), "role" -> m.role)
}
implicit private[study] val membersWrites: Writes[StudyMembers] = Writes[StudyMembers] { m =>
Json toJson m.members
}
implicit private val studyWrites = OWrites[Study] { s =>
Json
.obj(
"id" -> s.id,
"name" -> s.name,
"members" -> s.members,
"position" -> s.position,
"ownerId" -> s.ownerId,
"settings" -> s.settings,
"visibility" -> s.visibility,
"createdAt" -> s.createdAt,
"secondsSinceUpdate" -> (nowSeconds - s.updatedAt.getSeconds).toInt,
"from" -> s.from,
"likes" -> s.likes
)
.add("isNew" -> s.isNew)
}
}
object JsonView {
case class JsData(study: JsObject, analysis: JsObject)
implicit val studyIdWrites: Writes[Study.Id] = stringIsoWriter(Study.idIso)
implicit val studyNameWrites: Writes[Study.Name] = stringIsoWriter(Study.nameIso)
implicit val studyIdNameWrites = OWrites[Study.IdName] { s =>
Json.obj("id" -> s._id, "name" -> s.name)
}
implicit val chapterIdWrites: Writes[Chapter.Id] = stringIsoWriter(Chapter.idIso)
implicit val chapterNameWrites: Writes[Chapter.Name] = stringIsoWriter(Chapter.nameIso)
implicit private val posReader: Reads[Pos] = Reads[Pos] { v =>
(v.asOpt[String] flatMap Pos.fromKey).fold[JsResult[Pos]](JsError(Nil))(JsSuccess(_))
}
implicit private[study] val pathWrites: Writes[Path] = Writes[Path] { p =>
JsString(p.toString)
}
implicit private[study] val sriWriter: Writes[Sri] = Writes[Sri] { sri =>
JsString(sri.value)
}
implicit private[study] val visibilityWriter: Writes[Study.Visibility] = Writes[Study.Visibility] { v =>
JsString(v.key)
}
implicit private[study] val fromWriter: Writes[Study.From] = Writes[Study.From] {
case Study.From.Scratch => JsString("scratch")
case Study.From.Game(id) => Json.obj("game" -> id)
case Study.From.Study(id) => Json.obj("study" -> id)
case Study.From.Relay(id) => Json.obj("relay" -> id)
}
implicit private[study] val userSelectionWriter = Writes[Settings.UserSelection] { v =>
JsString(v.key)
}
implicit private[study] val settingsWriter: Writes[Settings] = Json.writes[Settings]
implicit private[study] val shapeReader: Reads[Shape] = Reads[Shape] { js =>
js.asOpt[JsObject]
.flatMap { o =>
for {
brush <- o str "brush"
orig <- o.get[Pos]("orig")
} yield o.get[Pos]("dest") match {
case Some(dest) => Shape.Arrow(brush, orig, dest)
case _ => Shape.Circle(brush, orig)
}
}
.fold[JsResult[Shape]](JsError(Nil))(JsSuccess(_))
}
implicit private val plyWrites = Writes[Chapter.Ply] { p =>
JsNumber(p.value)
}
implicit private val variantWrites = OWrites[chess.variant.Variant] { v =>
Json.obj("key" -> v.key, "name" -> v.name)
}
implicit val pgnTagWrites: Writes[chess.format.pgn.Tag] = Writes[chess.format.pgn.Tag] { t =>
Json.arr(t.name.toString, t.value)
}
implicit val pgnTagsWrites = Writes[chess.format.pgn.Tags] { tags =>
JsArray(tags.value map pgnTagWrites.writes)
}
implicit private val chapterSetupWrites = Json.writes[Chapter.Setup]
implicit val chapterMetadataWrites = OWrites[Chapter.Metadata] { c =>
Json
.obj("id" -> c._id, "name" -> c.name)
.add("ongoing", c.looksOngoing)
.add("res" -> c.resultStr)
}
implicit private[study] val positionRefWrites: Writes[Position.Ref] = Json.writes[Position.Ref]
implicit private val likesWrites: Writes[Study.Likes] = Writes[Study.Likes] { p =>
JsNumber(p.value)
}
implicit private[study] val likingRefWrites: Writes[Study.Liking] = Json.writes[Study.Liking]
implicit val relayWrites = OWrites[Chapter.Relay] { r =>
Json.obj(
"path" -> r.path,
"secondsSinceLastMove" -> r.secondsSinceLastMove
)
}
implicit private[study] val serverEvalWrites: Writes[Chapter.ServerEval] = Json.writes[Chapter.ServerEval]
implicit private[study] val whoWriter: Writes[actorApi.Who] = Writes[actorApi.Who] { w =>
Json.obj("u" -> w.u, "s" -> w.sri)
}
implicit val topicWrites: Writes[StudyTopic] = stringIsoWriter(StudyTopic.topicIso)
implicit val topicsWrites: Writes[StudyTopics] = Writes[StudyTopics] { topics =>
JsArray(topics.value map topicWrites.writes)
}
}