lila/app/controllers/Puzzle.scala

267 lines
8.2 KiB
Scala
Raw Normal View History

2014-02-01 16:41:49 -07:00
package controllers
2016-11-26 07:49:25 -07:00
import play.api.libs.json._
import scala.util.chaining._
2014-02-01 16:41:49 -07:00
import lila.api.Context
2014-02-01 16:41:49 -07:00
import lila.app._
2019-12-04 18:47:46 -07:00
import lila.common.config.MaxPerSecond
2017-11-23 15:17:01 -07:00
import lila.puzzle.{ PuzzleId, Result, Puzzle => PuzzleModel, UserInfos }
2014-02-01 16:41:49 -07:00
import views._
2019-12-04 18:47:46 -07:00
final class Puzzle(
env: Env,
2019-12-05 14:51:18 -07:00
apiC: => Api
2019-12-04 18:47:46 -07:00
) extends LilaController(env) {
2014-02-01 16:41:49 -07:00
private def renderJson(
2019-12-13 07:30:20 -07:00
puzzle: PuzzleModel,
userInfos: Option[UserInfos],
mode: String,
voted: Option[Boolean],
round: Option[lila.puzzle.Round] = None
2020-05-05 22:11:15 -06:00
)(implicit ctx: Context): Fu[JsObject] =
env.puzzle.jsonView(
puzzle = puzzle,
userInfos = userInfos,
round = round,
mode = mode,
mobileApi = ctx.mobileApiVersion,
voted = voted
)
2016-11-26 07:49:25 -07:00
private def renderShow(puzzle: PuzzleModel, mode: String)(implicit ctx: Context) =
2019-12-04 18:47:46 -07:00
env.puzzle userInfos ctx.me flatMap { infos =>
2016-12-07 07:40:56 -07:00
renderJson(puzzle = puzzle, userInfos = infos, mode = mode, voted = none) map { json =>
EnableSharedArrayBuffer(
Ok(views.html.puzzle.show(puzzle, data = json, pref = env.puzzle.jsonView.pref(ctx.pref)))
)
2016-11-26 07:49:25 -07:00
}
}
2020-05-05 22:11:15 -06:00
def daily =
Open { implicit ctx =>
NoBot {
OptionFuResult(env.puzzle.daily.get flatMap {
_.map(_.id) ?? env.puzzle.api.puzzle.find
}) { puzzle =>
negotiate(
html = renderShow(puzzle, "play"),
api = _ => puzzleJson(puzzle) map { Ok(_) }
) map NoCache
}
}
}
2020-05-05 22:11:15 -06:00
def home =
Open { implicit ctx =>
NoBot {
env.puzzle.selector(ctx.me) flatMap { puzzle =>
renderShow(puzzle, if (ctx.isAuth) "play" else "try")
}
}
2014-02-02 13:03:32 -07:00
}
2020-05-05 22:11:15 -06:00
def show(id: PuzzleId) =
Open { implicit ctx =>
NoBot {
OptionFuResult(env.puzzle.api.puzzle find id) { puzzle =>
renderShow(puzzle, "play")
}
}
2014-02-07 15:36:56 -07:00
}
2020-05-05 22:11:15 -06:00
def load(id: PuzzleId) =
Open { implicit ctx =>
NoBot {
XhrOnly {
OptionFuOk(env.puzzle.api.puzzle find id)(puzzleJson) map (_ as JSON)
}
}
2014-09-28 08:41:55 -06:00
}
2014-09-15 11:11:24 -06:00
2016-02-22 20:35:06 -07:00
private def puzzleJson(puzzle: PuzzleModel)(implicit ctx: Context) =
2019-12-04 18:47:46 -07:00
env.puzzle userInfos ctx.me flatMap { infos =>
2018-07-20 04:21:06 -06:00
renderJson(puzzle, infos, if (ctx.isAuth) "play" else "try", voted = none)
2016-08-25 13:01:06 -06:00
}
2016-02-22 20:35:06 -07:00
2014-02-09 10:50:08 -07:00
// XHR load next play puzzle
2020-05-05 22:11:15 -06:00
def newPuzzle =
Open { implicit ctx =>
NoBot {
XhrOnly {
env.puzzle.selector(ctx.me) flatMap puzzleJson map { json =>
Ok(json) as JSON
}
}
2017-03-08 07:02:44 -07:00
}
2014-09-15 11:11:24 -06:00
}
2014-04-21 10:20:49 -06:00
// mobile app BC
2020-05-05 22:11:15 -06:00
def round(id: PuzzleId) =
OpenBody { implicit ctx =>
implicit val req = ctx.body
2019-12-04 18:47:46 -07:00
OptionFuResult(env.puzzle.api.puzzle find id) { puzzle =>
2020-05-05 22:11:15 -06:00
lila.mon.puzzle.round.attempt(puzzle.mate, ctx.isAuth, "old")
2020-07-22 04:52:52 -06:00
env.puzzle.forms.round
.bindFromRequest()
.fold(
2020-05-05 22:11:15 -06:00
jsonFormError,
2020-07-22 04:52:52 -06:00
resultInt => {
val result = Result(resultInt == 1)
2020-05-05 22:11:15 -06:00
ctx.me match {
case Some(me) =>
for {
isStudent <- env.clas.api.student.isStudent(me.id)
2020-07-22 04:52:52 -06:00
(round, mode) <-
env.puzzle.finisher(puzzle, me, result, mobile = true, isStudent = isStudent)
2020-05-05 22:11:15 -06:00
me2 <- if (mode.rated) env.user.repo byId me.id map (_ | me) else fuccess(me)
infos <- env.puzzle userInfos me2
voted <- ctx.me.?? { env.puzzle.api.vote.value(puzzle.id, _) }
2020-07-22 04:52:52 -06:00
data <- renderJson(puzzle, infos.some, "view", voted = voted, round = round.some)
} yield {
val d2 = if (mode.rated) data else data ++ Json.obj("win" -> result.win)
Ok(d2)
}
2020-05-05 22:11:15 -06:00
case None =>
env.puzzle.finisher.incPuzzleAttempts(puzzle)
2020-07-22 04:52:52 -06:00
renderJson(puzzle, none, "view", voted = none) map { data =>
val d2 = data ++ Json.obj("win" -> result.win)
Ok(d2)
}
2020-05-05 22:11:15 -06:00
}
2020-07-22 04:52:52 -06:00
}
2020-05-05 22:11:15 -06:00
) map (_ as JSON)
2020-07-22 04:52:52 -06:00
}
}
// new API
def round2(id: PuzzleId) =
OpenBody { implicit ctx =>
NoBot {
implicit val req = ctx.body
OptionFuResult(env.puzzle.api.puzzle find id) { puzzle =>
lila.mon.puzzle.round.attempt(puzzle.mate, ctx.isAuth, "new")
env.puzzle.forms.round
.bindFromRequest()
.fold(
jsonFormError,
resultInt =>
ctx.me match {
case Some(me) =>
for {
isStudent <- env.clas.api.student.isStudent(me.id)
(round, mode) <- env.puzzle.finisher(
puzzle = puzzle,
user = me,
result = Result(resultInt == 1),
mobile = lila.api.Mobile.Api.requested(ctx.req),
isStudent = isStudent
)
me2 <- if (mode.rated) env.user.repo byId me.id map (_ | me) else fuccess(me)
infos <- env.puzzle userInfos me2
voted <- ctx.me.?? { env.puzzle.api.vote.value(puzzle.id, _) }
} yield Ok(
Json.obj(
2020-08-16 06:42:29 -06:00
"user" -> lila.puzzle.JsonView.infos(isOldMobile = false)(infos),
2020-07-22 04:52:52 -06:00
"round" -> lila.puzzle.JsonView.round(round),
"voted" -> voted
)
)
case None =>
env.puzzle.finisher.incPuzzleAttempts(puzzle)
Ok(Json.obj("user" -> false)).fuccess
}
) map (_ as JSON)
2020-05-05 22:11:15 -06:00
}
}
}
def vote(id: PuzzleId) =
AuthBody { implicit ctx => me =>
NoBot {
implicit val req = ctx.body
2020-07-22 04:52:52 -06:00
env.puzzle.forms.vote
.bindFromRequest()
.fold(
jsonFormError,
vote =>
env.puzzle.api.vote.find(id, me) flatMap { v =>
env.puzzle.api.vote.update(id, me, v, vote == 1)
2020-09-21 01:28:28 -06:00
} map { case (p, a) =>
if (vote == 1) lila.mon.puzzle.vote.up.increment()
else lila.mon.puzzle.vote.down.increment()
Ok(Json.arr(a.value, p.vote.sum))
2020-07-22 04:52:52 -06:00
}
) map (_ as JSON)
2020-05-05 22:11:15 -06:00
}
}
/* Mobile API: select a bunch of puzzles for offline use */
2020-05-05 22:11:15 -06:00
def batchSelect =
Auth { implicit ctx => me =>
negotiate(
html = notFound,
api = _ =>
for {
puzzles <- env.puzzle.batch.select(
me,
nb = getInt("nb") getOrElse 50 atLeast 1 atMost 100,
after = getInt("after")
)
userInfo <- env.puzzle userInfos me
json <- env.puzzle.jsonView.batch(puzzles, userInfo)
} yield Ok(json) as JSON
)
}
2017-11-23 15:17:01 -07:00
/* Mobile API: tell the server about puzzles solved while offline */
2020-05-05 22:11:15 -06:00
def batchSolve =
AuthBody(parse.json) { implicit ctx => me =>
import lila.puzzle.PuzzleBatch._
ctx.body.body
.validate[SolveData]
.fold(
err => BadRequest(err.toString).fuccess,
data =>
negotiate(
html = notFound,
api = _ =>
for {
_ <- env.puzzle.batch.solve(me, data)
me2 <- env.user.repo byId me.id map (_ | me)
infos <- env.puzzle userInfos me2
} yield Ok(
Json.obj(
2020-08-16 06:42:29 -06:00
"user" -> lila.puzzle.JsonView.infos(isOldMobile = false)(infos)
2020-05-05 22:11:15 -06:00
)
2019-12-13 07:30:20 -07:00
)
2020-05-05 22:11:15 -06:00
)
)
}
2020-05-05 22:11:15 -06:00
def frame =
Action.async { implicit req =>
env.puzzle.daily.get map {
case None => NotFound
case Some(daily) => html.puzzle.embed(daily)
}
2020-05-05 22:11:15 -06:00
}
def activity =
Scoped(_.Puzzle.Read) { req => me =>
val config = lila.puzzle.PuzzleActivity.Config(
user = me,
max = getInt("max", req) map (_ atLeast 1),
perSecond = MaxPerSecond(20)
)
apiC
.GlobalConcurrencyLimitPerIpAndUserOption(req, me.some)(env.puzzle.activity.stream(config)) {
source =>
Ok.chunked(source).as(ndJsonContentType) pipe noProxyBuffer
}
.fuccess
}
2014-02-01 16:41:49 -07:00
}