scalafmt 2.7.1
parent
c794d88af9
commit
aaf88bc62c
|
@ -1,4 +1,4 @@
|
|||
version = "2.6.3"
|
||||
version = "2.7.1"
|
||||
align.preset = more
|
||||
maxColumn = 110
|
||||
spaces.inImportCurlyBraces = true
|
||||
|
|
|
@ -130,10 +130,9 @@ final class Env(
|
|||
Future {
|
||||
puzzle.daily.get
|
||||
}.flatMap(identity)
|
||||
.withTimeoutDefault(50.millis, none) recover {
|
||||
case e: Exception =>
|
||||
lila.log("preloader").warn("daily puzzle", e)
|
||||
none
|
||||
.withTimeoutDefault(50.millis, none) recover { case e: Exception =>
|
||||
lila.log("preloader").warn("daily puzzle", e)
|
||||
none
|
||||
}
|
||||
|
||||
def scheduler = system.scheduler
|
||||
|
@ -163,14 +162,13 @@ final class Env(
|
|||
}
|
||||
} yield Bus.publish(lila.hub.actorApi.security.CloseAccount(u.id), "accountClose")
|
||||
|
||||
Bus.subscribeFun("garbageCollect") {
|
||||
case lila.hub.actorApi.security.GarbageCollect(userId) =>
|
||||
// GC can be aborted by reverting the initial SB mark
|
||||
user.repo.isTroll(userId) foreach { troll =>
|
||||
if (troll) scheduler.scheduleOnce(1.second) {
|
||||
closeAccount(userId, self = false)
|
||||
}
|
||||
Bus.subscribeFun("garbageCollect") { case lila.hub.actorApi.security.GarbageCollect(userId) =>
|
||||
// GC can be aborted by reverting the initial SB mark
|
||||
user.repo.isTroll(userId) foreach { troll =>
|
||||
if (troll) scheduler.scheduleOnce(1.second) {
|
||||
closeAccount(userId, self = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
system.actorOf(Props(new actor.Renderer), name = config.get[String]("app.renderer.name"))
|
||||
|
|
|
@ -44,10 +44,9 @@ final class Account(
|
|||
} { username =>
|
||||
env.user.repo
|
||||
.setUsernameCased(me.id, username) inject
|
||||
Redirect(routes.User show me.username).flashSuccess recover {
|
||||
case e =>
|
||||
Redirect(routes.User show me.username).flashSuccess recover { case e =>
|
||||
BadRequest(html.account.username(me, env.user.forms.username(me))).flashFailure(e.getMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,21 +60,21 @@ final class Account(
|
|||
env.round.proxyRepo.urgentGames(me) zip
|
||||
env.challenge.api.countInFor.get(me.id) zip
|
||||
env.playban.api.currentBan(me.id) map {
|
||||
case nbFollowers ~ prefs ~ povs ~ nbChallenges ~ playban =>
|
||||
Ok {
|
||||
import lila.pref.JsonView._
|
||||
env.user.jsonView(me) ++ Json
|
||||
.obj(
|
||||
"prefs" -> prefs,
|
||||
"nowPlaying" -> JsArray(povs take 50 map env.api.lobbyApi.nowPlaying),
|
||||
"nbFollowers" -> nbFollowers,
|
||||
"nbChallenges" -> nbChallenges
|
||||
)
|
||||
.add("kid" -> me.kid)
|
||||
.add("troll" -> me.marks.troll)
|
||||
.add("playban" -> playban)
|
||||
}.withHeaders(CACHE_CONTROL -> s"max-age=15")
|
||||
}
|
||||
case nbFollowers ~ prefs ~ povs ~ nbChallenges ~ playban =>
|
||||
Ok {
|
||||
import lila.pref.JsonView._
|
||||
env.user.jsonView(me) ++ Json
|
||||
.obj(
|
||||
"prefs" -> prefs,
|
||||
"nowPlaying" -> JsArray(povs take 50 map env.api.lobbyApi.nowPlaying),
|
||||
"nbFollowers" -> nbFollowers,
|
||||
"nbChallenges" -> nbChallenges
|
||||
)
|
||||
.add("kid" -> me.kid)
|
||||
.add("troll" -> me.marks.troll)
|
||||
.add("playban" -> playban)
|
||||
}.withHeaders(CACHE_CONTROL -> s"max-age=15")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -147,8 +146,8 @@ final class Account(
|
|||
env.security.store.closeAllSessionsOf(me.id) >>
|
||||
env.push.webSubscriptionApi.unsubscribeByUser(me) >>
|
||||
env.security.api.saveAuthentication(me.id, ctx.mobileApiVersion) map { sessionId =>
|
||||
result.withCookies(env.lilaCookie.session(env.security.api.sessionIdKey, sessionId))
|
||||
}
|
||||
result.withCookies(env.lilaCookie.session(env.security.api.sessionIdKey, sessionId))
|
||||
}
|
||||
|
||||
private def emailForm(user: UserModel) =
|
||||
env.user.repo email user.id flatMap {
|
||||
|
@ -201,17 +200,16 @@ final class Account(
|
|||
def emailConfirm(token: String) =
|
||||
Open { implicit ctx =>
|
||||
env.security.emailChange.confirm(token) flatMap {
|
||||
_ ?? {
|
||||
case (user, prevEmail) =>
|
||||
(prevEmail.exists(_.isNoReply) ?? env.clas.api.student.release(user)) >>
|
||||
auth.authenticateUser(
|
||||
user,
|
||||
result =
|
||||
if (prevEmail.exists(_.isNoReply))
|
||||
Some(_ => Redirect(routes.User.show(user.username)).flashSuccess)
|
||||
else
|
||||
Some(_ => Redirect(routes.Account.email()).flashSuccess)
|
||||
)
|
||||
_ ?? { case (user, prevEmail) =>
|
||||
(prevEmail.exists(_.isNoReply) ?? env.clas.api.student.release(user)) >>
|
||||
auth.authenticateUser(
|
||||
user,
|
||||
result =
|
||||
if (prevEmail.exists(_.isNoReply))
|
||||
Some(_ => Redirect(routes.User.show(user.username)).flashSuccess)
|
||||
else
|
||||
Some(_ => Redirect(routes.Account.email()).flashSuccess)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,8 +352,8 @@ final class Account(
|
|||
Auth { implicit ctx => me =>
|
||||
env.security.api.dedup(me.id, ctx.req) >>
|
||||
env.security.api.locatedOpenSessions(me.id, 50) map { sessions =>
|
||||
Ok(html.account.security(me, sessions, currentSessionId))
|
||||
}
|
||||
Ok(html.account.security(me, sessions, currentSessionId))
|
||||
}
|
||||
}
|
||||
|
||||
def signout(sessionId: String) =
|
||||
|
|
|
@ -50,8 +50,7 @@ final class Analyse(
|
|||
initialFen,
|
||||
analysis = none,
|
||||
PgnDump.WithFlags(clocks = false)
|
||||
) flatMap {
|
||||
case analysis ~ analysisInProgress ~ simul ~ chat ~ crosstable ~ bookmarked ~ pgn =>
|
||||
) flatMap { case analysis ~ analysisInProgress ~ simul ~ chat ~ crosstable ~ bookmarked ~ pgn =>
|
||||
env.api.roundApi.review(
|
||||
pov,
|
||||
lila.api.Mobile.Api.currentVersion,
|
||||
|
@ -87,7 +86,7 @@ final class Analyse(
|
|||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,12 @@ final class Auth(
|
|||
private def goodReferrer(referrer: String): Boolean =
|
||||
referrer.nonEmpty &&
|
||||
!sillyLoginReferrers(referrer) && Try {
|
||||
val url = Url.parse(referrer)
|
||||
url.schemeOption.fold(true)(scheme => scheme == "http" || scheme == "https") &&
|
||||
url.hostOption.fold(true)(host =>
|
||||
s".${host.value}".endsWith(s".${AbsoluteUrl.parse(env.net.baseUrl.value).host.value}")
|
||||
)
|
||||
}.getOrElse(false)
|
||||
val url = Url.parse(referrer)
|
||||
url.schemeOption.fold(true)(scheme => scheme == "http" || scheme == "https") &&
|
||||
url.hostOption.fold(true)(host =>
|
||||
s".${host.value}".endsWith(s".${AbsoluteUrl.parse(env.net.baseUrl.value).host.value}")
|
||||
)
|
||||
}.getOrElse(false)
|
||||
|
||||
def authenticateUser(u: UserModel, result: Option[String => Result] = None)(implicit
|
||||
ctx: Context
|
||||
|
@ -106,39 +106,38 @@ final class Auth(
|
|||
),
|
||||
usernameOrEmail =>
|
||||
HasherRateLimit(usernameOrEmail, ctx.req) { chargeIpLimiter =>
|
||||
api.loadLoginForm(usernameOrEmail) flatMap {
|
||||
loginForm =>
|
||||
loginForm
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
err => {
|
||||
chargeIpLimiter(1)
|
||||
negotiate(
|
||||
html = fuccess {
|
||||
err.errors match {
|
||||
case List(FormError("", List(err), _)) if is2fa(err) => Ok(err)
|
||||
case _ => Unauthorized(html.auth.login(err, referrer))
|
||||
}
|
||||
},
|
||||
api = _ =>
|
||||
Unauthorized(ridiculousBackwardCompatibleJsonError(errorsAsJson(err))).fuccess
|
||||
)
|
||||
},
|
||||
result =>
|
||||
result.toOption match {
|
||||
case None => InternalServerError("Authentication error").fuccess
|
||||
case Some(u) if u.disabled =>
|
||||
negotiate(
|
||||
html = redirectTo(routes.Account.reopen().url).fuccess,
|
||||
api = _ => Unauthorized(jsonError("This account is closed.")).fuccess
|
||||
)
|
||||
case Some(u) =>
|
||||
env.user.repo.email(u.id) foreach {
|
||||
_ foreach { garbageCollect(u, _) }
|
||||
}
|
||||
authenticateUser(u, Some(redirectTo))
|
||||
}
|
||||
)
|
||||
api.loadLoginForm(usernameOrEmail) flatMap { loginForm =>
|
||||
loginForm
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
err => {
|
||||
chargeIpLimiter(1)
|
||||
negotiate(
|
||||
html = fuccess {
|
||||
err.errors match {
|
||||
case List(FormError("", List(err), _)) if is2fa(err) => Ok(err)
|
||||
case _ => Unauthorized(html.auth.login(err, referrer))
|
||||
}
|
||||
},
|
||||
api = _ =>
|
||||
Unauthorized(ridiculousBackwardCompatibleJsonError(errorsAsJson(err))).fuccess
|
||||
)
|
||||
},
|
||||
result =>
|
||||
result.toOption match {
|
||||
case None => InternalServerError("Authentication error").fuccess
|
||||
case Some(u) if u.disabled =>
|
||||
negotiate(
|
||||
html = redirectTo(routes.Account.reopen().url).fuccess,
|
||||
api = _ => Unauthorized(jsonError("This account is closed.")).fuccess
|
||||
)
|
||||
case Some(u) =>
|
||||
env.user.repo.email(u.id) foreach {
|
||||
_ foreach { garbageCollect(u, _) }
|
||||
}
|
||||
authenticateUser(u, Some(redirectTo))
|
||||
}
|
||||
)
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
)
|
||||
|
@ -247,22 +246,21 @@ final class Auth(
|
|||
err => BadRequest(html.auth.checkYourEmail(userEmail.some, err.some)).fuccess,
|
||||
email =>
|
||||
env.user.repo.named(userEmail.username) flatMap {
|
||||
_.fold(Redirect(routes.Auth.signup()).fuccess) {
|
||||
user =>
|
||||
env.user.repo.mustConfirmEmail(user.id) flatMap {
|
||||
case false => Redirect(routes.Auth.login()).fuccess
|
||||
case _ =>
|
||||
val newUserEmail = userEmail.copy(email = EmailAddress(email))
|
||||
EmailConfirmRateLimit(newUserEmail, ctx.req) {
|
||||
lila.mon.email.send.fix.increment()
|
||||
env.user.repo.setEmail(user.id, newUserEmail.email) >>
|
||||
env.security.emailConfirm.send(user, newUserEmail.email) inject {
|
||||
_.fold(Redirect(routes.Auth.signup()).fuccess) { user =>
|
||||
env.user.repo.mustConfirmEmail(user.id) flatMap {
|
||||
case false => Redirect(routes.Auth.login()).fuccess
|
||||
case _ =>
|
||||
val newUserEmail = userEmail.copy(email = EmailAddress(email))
|
||||
EmailConfirmRateLimit(newUserEmail, ctx.req) {
|
||||
lila.mon.email.send.fix.increment()
|
||||
env.user.repo.setEmail(user.id, newUserEmail.email) >>
|
||||
env.security.emailConfirm.send(user, newUserEmail.email) inject {
|
||||
Redirect(routes.Auth.checkYourEmail()) withCookies
|
||||
lila.security.EmailConfirm.cookie
|
||||
.make(env.lilaCookie, user, newUserEmail.email)(ctx.req)
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -239,43 +239,42 @@ final class Challenge(
|
|||
val cost = if (me.isApiHog) 0 else 1
|
||||
ChallengeIpRateLimit(HTTPRequest lastRemoteAddress req, cost = cost) {
|
||||
ChallengeUserRateLimit(me.id, cost = cost) {
|
||||
env.user.repo enabledById userId.toLowerCase flatMap {
|
||||
destUser =>
|
||||
import lila.challenge.Challenge._
|
||||
val timeControl = config.clock map {
|
||||
TimeControl.Clock.apply
|
||||
} orElse config.days.map {
|
||||
TimeControl.Correspondence.apply
|
||||
} getOrElse TimeControl.Unlimited
|
||||
val challenge = lila.challenge.Challenge
|
||||
.make(
|
||||
variant = config.variant,
|
||||
initialFen = config.position,
|
||||
timeControl = timeControl,
|
||||
mode = config.mode,
|
||||
color = config.color.name,
|
||||
challenger = ChallengeModel.toRegistered(config.variant, timeControl)(me),
|
||||
destUser = destUser,
|
||||
rematchOf = none
|
||||
)
|
||||
(destUser, config.acceptByToken) match {
|
||||
case (Some(dest), Some(strToken)) => apiChallengeAccept(dest, challenge, strToken)
|
||||
case _ =>
|
||||
destUser ?? { env.challenge.granter(me.some, _, config.perfType) } flatMap {
|
||||
case Some(denied) =>
|
||||
BadRequest(jsonError(lila.challenge.ChallengeDenied.translated(denied))).fuccess
|
||||
case _ =>
|
||||
(env.challenge.api create challenge) map {
|
||||
case true =>
|
||||
JsonOk(
|
||||
env.challenge.jsonView
|
||||
.show(challenge, SocketVersion(0), lila.challenge.Direction.Out.some)
|
||||
)
|
||||
case false =>
|
||||
BadRequest(jsonError("Challenge not created"))
|
||||
}
|
||||
} map (_ as JSON)
|
||||
}
|
||||
env.user.repo enabledById userId.toLowerCase flatMap { destUser =>
|
||||
import lila.challenge.Challenge._
|
||||
val timeControl = config.clock map {
|
||||
TimeControl.Clock.apply
|
||||
} orElse config.days.map {
|
||||
TimeControl.Correspondence.apply
|
||||
} getOrElse TimeControl.Unlimited
|
||||
val challenge = lila.challenge.Challenge
|
||||
.make(
|
||||
variant = config.variant,
|
||||
initialFen = config.position,
|
||||
timeControl = timeControl,
|
||||
mode = config.mode,
|
||||
color = config.color.name,
|
||||
challenger = ChallengeModel.toRegistered(config.variant, timeControl)(me),
|
||||
destUser = destUser,
|
||||
rematchOf = none
|
||||
)
|
||||
(destUser, config.acceptByToken) match {
|
||||
case (Some(dest), Some(strToken)) => apiChallengeAccept(dest, challenge, strToken)
|
||||
case _ =>
|
||||
destUser ?? { env.challenge.granter(me.some, _, config.perfType) } flatMap {
|
||||
case Some(denied) =>
|
||||
BadRequest(jsonError(lila.challenge.ChallengeDenied.translated(denied))).fuccess
|
||||
case _ =>
|
||||
(env.challenge.api create challenge) map {
|
||||
case true =>
|
||||
JsonOk(
|
||||
env.challenge.jsonView
|
||||
.show(challenge, SocketVersion(0), lila.challenge.Direction.Out.some)
|
||||
)
|
||||
case false =>
|
||||
BadRequest(jsonError("Challenge not created"))
|
||||
}
|
||||
} map (_ as JSON)
|
||||
}
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
}(rateLimitedFu)
|
||||
|
|
|
@ -29,9 +29,9 @@ final class Clas(
|
|||
case _ =>
|
||||
env.clas.api.student.clasIdsOfUser(me.id) flatMap
|
||||
env.clas.api.clas.byIds map {
|
||||
case List(single) => Redirect(routes.Clas.show(single.id.value))
|
||||
case many => Ok(views.html.clas.clas.studentIndex(many))
|
||||
}
|
||||
case List(single) => Redirect(routes.Clas.show(single.id.value))
|
||||
case many => Ok(views.html.clas.clas.studentIndex(many))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,10 +204,9 @@ final class Clas(
|
|||
val studentIds = students.map(_.user.id)
|
||||
env.learn.api.completionPercent(studentIds) zip
|
||||
env.practice.api.progress.completionPercent(studentIds) zip
|
||||
env.coordinate.api.bestScores(studentIds) map {
|
||||
case basic ~ practice ~ coords =>
|
||||
env.coordinate.api.bestScores(studentIds) map { case basic ~ practice ~ coords =>
|
||||
views.html.clas.teacherDashboard.learn(clas, students, basic, practice, coords)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -321,11 +320,10 @@ final class Clas(
|
|||
case _ => none
|
||||
}
|
||||
}
|
||||
.map {
|
||||
case (u, p) =>
|
||||
env.clas.api.student
|
||||
.get(clas, u)
|
||||
.map2(lila.clas.Student.WithPassword(_, lila.user.User.ClearPassword(p)))
|
||||
.map { case (u, p) =>
|
||||
env.clas.api.student
|
||||
.get(clas, u)
|
||||
.map2(lila.clas.Student.WithPassword(_, lila.user.User.ClearPassword(p)))
|
||||
}
|
||||
.sequenceFu
|
||||
.map(_.flatten)
|
||||
|
@ -389,21 +387,19 @@ final class Clas(
|
|||
},
|
||||
data =>
|
||||
env.user.repo named data.username flatMap {
|
||||
_ ?? {
|
||||
user =>
|
||||
import lila.clas.ClasInvite.{ Feedback => F }
|
||||
env.clas.api.invite.create(clas, user, data.realName, me) map {
|
||||
feedback =>
|
||||
Redirect(routes.Clas.studentForm(clas.id.value)).flashing {
|
||||
feedback match {
|
||||
case F.Already => "success" -> s"${user.username} is now a student of the class"
|
||||
case F.Invited => "success" -> s"An invitation has been sent to ${user.username}"
|
||||
case F.Found => "warning" -> s"${user.username} already has a pending invitation"
|
||||
case F.CantMsgKid(url) =>
|
||||
"warning" -> s"${user.username} is a kid account and can't receive your message. You must give them the invitation URL manually: $url"
|
||||
}
|
||||
}
|
||||
_ ?? { user =>
|
||||
import lila.clas.ClasInvite.{ Feedback => F }
|
||||
env.clas.api.invite.create(clas, user, data.realName, me) map { feedback =>
|
||||
Redirect(routes.Clas.studentForm(clas.id.value)).flashing {
|
||||
feedback match {
|
||||
case F.Already => "success" -> s"${user.username} is now a student of the class"
|
||||
case F.Invited => "success" -> s"An invitation has been sent to ${user.username}"
|
||||
case F.Found => "warning" -> s"${user.username} already has a pending invitation"
|
||||
case F.CantMsgKid(url) =>
|
||||
"warning" -> s"${user.username} is a kid account and can't receive your message. You must give them the invitation URL manually: $url"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -474,9 +470,9 @@ final class Clas(
|
|||
WithStudent(clas, username) { s =>
|
||||
env.security.store.closeAllSessionsOf(s.user.id) >>
|
||||
env.clas.api.student.resetPassword(s.student) map { password =>
|
||||
Redirect(routes.Clas.studentShow(clas.id.value, username))
|
||||
.flashing("password" -> password.value)
|
||||
}
|
||||
Redirect(routes.Clas.studentShow(clas.id.value, username))
|
||||
.flashing("password" -> password.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -529,8 +525,8 @@ final class Clas(
|
|||
|
||||
def invitation(id: String) =
|
||||
Auth { implicit ctx => me =>
|
||||
OptionOk(env.clas.api.invite.view(lila.clas.ClasInvite.Id(id), me)) {
|
||||
case (invite -> clas) => views.html.clas.invite.show(clas, invite)
|
||||
OptionOk(env.clas.api.invite.view(lila.clas.ClasInvite.Id(id), me)) { case (invite -> clas) =>
|
||||
views.html.clas.invite.show(clas, invite)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,8 +127,8 @@ final class Coach(env: Env) extends LilaController(env) {
|
|||
OptionFuResult(api findOrInit me) { c =>
|
||||
ctx.body.body.file("picture") match {
|
||||
case Some(pic) =>
|
||||
api.uploadPicture(c, pic) recover {
|
||||
case e: lila.base.LilaException => BadRequest(html.coach.picture(c, e.message.some))
|
||||
api.uploadPicture(c, pic) recover { case e: lila.base.LilaException =>
|
||||
BadRequest(html.coach.picture(c, e.message.some))
|
||||
} inject Redirect(routes.Coach.edit())
|
||||
case None => fuccess(Redirect(routes.Coach.edit()))
|
||||
}
|
||||
|
|
|
@ -28,12 +28,11 @@ final class Export(env: Env) extends LilaController(env) {
|
|||
Open { implicit ctx =>
|
||||
OnlyHumansAndFacebookOrTwitter {
|
||||
ExportGifRateLimitGlobal("-", msg = HTTPRequest.lastRemoteAddress(ctx.req).value) {
|
||||
OptionFuResult(env.game.gameRepo gameWithInitialFen id) {
|
||||
case (game, initialFen) =>
|
||||
val pov = Pov(game, Color(color) | Color.white)
|
||||
env.game.gifExport.fromPov(pov, initialFen) map
|
||||
stream("image/gif") map
|
||||
gameImageCacheSeconds(game)
|
||||
OptionFuResult(env.game.gameRepo gameWithInitialFen id) { case (game, initialFen) =>
|
||||
val pov = Pov(game, Color(color) | Color.white)
|
||||
env.game.gifExport.fromPov(pov, initialFen) map
|
||||
stream("image/gif") map
|
||||
gameImageCacheSeconds(game)
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
}
|
||||
|
|
|
@ -21,13 +21,12 @@ final class ForumCateg(env: Env) extends LilaController(env) with ForumControlle
|
|||
Open { implicit ctx =>
|
||||
NotForKids {
|
||||
Reasonable(page, 50, errorPage = notFound) {
|
||||
OptionFuOk(categApi.show(slug, page, ctx.me)) {
|
||||
case (categ, topics) =>
|
||||
for {
|
||||
canWrite <- isGrantedWrite(categ.slug)
|
||||
stickyPosts <- (page == 1) ?? env.forum.topicApi.getSticky(categ, ctx.me)
|
||||
_ <- env.user.lightUserApi preloadMany topics.currentPageResults.flatMap(_.lastPostUserId)
|
||||
} yield html.forum.categ.show(categ, topics, canWrite, stickyPosts)
|
||||
OptionFuOk(categApi.show(slug, page, ctx.me)) { case (categ, topics) =>
|
||||
for {
|
||||
canWrite <- isGrantedWrite(categ.slug)
|
||||
stickyPosts <- (page == 1) ?? env.forum.topicApi.getSticky(categ, ctx.me)
|
||||
_ <- env.user.lightUserApi preloadMany topics.currentPageResults.flatMap(_.lastPostUserId)
|
||||
} yield html.forum.categ.show(categ, topics, canWrite, stickyPosts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,31 +23,30 @@ final class ForumPost(env: Env) extends LilaController(env) with ForumController
|
|||
NoBot {
|
||||
CategGrantWrite(categSlug) {
|
||||
implicit val req = ctx.body
|
||||
OptionFuResult(topicApi.show(categSlug, slug, page, ctx.me)) {
|
||||
case (categ, topic, posts) =>
|
||||
if (topic.closed) fuccess(BadRequest("This topic is closed"))
|
||||
else if (topic.isOld) fuccess(BadRequest("This topic is archived"))
|
||||
else
|
||||
forms
|
||||
.post(me)
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
err =>
|
||||
for {
|
||||
captcha <- forms.anyCaptcha
|
||||
unsub <- env.timeline.status(s"forum:${topic.id}")(me.id)
|
||||
canModCateg <- isGrantedMod(categ.slug)
|
||||
} yield BadRequest(
|
||||
html.forum.topic
|
||||
.show(categ, topic, posts, Some(err -> captcha), unsub, canModCateg = canModCateg)
|
||||
),
|
||||
data =>
|
||||
CreateRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
|
||||
postApi.makePost(categ, topic, data, me) map { post =>
|
||||
Redirect(routes.ForumPost.redirect(post.id))
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
)
|
||||
OptionFuResult(topicApi.show(categSlug, slug, page, ctx.me)) { case (categ, topic, posts) =>
|
||||
if (topic.closed) fuccess(BadRequest("This topic is closed"))
|
||||
else if (topic.isOld) fuccess(BadRequest("This topic is archived"))
|
||||
else
|
||||
forms
|
||||
.post(me)
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
err =>
|
||||
for {
|
||||
captcha <- forms.anyCaptcha
|
||||
unsub <- env.timeline.status(s"forum:${topic.id}")(me.id)
|
||||
canModCateg <- isGrantedMod(categ.slug)
|
||||
} yield BadRequest(
|
||||
html.forum.topic
|
||||
.show(categ, topic, posts, Some(err -> captcha), unsub, canModCateg = canModCateg)
|
||||
),
|
||||
data =>
|
||||
CreateRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
|
||||
postApi.makePost(categ, topic, data, me) map { post =>
|
||||
Redirect(routes.ForumPost.redirect(post.id))
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,9 +87,8 @@ final class ForumPost(env: Env) extends LilaController(env) with ForumController
|
|||
|
||||
def redirect(id: String) =
|
||||
Open { implicit ctx =>
|
||||
OptionResult(postApi.urlData(id, ctx.me)) {
|
||||
case lila.forum.PostUrlData(categ, topic, page, number) =>
|
||||
Redirect(routes.ForumTopic.show(categ, topic, page).url + "#" + number)
|
||||
OptionResult(postApi.urlData(id, ctx.me)) { case lila.forum.PostUrlData(categ, topic, page, number) =>
|
||||
Redirect(routes.ForumTopic.show(categ, topic, page).url + "#" + number)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,17 +52,16 @@ final class ForumTopic(env: Env) extends LilaController(env) with ForumControlle
|
|||
def show(categSlug: String, slug: String, page: Int) =
|
||||
Open { implicit ctx =>
|
||||
NotForKids {
|
||||
OptionFuOk(topicApi.show(categSlug, slug, page, ctx.me)) {
|
||||
case (categ, topic, posts) =>
|
||||
for {
|
||||
unsub <- ctx.userId ?? env.timeline.status(s"forum:${topic.id}")
|
||||
canWrite <- isGrantedWrite(categSlug)
|
||||
form <- ctx.me.ifTrue(
|
||||
!posts.hasNextPage && canWrite && topic.open && !topic.isOld
|
||||
) ?? { me => forms.postWithCaptcha(me) map some }
|
||||
canModCateg <- isGrantedMod(categ.slug)
|
||||
_ <- env.user.lightUserApi preloadMany posts.currentPageResults.flatMap(_.userId)
|
||||
} yield html.forum.topic.show(categ, topic, posts, form, unsub, canModCateg = canModCateg)
|
||||
OptionFuOk(topicApi.show(categSlug, slug, page, ctx.me)) { case (categ, topic, posts) =>
|
||||
for {
|
||||
unsub <- ctx.userId ?? env.timeline.status(s"forum:${topic.id}")
|
||||
canWrite <- isGrantedWrite(categSlug)
|
||||
form <- ctx.me.ifTrue(
|
||||
!posts.hasNextPage && canWrite && topic.open && !topic.isOld
|
||||
) ?? { me => forms.postWithCaptcha(me) map some }
|
||||
canModCateg <- isGrantedMod(categ.slug)
|
||||
_ <- env.user.lightUserApi preloadMany posts.currentPageResults.flatMap(_.userId)
|
||||
} yield html.forum.topic.show(categ, topic, posts, form, unsub, canModCateg = canModCateg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,30 +69,27 @@ final class ForumTopic(env: Env) extends LilaController(env) with ForumControlle
|
|||
def close(categSlug: String, slug: String) =
|
||||
Auth { implicit ctx => me =>
|
||||
CategGrantMod(categSlug) {
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1, ctx.me)) {
|
||||
case (categ, topic, pag) =>
|
||||
topicApi.toggleClose(categ, topic, me) inject
|
||||
routes.ForumTopic.show(categSlug, slug, pag.nbPages)
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1, ctx.me)) { case (categ, topic, pag) =>
|
||||
topicApi.toggleClose(categ, topic, me) inject
|
||||
routes.ForumTopic.show(categSlug, slug, pag.nbPages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def hide(categSlug: String, slug: String) =
|
||||
Secure(_.ModerateForum) { implicit ctx => me =>
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1, ctx.me)) {
|
||||
case (categ, topic, pag) =>
|
||||
topicApi.toggleHide(categ, topic, me) inject
|
||||
routes.ForumTopic.show(categSlug, slug, pag.nbPages)
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1, ctx.me)) { case (categ, topic, pag) =>
|
||||
topicApi.toggleHide(categ, topic, me) inject
|
||||
routes.ForumTopic.show(categSlug, slug, pag.nbPages)
|
||||
}
|
||||
}
|
||||
|
||||
def sticky(categSlug: String, slug: String) =
|
||||
Auth { implicit ctx => me =>
|
||||
CategGrantMod(categSlug) {
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1, ctx.me)) {
|
||||
case (categ, topic, pag) =>
|
||||
topicApi.toggleSticky(categ, topic, me) inject
|
||||
routes.ForumTopic.show(categSlug, slug, pag.nbPages)
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1, ctx.me)) { case (categ, topic, pag) =>
|
||||
topicApi.toggleSticky(categ, topic, me) inject
|
||||
routes.ForumTopic.show(categSlug, slug, pag.nbPages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,12 +90,11 @@ final class Importer(env: Env) extends LilaController(env) {
|
|||
): Fu[Option[lila.game.Game]] =
|
||||
env.importer.importer(data, me.map(_.id)) flatMap { game =>
|
||||
me.map(_.id).??(env.game.cached.clearNbImportedByCache) inject game.some
|
||||
} recover {
|
||||
case e: Exception =>
|
||||
lila
|
||||
.log("importer")
|
||||
.warn(s"Imported game validates but can't be replayed:\n${data.pgn}", e)
|
||||
none
|
||||
} recover { case e: Exception =>
|
||||
lila
|
||||
.log("importer")
|
||||
.warn(s"Imported game validates but can't be replayed:\n${data.pgn}", e)
|
||||
none
|
||||
}
|
||||
|
||||
def masterGame(id: String, orientation: String) =
|
||||
|
|
|
@ -38,11 +38,10 @@ final class Learn(env: Env) extends LilaController(env) {
|
|||
.bindFromRequest()
|
||||
.fold(
|
||||
_ => BadRequest.fuccess,
|
||||
{
|
||||
case (stage, level, s) =>
|
||||
val score = lila.learn.StageProgress.Score(s)
|
||||
env.learn.api.setScore(me, stage, level, score) >>
|
||||
env.activity.write.learn(me.id, stage) inject Ok(Json.obj("ok" -> true))
|
||||
{ case (stage, level, s) =>
|
||||
val score = lila.learn.StageProgress.Score(s)
|
||||
env.learn.api.setScore(me, stage, level, score) >>
|
||||
env.activity.write.learn(me.id, stage) inject Ok(Json.obj("ok" -> true))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -483,19 +483,17 @@ abstract private[controllers] class LilaController(val env: Env)
|
|||
.dmap(_.withHeaders("Vary" -> "Accept"))
|
||||
|
||||
protected def reqToCtx(req: RequestHeader): Fu[HeaderContext] =
|
||||
restoreUser(req) flatMap {
|
||||
case (d, impersonatedBy) =>
|
||||
val lang = getAndSaveLang(req, d.map(_.user))
|
||||
val ctx = UserContext(req, d.map(_.user), impersonatedBy, lang)
|
||||
pageDataBuilder(ctx, d.exists(_.hasFingerPrint)) dmap { Context(ctx, _) }
|
||||
restoreUser(req) flatMap { case (d, impersonatedBy) =>
|
||||
val lang = getAndSaveLang(req, d.map(_.user))
|
||||
val ctx = UserContext(req, d.map(_.user), impersonatedBy, lang)
|
||||
pageDataBuilder(ctx, d.exists(_.hasFingerPrint)) dmap { Context(ctx, _) }
|
||||
}
|
||||
|
||||
protected def reqToCtx[A](req: Request[A]): Fu[BodyContext[A]] =
|
||||
restoreUser(req) flatMap {
|
||||
case (d, impersonatedBy) =>
|
||||
val lang = getAndSaveLang(req, d.map(_.user))
|
||||
val ctx = UserContext(req, d.map(_.user), impersonatedBy, lang)
|
||||
pageDataBuilder(ctx, d.exists(_.hasFingerPrint)) dmap { Context(ctx, _) }
|
||||
restoreUser(req) flatMap { case (d, impersonatedBy) =>
|
||||
val lang = getAndSaveLang(req, d.map(_.user))
|
||||
val ctx = UserContext(req, d.map(_.user), impersonatedBy, lang)
|
||||
pageDataBuilder(ctx, d.exists(_.hasFingerPrint)) dmap { Context(ctx, _) }
|
||||
}
|
||||
|
||||
private def getAndSaveLang(req: RequestHeader, user: Option[UserModel]): Lang = {
|
||||
|
@ -510,33 +508,33 @@ abstract private[controllers] class LilaController(val env: Env)
|
|||
ctx.me.fold(fuccess(PageData.anon(ctx.req, nonce, blindMode(ctx)))) { me =>
|
||||
env.pref.api.getPref(me, ctx.req) zip
|
||||
(if (isGranted(_.Teacher, me)) fuccess(true) else env.clas.api.student.isStudent(me.id)) zip {
|
||||
if (isPage) {
|
||||
env.user.lightUserApi preloadUser me
|
||||
env.team.api.nbRequests(me.id) zip
|
||||
env.challenge.api.countInFor.get(me.id) zip
|
||||
env.notifyM.api.unreadCount(Notifies(me.id)).dmap(_.value) zip
|
||||
env.mod.inquiryApi.forMod(me)
|
||||
} else
|
||||
fuccess {
|
||||
(((0, 0), 0), none)
|
||||
}
|
||||
} map {
|
||||
case (
|
||||
(pref, hasClas),
|
||||
teamNbRequests ~ nbChallenges ~ nbNotifications ~ inquiry
|
||||
) =>
|
||||
PageData(
|
||||
teamNbRequests,
|
||||
nbChallenges,
|
||||
nbNotifications,
|
||||
pref,
|
||||
blindMode = blindMode(ctx),
|
||||
hasFingerprint = hasFingerPrint,
|
||||
hasClas = hasClas,
|
||||
inquiry = inquiry,
|
||||
nonce = nonce
|
||||
)
|
||||
}
|
||||
if (isPage) {
|
||||
env.user.lightUserApi preloadUser me
|
||||
env.team.api.nbRequests(me.id) zip
|
||||
env.challenge.api.countInFor.get(me.id) zip
|
||||
env.notifyM.api.unreadCount(Notifies(me.id)).dmap(_.value) zip
|
||||
env.mod.inquiryApi.forMod(me)
|
||||
} else
|
||||
fuccess {
|
||||
(((0, 0), 0), none)
|
||||
}
|
||||
} map {
|
||||
case (
|
||||
(pref, hasClas),
|
||||
teamNbRequests ~ nbChallenges ~ nbNotifications ~ inquiry
|
||||
) =>
|
||||
PageData(
|
||||
teamNbRequests,
|
||||
nbChallenges,
|
||||
nbNotifications,
|
||||
pref,
|
||||
blindMode = blindMode(ctx),
|
||||
hasFingerprint = hasFingerPrint,
|
||||
hasClas = hasClas,
|
||||
inquiry = inquiry,
|
||||
nonce = nonce
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,13 @@ final class Main(
|
|||
.bindFromRequest()
|
||||
.fold(
|
||||
_ => BadRequest,
|
||||
{
|
||||
case (enable, redirect) =>
|
||||
Redirect(redirect) withCookies env.lilaCookie.cookie(
|
||||
env.api.config.accessibility.blindCookieName,
|
||||
if (enable == "0") "" else env.api.config.accessibility.hash,
|
||||
maxAge = env.api.config.accessibility.blindCookieMaxAge.toSeconds.toInt.some,
|
||||
httpOnly = true.some
|
||||
)
|
||||
{ case (enable, redirect) =>
|
||||
Redirect(redirect) withCookies env.lilaCookie.cookie(
|
||||
env.api.config.accessibility.blindCookieName,
|
||||
if (enable == "0") "" else env.api.config.accessibility.hash,
|
||||
maxAge = env.api.config.accessibility.blindCookieMaxAge.toSeconds.toInt.some,
|
||||
httpOnly = true.some
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -50,8 +49,8 @@ final class Main(
|
|||
|
||||
def captchaCheck(id: String) =
|
||||
Open { implicit ctx =>
|
||||
env.hub.captcher.actor ? ValidCaptcha(id, ~get("solution")) map {
|
||||
case valid: Boolean => Ok(if (valid) 1 else 0)
|
||||
env.hub.captcher.actor ? ValidCaptcha(id, ~get("solution")) map { case valid: Boolean =>
|
||||
Ok(if (valid) 1 else 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,8 +73,8 @@ final class Main(
|
|||
def mobile =
|
||||
Open { implicit ctx =>
|
||||
pageHit
|
||||
OptionOk(prismicC getBookmark "mobile-apk") {
|
||||
case (doc, resolver) => html.mobile(doc, resolver)
|
||||
OptionOk(prismicC getBookmark "mobile-apk") { case (doc, resolver) =>
|
||||
html.mobile(doc, resolver)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ final class Mod(
|
|||
} yield (inquiry, sus).some
|
||||
}
|
||||
}(ctx =>
|
||||
me => {
|
||||
case (inquiry, suspect) => reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
me => { case (inquiry, suspect) =>
|
||||
reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -50,16 +50,15 @@ final class Mod(
|
|||
} yield (inquiry, sus).some
|
||||
}
|
||||
}(ctx =>
|
||||
me => {
|
||||
case (inquiry, suspect) => reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
me => { case (inquiry, suspect) =>
|
||||
reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
}
|
||||
)
|
||||
|
||||
def publicChat =
|
||||
Secure(_.ChatTimeout) { implicit ctx => _ =>
|
||||
env.mod.publicChat.all map {
|
||||
case (tournamentsAndChats, simulsAndChats) =>
|
||||
Ok(html.mod.publicChat(tournamentsAndChats, simulsAndChats))
|
||||
env.mod.publicChat.all map { case (tournamentsAndChats, simulsAndChats) =>
|
||||
Ok(html.mod.publicChat(tournamentsAndChats, simulsAndChats))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,8 +71,8 @@ final class Mod(
|
|||
} yield (inquiry, suspect).some
|
||||
}
|
||||
}(ctx =>
|
||||
me => {
|
||||
case (inquiry, suspect) => reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
me => { case (inquiry, suspect) =>
|
||||
reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -86,8 +85,8 @@ final class Mod(
|
|||
} yield (inquiry, suspect).some
|
||||
}
|
||||
}(ctx =>
|
||||
me => {
|
||||
case (inquiry, suspect) => reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
me => { case (inquiry, suspect) =>
|
||||
reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -104,8 +103,8 @@ final class Mod(
|
|||
}
|
||||
}
|
||||
}(ctx =>
|
||||
me => {
|
||||
case (inquiry, suspect) => reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
me => { case (inquiry, suspect) =>
|
||||
reportC.onInquiryClose(inquiry, me, suspect.some)(ctx)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -245,25 +244,29 @@ final class Mod(
|
|||
env.report.api.inquiries
|
||||
.ofModId(me.id)
|
||||
.mon(_.mod.comm.segment("inquiries")) map {
|
||||
case chats ~ convos ~ publicLines ~ notes ~ history ~ inquiry =>
|
||||
if (priv) {
|
||||
if (!inquiry.??(_.isRecentCommOf(Suspect(user))))
|
||||
env.slack.api.commlog(mod = me, user = user, inquiry.map(_.oldestAtom.by.value))
|
||||
if (isGranted(_.MonitoredMod))
|
||||
env.slack.api.monitorMod(me.id, "eyes", s"checked out @${user.username}'s private comms")
|
||||
}
|
||||
html.mod.communication(
|
||||
user,
|
||||
(povs zip chats) collect {
|
||||
case (p, Some(c)) if c.nonEmpty => p -> c
|
||||
} take 15,
|
||||
convos,
|
||||
publicLines,
|
||||
notes.filter(_.from != "irwin"),
|
||||
history,
|
||||
priv
|
||||
)
|
||||
}
|
||||
case chats ~ convos ~ publicLines ~ notes ~ history ~ inquiry =>
|
||||
if (priv) {
|
||||
if (!inquiry.??(_.isRecentCommOf(Suspect(user))))
|
||||
env.slack.api.commlog(mod = me, user = user, inquiry.map(_.oldestAtom.by.value))
|
||||
if (isGranted(_.MonitoredMod))
|
||||
env.slack.api.monitorMod(
|
||||
me.id,
|
||||
"eyes",
|
||||
s"checked out @${user.username}'s private comms"
|
||||
)
|
||||
}
|
||||
html.mod.communication(
|
||||
user,
|
||||
(povs zip chats) collect {
|
||||
case (p, Some(c)) if c.nonEmpty => p -> c
|
||||
} take 15,
|
||||
convos,
|
||||
publicLines,
|
||||
notes.filter(_.from != "irwin"),
|
||||
history,
|
||||
priv
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -305,9 +308,9 @@ final class Mod(
|
|||
def gamify =
|
||||
Secure(_.SeeReport) { implicit ctx => _ =>
|
||||
env.mod.gamify.leaderboards zip
|
||||
env.mod.gamify.history(orCompute = true) map {
|
||||
case (leaderboards, history) => Ok(html.mod.gamify.index(leaderboards, history))
|
||||
}
|
||||
env.mod.gamify.history(orCompute = true) map { case (leaderboards, history) =>
|
||||
Ok(html.mod.gamify.index(leaderboards, history))
|
||||
}
|
||||
}
|
||||
def gamifyPeriod(periodStr: String) =
|
||||
Secure(_.SeeReport) { implicit ctx => _ =>
|
||||
|
@ -430,8 +433,8 @@ final class Mod(
|
|||
modApi.setEmail(me.id, user.id, setEmail)
|
||||
} >>
|
||||
env.user.repo.email(user.id) map { email =>
|
||||
Ok(html.mod.emailConfirm("", user.some, email)).some
|
||||
}
|
||||
Ok(html.mod.emailConfirm("", user.some, email)).some
|
||||
}
|
||||
case _ => fuccess(none)
|
||||
}
|
||||
email.?? { em =>
|
||||
|
|
|
@ -13,10 +13,9 @@ final class Msg(
|
|||
def home =
|
||||
Auth { implicit ctx => me =>
|
||||
negotiate(
|
||||
html =
|
||||
inboxJson(me) map { json =>
|
||||
Ok(views.html.msg.home(json))
|
||||
},
|
||||
html = inboxJson(me) map { json =>
|
||||
Ok(views.html.msg.home(json))
|
||||
},
|
||||
api = v =>
|
||||
{
|
||||
if (v >= 5) inboxJson(me)
|
||||
|
@ -38,10 +37,9 @@ final class Msg(
|
|||
case Some(c) =>
|
||||
def newJson = inboxJson(me).map { _ + ("convo" -> env.msg.json.convo(c)) }
|
||||
negotiate(
|
||||
html =
|
||||
newJson map { json =>
|
||||
Ok(views.html.msg.home(json))
|
||||
},
|
||||
html = newJson map { json =>
|
||||
Ok(views.html.msg.home(json))
|
||||
},
|
||||
api = v =>
|
||||
{
|
||||
if (v >= 5) newJson
|
||||
|
|
|
@ -18,8 +18,8 @@ final class Page(
|
|||
private def helpBookmark(name: String) =
|
||||
Open { implicit ctx =>
|
||||
pageHit
|
||||
OptionOk(prismicC getBookmark name) {
|
||||
case (doc, resolver) => views.html.site.help.page(name, doc, resolver)
|
||||
OptionOk(prismicC getBookmark name) { case (doc, resolver) =>
|
||||
views.html.site.help.page(name, doc, resolver)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,16 +28,16 @@ final class Page(
|
|||
private def bookmark(name: String) =
|
||||
Open { implicit ctx =>
|
||||
pageHit
|
||||
OptionOk(prismicC getBookmark name) {
|
||||
case (doc, resolver) => views.html.site.page(doc, resolver)
|
||||
OptionOk(prismicC getBookmark name) { case (doc, resolver) =>
|
||||
views.html.site.page(doc, resolver)
|
||||
}
|
||||
}
|
||||
|
||||
def source =
|
||||
Open { implicit ctx =>
|
||||
pageHit
|
||||
OptionOk(prismicC getBookmark "source") {
|
||||
case (doc, resolver) => views.html.site.help.source(doc, resolver)
|
||||
OptionOk(prismicC getBookmark "source") { case (doc, resolver) =>
|
||||
views.html.site.help.source(doc, resolver)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,8 +45,8 @@ final class Page(
|
|||
Open { implicit ctx =>
|
||||
import play.api.libs.json._
|
||||
negotiate(
|
||||
html = OptionOk(prismicC getBookmark "variant") {
|
||||
case (doc, resolver) => views.html.site.variant.home(doc, resolver)
|
||||
html = OptionOk(prismicC getBookmark "variant") { case (doc, resolver) =>
|
||||
views.html.site.variant.home(doc, resolver)
|
||||
},
|
||||
api = _ =>
|
||||
Ok(JsArray(chess.variant.Variant.all.map { v =>
|
||||
|
@ -64,8 +64,8 @@ final class Page(
|
|||
(for {
|
||||
variant <- chess.variant.Variant.byKey get key
|
||||
perfType <- lila.rating.PerfType byVariant variant
|
||||
} yield OptionOk(prismicC getVariant variant) {
|
||||
case (doc, resolver) => views.html.site.variant.show(doc, resolver, variant, perfType)
|
||||
} yield OptionOk(prismicC getVariant variant) { case (doc, resolver) =>
|
||||
views.html.site.variant.show(doc, resolver, variant, perfType)
|
||||
}) | notFound
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,10 +135,9 @@ final class Plan(env: Env)(implicit system: akka.actor.ActorSystem) extends Lila
|
|||
}
|
||||
|
||||
def badStripeSession[A: Writes](err: A) = BadRequest(jsonError(err))
|
||||
def badStripeApiCall: PartialFunction[Throwable, Result] = {
|
||||
case e: StripeException =>
|
||||
logger.error("Plan.stripeCheckout", e)
|
||||
badStripeSession("Stripe API call failed")
|
||||
def badStripeApiCall: PartialFunction[Throwable, Result] = { case e: StripeException =>
|
||||
logger.error("Plan.stripeCheckout", e)
|
||||
badStripeSession("Stripe API call failed")
|
||||
}
|
||||
|
||||
private def createStripeSession(checkout: Checkout, customerId: CustomerId) =
|
||||
|
|
|
@ -40,9 +40,9 @@ final class PlayApi(
|
|||
env.user.repo.setBot(me) >>
|
||||
env.pref.api.setBot(me) >>-
|
||||
env.user.lightUserApi.invalidate(me.id) pipe
|
||||
toResult recover {
|
||||
case lila.base.LilaInvalid(msg) => BadRequest(jsonError(msg))
|
||||
}
|
||||
toResult recover { case lila.base.LilaInvalid(msg) =>
|
||||
BadRequest(jsonError(msg))
|
||||
}
|
||||
}
|
||||
case _ => impl.command(me, cmd)(WithPovAsBot)
|
||||
}
|
||||
|
@ -111,8 +111,8 @@ final class PlayApi(
|
|||
|
||||
private def toResult(f: Funit): Fu[Result] = catchClientError(f inject jsonOkResult)
|
||||
private def catchClientError(f: Fu[Result]): Fu[Result] =
|
||||
f recover {
|
||||
case e: lila.round.BenignError => BadRequest(jsonError(e.getMessage))
|
||||
f recover { case e: lila.round.BenignError =>
|
||||
BadRequest(jsonError(e.getMessage))
|
||||
}
|
||||
|
||||
private def WithPovAsBot(anyId: String, me: lila.user.User)(f: Pov => Fu[Result]) =
|
||||
|
|
|
@ -64,35 +64,33 @@ final class Practice(
|
|||
}
|
||||
|
||||
private def showUserPractice(us: lila.practice.UserStudy)(implicit ctx: Context) =
|
||||
analysisJson(us) map {
|
||||
case (analysisJson, studyJson) =>
|
||||
NoCache(
|
||||
EnableSharedArrayBuffer(
|
||||
Ok(
|
||||
html.practice.show(
|
||||
us,
|
||||
lila.practice.JsonView.JsData(
|
||||
study = studyJson,
|
||||
analysis = analysisJson,
|
||||
practice = lila.practice.JsonView(us)
|
||||
)
|
||||
analysisJson(us) map { case (analysisJson, studyJson) =>
|
||||
NoCache(
|
||||
EnableSharedArrayBuffer(
|
||||
Ok(
|
||||
html.practice.show(
|
||||
us,
|
||||
lila.practice.JsonView.JsData(
|
||||
study = studyJson,
|
||||
analysis = analysisJson,
|
||||
practice = lila.practice.JsonView(us)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def chapter(studyId: String, chapterId: String) =
|
||||
Open { implicit ctx =>
|
||||
OptionFuResult(api.getStudyWithChapter(ctx.me, studyId, chapterId)) { us =>
|
||||
analysisJson(us) map {
|
||||
case (analysisJson, studyJson) =>
|
||||
Ok(
|
||||
Json.obj(
|
||||
"study" -> studyJson,
|
||||
"analysis" -> analysisJson
|
||||
)
|
||||
) as JSON
|
||||
analysisJson(us) map { case (analysisJson, studyJson) =>
|
||||
Ok(
|
||||
Json.obj(
|
||||
"study" -> studyJson,
|
||||
"analysis" -> analysisJson
|
||||
)
|
||||
) as JSON
|
||||
}
|
||||
} map NoCache
|
||||
}
|
||||
|
|
|
@ -55,13 +55,12 @@ final class Pref(env: Env) extends LilaController(env) {
|
|||
Ok.withCookies(env.lilaCookie.session("zoom2", (getInt("v") | 185).toString)).fuccess
|
||||
} else {
|
||||
implicit val req = ctx.body
|
||||
(setters get name) ?? {
|
||||
case (form, fn) =>
|
||||
FormResult(form) { v =>
|
||||
fn(v, ctx) map { cookie =>
|
||||
Ok(()).withCookies(cookie)
|
||||
}
|
||||
(setters get name) ?? { case (form, fn) =>
|
||||
FormResult(form) { v =>
|
||||
fn(v, ctx) map { cookie =>
|
||||
Ok(()).withCookies(cookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,10 +33,9 @@ final class Prismic(
|
|||
api.bookmarks.get(name) ?? getDocument map2 { (doc: io.prismic.Document) =>
|
||||
doc -> makeLinkResolver(api)
|
||||
}
|
||||
} recover {
|
||||
case e: Exception =>
|
||||
logger.error(s"bookmark:$name", e)
|
||||
none
|
||||
} recover { case e: Exception =>
|
||||
logger.error(s"bookmark:$name", e)
|
||||
none
|
||||
}
|
||||
|
||||
def getVariant(variant: chess.variant.Variant) =
|
||||
|
|
|
@ -188,11 +188,10 @@ final class Puzzle(
|
|||
vote =>
|
||||
env.puzzle.api.vote.find(id, me) flatMap { v =>
|
||||
env.puzzle.api.vote.update(id, me, v, vote == 1)
|
||||
} 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))
|
||||
} 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))
|
||||
}
|
||||
) map (_ as JSON)
|
||||
}
|
||||
|
|
|
@ -21,8 +21,7 @@ final class Relation(
|
|||
private def renderActions(userId: String, mini: Boolean)(implicit ctx: Context) =
|
||||
(ctx.userId ?? { api.fetchRelation(_, userId) }) zip
|
||||
(ctx.isAuth ?? { env.pref.api followable userId }) zip
|
||||
(ctx.userId ?? { api.fetchBlocks(userId, _) }) flatMap {
|
||||
case relation ~ followable ~ blocked =>
|
||||
(ctx.userId ?? { api.fetchBlocks(userId, _) }) flatMap { case relation ~ followable ~ blocked =>
|
||||
negotiate(
|
||||
html = fuccess(Ok {
|
||||
if (mini)
|
||||
|
@ -41,7 +40,7 @@ final class Relation(
|
|||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def follow(userId: String) =
|
||||
Auth { implicit ctx => me =>
|
||||
|
|
|
@ -119,18 +119,17 @@ final class Relay(
|
|||
OpenOrScoped(_.Study.Read)(
|
||||
open = implicit ctx => {
|
||||
pageHit
|
||||
WithRelay(slug, id) {
|
||||
relay =>
|
||||
val sc =
|
||||
if (relay.sync.ongoing) env.study.chapterRepo relaysAndTagsByStudyId relay.studyId flatMap {
|
||||
chapters =>
|
||||
chapters.find(_.looksAlive) orElse chapters.headOption match {
|
||||
case Some(chapter) => env.study.api.byIdWithChapter(relay.studyId, chapter.id)
|
||||
case None => env.study.api byIdWithChapter relay.studyId
|
||||
}
|
||||
WithRelay(slug, id) { relay =>
|
||||
val sc =
|
||||
if (relay.sync.ongoing)
|
||||
env.study.chapterRepo relaysAndTagsByStudyId relay.studyId flatMap { chapters =>
|
||||
chapters.find(_.looksAlive) orElse chapters.headOption match {
|
||||
case Some(chapter) => env.study.api.byIdWithChapter(relay.studyId, chapter.id)
|
||||
case None => env.study.api byIdWithChapter relay.studyId
|
||||
}
|
||||
}
|
||||
else env.study.api byIdWithChapter relay.studyId
|
||||
sc flatMap { _ ?? { doShow(relay, _) } }
|
||||
else env.study.api byIdWithChapter relay.studyId
|
||||
sc flatMap { _ ?? { doShow(relay, _) } }
|
||||
}
|
||||
},
|
||||
scoped = _ =>
|
||||
|
|
|
@ -35,22 +35,20 @@ final class Report(
|
|||
|
||||
private def renderList(room: String)(implicit ctx: Context) =
|
||||
api.openAndRecentWithFilter(12, Room(room)) zip
|
||||
getCounts flatMap {
|
||||
case (reports, counts ~ streamers ~ appeals) =>
|
||||
getCounts flatMap { case (reports, counts ~ streamers ~ appeals) =>
|
||||
(env.user.lightUserApi preloadMany reports.flatMap(_.report.userIds)) inject
|
||||
Ok(html.report.list(reports, room, counts, streamers, appeals))
|
||||
}
|
||||
}
|
||||
|
||||
def inquiry(id: String) =
|
||||
Secure(_.SeeReport) { _ => me =>
|
||||
api.inquiries.toggle(AsMod(me), id) map {
|
||||
case (prev, next) =>
|
||||
next.fold(
|
||||
Redirect {
|
||||
if (prev.exists(_.isAppeal)) routes.Appeal.queue()
|
||||
else routes.Report.list()
|
||||
}
|
||||
)(onInquiryStart)
|
||||
api.inquiries.toggle(AsMod(me), id) map { case (prev, next) =>
|
||||
next.fold(
|
||||
Redirect {
|
||||
if (prev.exists(_.isAppeal)) routes.Appeal.queue()
|
||||
else routes.Report.list()
|
||||
}
|
||||
)(onInquiryStart)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,12 +93,11 @@ final class Report(
|
|||
}
|
||||
else if (force) userC.modZoneOrRedirect(prev.user)
|
||||
else
|
||||
api.inquiries.toggle(AsMod(me), prev.id) map {
|
||||
case (prev, next) =>
|
||||
next.fold(
|
||||
if (prev.exists(_.isAppeal)) Redirect(routes.Appeal.queue())
|
||||
else redirectToList
|
||||
)(onInquiryStart)
|
||||
api.inquiries.toggle(AsMod(me), prev.id) map { case (prev, next) =>
|
||||
next.fold(
|
||||
if (prev.exists(_.isAppeal)) Redirect(routes.Appeal.queue())
|
||||
else redirectToList
|
||||
)(onInquiryStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,8 +130,8 @@ final class Report(
|
|||
def form =
|
||||
Auth { implicit ctx => _ =>
|
||||
get("username") ?? env.user.repo.named flatMap { user =>
|
||||
env.report.forms.createWithCaptcha map {
|
||||
case (form, captcha) => Ok(html.report.form(form, user, captcha))
|
||||
env.report.forms.createWithCaptcha map { case (form, captcha) =>
|
||||
Ok(html.report.form(form, user, captcha))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,16 +33,15 @@ final class Round(
|
|||
else
|
||||
PreventTheft(pov) {
|
||||
pov.game.playableByAi ?? env.fishnet.player(pov.game)
|
||||
env.tournament.api.gameView.player(pov) flatMap {
|
||||
tour =>
|
||||
gameC.preloadUsers(pov.game) zip
|
||||
(pov.game.simulId ?? env.simul.repo.find) zip
|
||||
getPlayerChat(pov.game, tour.map(_.tour)) zip
|
||||
(ctx.noBlind ?? env.game.crosstableApi
|
||||
.withMatchup(pov.game)) zip
|
||||
(pov.game.isSwitchable ?? otherPovs(pov.game)) zip
|
||||
env.bookmark.api.exists(pov.game, ctx.me) zip
|
||||
env.api.roundApi.player(pov, tour, lila.api.Mobile.Api.currentVersion) map {
|
||||
env.tournament.api.gameView.player(pov) flatMap { tour =>
|
||||
gameC.preloadUsers(pov.game) zip
|
||||
(pov.game.simulId ?? env.simul.repo.find) zip
|
||||
getPlayerChat(pov.game, tour.map(_.tour)) zip
|
||||
(ctx.noBlind ?? env.game.crosstableApi
|
||||
.withMatchup(pov.game)) zip
|
||||
(pov.game.isSwitchable ?? otherPovs(pov.game)) zip
|
||||
env.bookmark.api.exists(pov.game, ctx.me) zip
|
||||
env.api.roundApi.player(pov, tour, lila.api.Mobile.Api.currentVersion) map {
|
||||
case _ ~ simul ~ chatOption ~ crosstable ~ playing ~ bookmarked ~ data =>
|
||||
simul foreach env.simul.api.onPlayerConnection(pov.game, ctx.me)
|
||||
Ok(
|
||||
|
@ -67,12 +66,11 @@ final class Round(
|
|||
pov.game.playableByAi ?? env.fishnet.player(pov.game)
|
||||
gameC.preloadUsers(pov.game) zip
|
||||
env.api.roundApi.player(pov, tour, apiVersion) zip
|
||||
getPlayerChat(pov.game, none) map {
|
||||
case _ ~ data ~ chat =>
|
||||
getPlayerChat(pov.game, none) map { case _ ~ data ~ chat =>
|
||||
Ok {
|
||||
data.add("chat", chat.flatMap(_.game).map(c => lila.chat.JsonView(c.chat)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
) dmap NoCache
|
||||
|
@ -171,29 +169,29 @@ final class Round(
|
|||
getWatcherChat(pov.game) zip
|
||||
(ctx.noBlind ?? env.game.crosstableApi.withMatchup(pov.game)) zip
|
||||
env.bookmark.api.exists(pov.game, ctx.me) flatMap {
|
||||
case tour ~ simul ~ chat ~ crosstable ~ bookmarked =>
|
||||
env.api.roundApi.watcher(
|
||||
pov,
|
||||
tour,
|
||||
lila.api.Mobile.Api.currentVersion,
|
||||
tv = userTv.map { u =>
|
||||
lila.round.OnUserTv(u.id)
|
||||
}
|
||||
) map { data =>
|
||||
Ok(
|
||||
html.round.watcher(
|
||||
pov,
|
||||
data,
|
||||
tour.map(_.tourAndTeamVs),
|
||||
simul,
|
||||
crosstable,
|
||||
userTv = userTv,
|
||||
chatOption = chat,
|
||||
bookmarked = bookmarked
|
||||
case tour ~ simul ~ chat ~ crosstable ~ bookmarked =>
|
||||
env.api.roundApi.watcher(
|
||||
pov,
|
||||
tour,
|
||||
lila.api.Mobile.Api.currentVersion,
|
||||
tv = userTv.map { u =>
|
||||
lila.round.OnUserTv(u.id)
|
||||
}
|
||||
) map { data =>
|
||||
Ok(
|
||||
html.round.watcher(
|
||||
pov,
|
||||
data,
|
||||
tour.map(_.tourAndTeamVs),
|
||||
simul,
|
||||
crosstable,
|
||||
userTv = userTv,
|
||||
chatOption = chat,
|
||||
bookmarked = bookmarked
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
for { // web crawlers don't need the full thing
|
||||
initialFen <- env.game.gameRepo.initialFen(pov.gameId)
|
||||
|
@ -243,7 +241,8 @@ final class Round(
|
|||
)
|
||||
.some
|
||||
(game.tournamentId, game.simulId, game.swissId) match {
|
||||
case (Some(tid), _, _) => {
|
||||
case (Some(tid), _, _) =>
|
||||
{
|
||||
ctx.isAuth && tour.fold(true)(tournamentC.canHaveChat(_, none))
|
||||
} ?? env.chat.api.userChat.cached
|
||||
.findMine(Chat.Id(tid), ctx.me)
|
||||
|
@ -285,9 +284,9 @@ final class Round(
|
|||
env.game.gameRepo.initialFen(pov.game) zip
|
||||
env.game.crosstableApi.withMatchup(pov.game) zip
|
||||
env.bookmark.api.exists(pov.game, ctx.me) map {
|
||||
case tour ~ simul ~ initialFen ~ crosstable ~ bookmarked =>
|
||||
Ok(html.game.bits.sides(pov, initialFen, tour, crosstable, simul, bookmarked = bookmarked))
|
||||
}
|
||||
case tour ~ simul ~ initialFen ~ crosstable ~ bookmarked =>
|
||||
Ok(html.game.bits.sides(pov, initialFen, tour, crosstable, simul, bookmarked = bookmarked))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,25 +18,22 @@ final class Simul(env: Env) extends LilaController(env) {
|
|||
|
||||
val home = Open { implicit ctx =>
|
||||
pageHit
|
||||
fetchSimuls(ctx.me) flatMap {
|
||||
case pending ~ created ~ started ~ finished =>
|
||||
Ok(html.simul.home(pending, created, started, finished)).fuccess
|
||||
fetchSimuls(ctx.me) flatMap { case pending ~ created ~ started ~ finished =>
|
||||
Ok(html.simul.home(pending, created, started, finished)).fuccess
|
||||
}
|
||||
}
|
||||
|
||||
val apiList = Action.async {
|
||||
fetchSimuls(none) flatMap {
|
||||
case pending ~ created ~ started ~ finished =>
|
||||
env.simul.jsonView.apiAll(pending, created, started, finished) map { json =>
|
||||
Ok(json) as JSON
|
||||
}
|
||||
fetchSimuls(none) flatMap { case pending ~ created ~ started ~ finished =>
|
||||
env.simul.jsonView.apiAll(pending, created, started, finished) map { json =>
|
||||
Ok(json) as JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val homeReload = Open { implicit ctx =>
|
||||
fetchSimuls(ctx.me) map {
|
||||
case pending ~ created ~ started ~ finished =>
|
||||
Ok(html.simul.homeInner(pending, created, started, finished))
|
||||
fetchSimuls(ctx.me) map { case pending ~ created ~ started ~ finished =>
|
||||
Ok(html.simul.homeInner(pending, created, started, finished))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,10 +80,10 @@ final class Simul(env: Env) extends LilaController(env) {
|
|||
ctx.me.fold(true) { // anon can see public chats
|
||||
env.chat.panic.allowed
|
||||
} && simul.team.fold(true) { teamId =>
|
||||
ctx.userId exists {
|
||||
env.team.api.syncBelongsTo(teamId, _)
|
||||
ctx.userId exists {
|
||||
env.team.api.syncBelongsTo(teamId, _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def hostPing(simulId: String) =
|
||||
Open { implicit ctx =>
|
||||
|
|
|
@ -136,8 +136,8 @@ final class Streamer(
|
|||
AsStreamer { s =>
|
||||
ctx.body.body.file("picture") match {
|
||||
case Some(pic) =>
|
||||
api.uploadPicture(s.streamer, pic) recover {
|
||||
case e: lila.base.LilaException => BadRequest(html.streamer.picture(s, e.message.some))
|
||||
api.uploadPicture(s.streamer, pic) recover { case e: lila.base.LilaException =>
|
||||
BadRequest(html.streamer.picture(s, e.message.some))
|
||||
} inject Redirect(routes.Streamer.edit())
|
||||
case None => fuccess(Redirect(routes.Streamer.edit()))
|
||||
}
|
||||
|
|
|
@ -79,10 +79,9 @@ final class Study(
|
|||
Auth { implicit ctx => me =>
|
||||
env.study.pager.mine(me, Order(order), page) flatMap { pag =>
|
||||
negotiate(
|
||||
html =
|
||||
env.study.topicApi.userTopics(me.id) map { topics =>
|
||||
Ok(html.study.list.mine(pag, Order(order), me, topics))
|
||||
},
|
||||
html = env.study.topicApi.userTopics(me.id) map { topics =>
|
||||
Ok(html.study.list.mine(pag, Order(order), me, topics))
|
||||
},
|
||||
api = _ => apiStudies(pag)
|
||||
)
|
||||
}
|
||||
|
@ -112,10 +111,9 @@ final class Study(
|
|||
Auth { implicit ctx => me =>
|
||||
env.study.pager.mineMember(me, Order(order), page) flatMap { pag =>
|
||||
negotiate(
|
||||
html =
|
||||
env.study.topicApi.userTopics(me.id) map { topics =>
|
||||
Ok(html.study.list.mineMember(pag, Order(order), me, topics))
|
||||
},
|
||||
html = env.study.topicApi.userTopics(me.id) map { topics =>
|
||||
Ok(html.study.list.mineMember(pag, Order(order), me, topics))
|
||||
},
|
||||
api = _ => apiStudies(pag)
|
||||
)
|
||||
}
|
||||
|
@ -137,10 +135,9 @@ final class Study(
|
|||
case None => notFound
|
||||
case Some(topic) =>
|
||||
env.study.pager.byTopic(topic, ctx.me, Order(order), page) zip
|
||||
ctx.me.??(u => env.study.topicApi.userTopics(u.id) dmap some) map {
|
||||
case (pag, topics) =>
|
||||
ctx.me.??(u => env.study.topicApi.userTopics(u.id) dmap some) map { case (pag, topics) =>
|
||||
Ok(html.study.topic.show(topic, pag, Order(order), topics))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,40 +338,39 @@ final class Study(
|
|||
def embed(id: String, chapterId: String) =
|
||||
Action.async { implicit req =>
|
||||
env.study.api.byIdWithChapter(id, chapterId).map(_.filterNot(_.study.isPrivate)) flatMap {
|
||||
_.fold(embedNotFound) {
|
||||
case WithChapter(study, chapter) =>
|
||||
for {
|
||||
chapters <- env.study.chapterRepo.idNames(study.id)
|
||||
studyJson <- env.study.jsonView(
|
||||
study.copy(
|
||||
members = lila.study.StudyMembers(Map.empty) // don't need no members
|
||||
),
|
||||
List(chapter.metadata),
|
||||
chapter,
|
||||
none
|
||||
)
|
||||
setup = chapter.setup
|
||||
initialFen = chapter.root.fen.some
|
||||
pov = userAnalysisC.makePov(initialFen, setup.variant)
|
||||
baseData = env.round.jsonView.userAnalysisJson(
|
||||
pov,
|
||||
lila.pref.Pref.default,
|
||||
initialFen,
|
||||
setup.orientation,
|
||||
owner = false,
|
||||
me = none
|
||||
)
|
||||
analysis = baseData ++ Json.obj(
|
||||
"treeParts" -> partitionTreeJsonWriter.writes {
|
||||
lila.study.TreeBuilder.makeRoot(chapter.root, setup.variant)
|
||||
}
|
||||
)
|
||||
data = lila.study.JsonView.JsData(study = studyJson, analysis = analysis)
|
||||
result <- negotiate(
|
||||
html = Ok(html.study.embed(study, chapter, chapters, data)).fuccess,
|
||||
api = _ => Ok(Json.obj("study" -> data.study, "analysis" -> data.analysis)).fuccess
|
||||
)
|
||||
} yield result
|
||||
_.fold(embedNotFound) { case WithChapter(study, chapter) =>
|
||||
for {
|
||||
chapters <- env.study.chapterRepo.idNames(study.id)
|
||||
studyJson <- env.study.jsonView(
|
||||
study.copy(
|
||||
members = lila.study.StudyMembers(Map.empty) // don't need no members
|
||||
),
|
||||
List(chapter.metadata),
|
||||
chapter,
|
||||
none
|
||||
)
|
||||
setup = chapter.setup
|
||||
initialFen = chapter.root.fen.some
|
||||
pov = userAnalysisC.makePov(initialFen, setup.variant)
|
||||
baseData = env.round.jsonView.userAnalysisJson(
|
||||
pov,
|
||||
lila.pref.Pref.default,
|
||||
initialFen,
|
||||
setup.orientation,
|
||||
owner = false,
|
||||
me = none
|
||||
)
|
||||
analysis = baseData ++ Json.obj(
|
||||
"treeParts" -> partitionTreeJsonWriter.writes {
|
||||
lila.study.TreeBuilder.makeRoot(chapter.root, setup.variant)
|
||||
}
|
||||
)
|
||||
data = lila.study.JsonView.JsData(study = studyJson, analysis = analysis)
|
||||
result <- negotiate(
|
||||
html = Ok(html.study.embed(study, chapter, chapters, data)).fuccess,
|
||||
api = _ => Ok(Json.obj("study" -> data.study, "analysis" -> data.analysis)).fuccess
|
||||
)
|
||||
} yield result
|
||||
}
|
||||
} map NoCache
|
||||
}
|
||||
|
@ -446,17 +442,16 @@ final class Study(
|
|||
def chapterPgn(id: String, chapterId: String) =
|
||||
Open { implicit ctx =>
|
||||
env.study.api.byIdWithChapter(id, chapterId) flatMap {
|
||||
_.fold(notFound) {
|
||||
case WithChapter(study, chapter) =>
|
||||
CanViewResult(study) {
|
||||
lila.mon.export.pgn.studyChapter.increment()
|
||||
Ok(env.study.pgnDump.ofChapter(study, requestPgnFlags(ctx.req))(chapter).toString)
|
||||
.withHeaders(
|
||||
CONTENT_DISPOSITION -> s"attachment; filename=${env.study.pgnDump.filename(study, chapter)}.pgn"
|
||||
)
|
||||
.as(pgnContentType)
|
||||
.fuccess
|
||||
}
|
||||
_.fold(notFound) { case WithChapter(study, chapter) =>
|
||||
CanViewResult(study) {
|
||||
lila.mon.export.pgn.studyChapter.increment()
|
||||
Ok(env.study.pgnDump.ofChapter(study, requestPgnFlags(ctx.req))(chapter).toString)
|
||||
.withHeaders(
|
||||
CONTENT_DISPOSITION -> s"attachment; filename=${env.study.pgnDump.filename(study, chapter)}.pgn"
|
||||
)
|
||||
.as(pgnContentType)
|
||||
.fuccess
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -471,17 +466,16 @@ final class Study(
|
|||
def chapterGif(id: String, chapterId: String) =
|
||||
Open { implicit ctx =>
|
||||
env.study.api.byIdWithChapter(id, chapterId) flatMap {
|
||||
_.fold(notFound) {
|
||||
case WithChapter(study, chapter) =>
|
||||
CanViewResult(study) {
|
||||
env.study.gifExport.ofChapter(chapter) map { stream =>
|
||||
Ok.chunked(stream)
|
||||
.withHeaders(
|
||||
noProxyBufferHeader,
|
||||
CONTENT_DISPOSITION -> s"attachment; filename=${env.study.pgnDump.filename(study, chapter)}.gif"
|
||||
) as "image/gif"
|
||||
}
|
||||
_.fold(notFound) { case WithChapter(study, chapter) =>
|
||||
CanViewResult(study) {
|
||||
env.study.gifExport.ofChapter(chapter) map { stream =>
|
||||
Ok.chunked(stream)
|
||||
.withHeaders(
|
||||
noProxyBufferHeader,
|
||||
CONTENT_DISPOSITION -> s"attachment; filename=${env.study.pgnDump.filename(study, chapter)}.gif"
|
||||
) as "image/gif"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -512,11 +506,10 @@ final class Study(
|
|||
def topics =
|
||||
Open { implicit ctx =>
|
||||
env.study.topicApi.popular(50) zip
|
||||
ctx.me.??(u => env.study.topicApi.userTopics(u.id) dmap some) map {
|
||||
case (popular, mine) =>
|
||||
ctx.me.??(u => env.study.topicApi.userTopics(u.id) dmap some) map { case (popular, mine) =>
|
||||
val form = mine map lila.study.StudyForm.topicsForm
|
||||
Ok(html.study.topic.index(popular, mine, form))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setTopics =
|
||||
|
|
|
@ -331,18 +331,16 @@ final class Team(
|
|||
OptionFuRedirectUrl(for {
|
||||
requestOption <- api request requestId
|
||||
teamOption <- requestOption.??(req => env.team.teamRepo.byLeader(req.team, me.id))
|
||||
} yield (teamOption, requestOption).mapN((_, _))) {
|
||||
case (team, request) =>
|
||||
implicit val req = ctx.body
|
||||
forms.processRequest
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
_ => fuccess(routes.Team.show(team.id).toString),
|
||||
{
|
||||
case (decision, url) =>
|
||||
api.processRequest(team, request, decision == "accept") inject url
|
||||
}
|
||||
)
|
||||
} yield (teamOption, requestOption).mapN((_, _))) { case (team, request) =>
|
||||
implicit val req = ctx.body
|
||||
forms.processRequest
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
_ => fuccess(routes.Team.show(team.id).toString),
|
||||
{ case (decision, url) =>
|
||||
api.processRequest(team, request, decision == "accept") inject url
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -410,10 +410,9 @@ final class Tournament(
|
|||
|
||||
def categShields(k: String) =
|
||||
Open { implicit ctx =>
|
||||
OptionFuOk(env.tournament.shieldApi.byCategKey(k)) {
|
||||
case (categ, awards) =>
|
||||
env.user.lightUserApi preloadMany awards.map(_.owner.value) inject
|
||||
html.tournament.shields.byCateg(categ, awards)
|
||||
OptionFuOk(env.tournament.shieldApi.byCategKey(k)) { case (categ, awards) =>
|
||||
env.user.lightUserApi preloadMany awards.map(_.owner.value) inject
|
||||
html.tournament.shields.byCateg(categ, awards)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,25 +39,23 @@ final class Tv(
|
|||
}
|
||||
|
||||
private def lichessTv(channel: lila.tv.Tv.Channel)(implicit ctx: Context) =
|
||||
OptionFuResult(env.tv.tv getGameAndHistory channel) {
|
||||
case (game, history) =>
|
||||
val flip = getBool("flip")
|
||||
val natural = Pov naturalOrientation game
|
||||
val pov = if (flip) !natural else natural
|
||||
val onTv = lila.round.OnLichessTv(channel.key, flip)
|
||||
negotiate(
|
||||
html = env.tournament.api.gameView.watcher(pov.game) flatMap { tour =>
|
||||
env.api.roundApi.watcher(pov, tour, lila.api.Mobile.Api.currentVersion, tv = onTv.some) zip
|
||||
env.game.crosstableApi.withMatchup(game) zip
|
||||
env.tv.tv.getChampions map {
|
||||
case data ~ cross ~ champions =>
|
||||
NoCache {
|
||||
Ok(html.tv.index(channel, champions, pov, data, cross, history))
|
||||
}
|
||||
OptionFuResult(env.tv.tv getGameAndHistory channel) { case (game, history) =>
|
||||
val flip = getBool("flip")
|
||||
val natural = Pov naturalOrientation game
|
||||
val pov = if (flip) !natural else natural
|
||||
val onTv = lila.round.OnLichessTv(channel.key, flip)
|
||||
negotiate(
|
||||
html = env.tournament.api.gameView.watcher(pov.game) flatMap { tour =>
|
||||
env.api.roundApi.watcher(pov, tour, lila.api.Mobile.Api.currentVersion, tv = onTv.some) zip
|
||||
env.game.crosstableApi.withMatchup(game) zip
|
||||
env.tv.tv.getChampions map { case data ~ cross ~ champions =>
|
||||
NoCache {
|
||||
Ok(html.tv.index(channel, champions, pov, data, cross, history))
|
||||
}
|
||||
}
|
||||
},
|
||||
api = apiVersion => env.api.roundApi.watcher(pov, none, apiVersion, tv = onTv.some) map { Ok(_) }
|
||||
)
|
||||
},
|
||||
api = apiVersion => env.api.roundApi.watcher(pov, none, apiVersion, tv = onTv.some) map { Ok(_) }
|
||||
)
|
||||
}
|
||||
|
||||
def games = gamesChannel(lila.tv.Tv.Channel.Best.key)
|
||||
|
@ -65,11 +63,10 @@ final class Tv(
|
|||
def gamesChannel(chanKey: String) =
|
||||
Open { implicit ctx =>
|
||||
(lila.tv.Tv.Channel.byKey get chanKey) ?? { channel =>
|
||||
env.tv.tv.getChampions zip env.tv.tv.getGames(channel, 15) map {
|
||||
case (champs, games) =>
|
||||
NoCache {
|
||||
Ok(html.tv.games(channel, games map Pov.naturalOrientation, champs))
|
||||
}
|
||||
env.tv.tv.getChampions zip env.tv.tv.getGames(channel, 15) map { case (champs, games) =>
|
||||
NoCache {
|
||||
Ok(html.tv.games(channel, games map Pov.naturalOrientation, champs))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,8 +79,8 @@ final class Tv(
|
|||
import play.api.libs.EventSource
|
||||
env.round.tvBroadcast ? TvBroadcast.Connect mapTo
|
||||
manifest[TvBroadcast.SourceType] map { source =>
|
||||
Ok.chunked(source via EventSource.flow).as(ContentTypes.EVENT_STREAM) pipe noProxyBuffer
|
||||
}
|
||||
Ok.chunked(source via EventSource.flow).as(ContentTypes.EVENT_STREAM) pipe noProxyBuffer
|
||||
}
|
||||
}
|
||||
|
||||
def frame =
|
||||
|
|
|
@ -48,9 +48,9 @@ final class User(
|
|||
val userId = UserModel normalize username
|
||||
env.game.cached.lastPlayedPlayingId(userId) orElse
|
||||
env.game.gameRepo.quickLastPlayedId(userId) flatMap {
|
||||
case None => NotFound("No ongoing game").fuccess
|
||||
case Some(gameId) => gameC.exportGame(gameId, req)
|
||||
}
|
||||
case None => NotFound("No ongoing game").fuccess
|
||||
case Some(gameId) => gameC.exportGame(gameId, req)
|
||||
}
|
||||
}
|
||||
|
||||
private def apiGames(u: UserModel, filter: String, page: Int)(implicit ctx: BodyContext[_]) = {
|
||||
|
@ -146,26 +146,26 @@ final class User(
|
|||
ctx.userId.?? { env.game.crosstableApi.fetchOrEmpty(user.id, _) dmap some } zip
|
||||
ctx.isAuth.?? { env.pref.api.followable(user.id) } zip
|
||||
ctx.userId.?? { relationApi.fetchRelation(_, user.id) } flatMap {
|
||||
case blocked ~ crosstable ~ followable ~ relation =>
|
||||
val ping = env.socket.isOnline(user.id) ?? UserLagCache.getLagRating(user.id)
|
||||
negotiate(
|
||||
html = !ctx.is(user) ?? currentlyPlaying(user) map { pov =>
|
||||
Ok(html.user.mini(user, pov, blocked, followable, relation, ping, crosstable))
|
||||
.withHeaders(CACHE_CONTROL -> "max-age=5")
|
||||
},
|
||||
api = _ => {
|
||||
import lila.game.JsonView.crosstableWrites
|
||||
fuccess(
|
||||
Ok(
|
||||
Json.obj(
|
||||
"crosstable" -> crosstable,
|
||||
"perfs" -> lila.user.JsonView.perfs(user, user.best8Perfs)
|
||||
case blocked ~ crosstable ~ followable ~ relation =>
|
||||
val ping = env.socket.isOnline(user.id) ?? UserLagCache.getLagRating(user.id)
|
||||
negotiate(
|
||||
html = !ctx.is(user) ?? currentlyPlaying(user) map { pov =>
|
||||
Ok(html.user.mini(user, pov, blocked, followable, relation, ping, crosstable))
|
||||
.withHeaders(CACHE_CONTROL -> "max-age=5")
|
||||
},
|
||||
api = _ => {
|
||||
import lila.game.JsonView.crosstableWrites
|
||||
fuccess(
|
||||
Ok(
|
||||
Json.obj(
|
||||
"crosstable" -> crosstable,
|
||||
"perfs" -> lila.user.JsonView.perfs(user, user.best8Perfs)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
else fuccess(Ok(html.user.bits.miniClosed(user)))
|
||||
}
|
||||
}
|
||||
|
@ -364,9 +364,9 @@ final class User(
|
|||
.logTimeIfGt(s"$username noteApi.forMod", 2 seconds)) zip
|
||||
env.playban.api.bans(familyUserIds).logTimeIfGt(s"$username playban.bans", 2 seconds) zip
|
||||
lila.security.UserSpy.withMeSortedWithEmails(env.user.repo, user, spy) map {
|
||||
case notes ~ bans ~ othersWithEmail =>
|
||||
html.user.mod.otherUsers(user, spy, othersWithEmail, notes, bans, nbOthers)
|
||||
}
|
||||
case notes ~ bans ~ othersWithEmail =>
|
||||
html.user.mod.otherUsers(user, spy, othersWithEmail, notes, bans, nbOthers)
|
||||
}
|
||||
}
|
||||
val identification = spyFu map { spy =>
|
||||
(isGranted(_.Doxing) || (user.lameOrAlt && !user.hasTitle)) ??
|
||||
|
@ -461,11 +461,10 @@ final class User(
|
|||
relateds <-
|
||||
ops
|
||||
.zip(followables)
|
||||
.map {
|
||||
case ((u, nb), followable) =>
|
||||
relationApi.fetchRelation(me.id, u.id) map {
|
||||
lila.relation.Related(u, nb.some, followable, _)
|
||||
}
|
||||
.map { case ((u, nb), followable) =>
|
||||
relationApi.fetchRelation(me.id, u.id) map {
|
||||
lila.relation.Related(u, nb.some, followable, _)
|
||||
}
|
||||
}
|
||||
.sequenceFu
|
||||
} yield html.user.opponents(me, relateds)
|
||||
|
@ -516,7 +515,8 @@ final class User(
|
|||
env.user.repo nameExists term map { r =>
|
||||
Ok(JsBoolean(r))
|
||||
}
|
||||
case Some(term) => {
|
||||
case Some(term) =>
|
||||
{
|
||||
(get("tour"), get("swiss")) match {
|
||||
case (Some(tourId), _) => env.tournament.playerRepo.searchPlayers(tourId, term, 10)
|
||||
case (_, Some(swissId)) => env.swiss.api.searchPlayers(lila.swiss.Swiss.Id(swissId), term, 10)
|
||||
|
|
|
@ -113,8 +113,7 @@ final class UserAnalysis(
|
|||
env.game.gameRepo initialFen pov.gameId flatMap { initialFen =>
|
||||
gameC.preloadUsers(pov.game) zip
|
||||
(env.analyse.analyser get pov.game) zip
|
||||
env.game.crosstableApi(pov.game) flatMap {
|
||||
case _ ~ analysis ~ crosstable =>
|
||||
env.game.crosstableApi(pov.game) flatMap { case _ ~ analysis ~ crosstable =>
|
||||
import lila.game.JsonView.crosstableWrites
|
||||
env.api.roundApi.review(
|
||||
pov,
|
||||
|
@ -126,7 +125,7 @@ final class UserAnalysis(
|
|||
) map { data =>
|
||||
Ok(data.add("crosstable", crosstable))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XHR only
|
||||
|
@ -142,19 +141,18 @@ final class UserAnalysis(
|
|||
.inMemory(data)
|
||||
.fold(
|
||||
err => BadRequest(jsonError(err)).fuccess,
|
||||
{
|
||||
case (game, fen) =>
|
||||
val pov = Pov(game, chess.White)
|
||||
env.api.roundApi.userAnalysisJson(
|
||||
pov,
|
||||
ctx.pref,
|
||||
initialFen = fen,
|
||||
pov.color,
|
||||
owner = false,
|
||||
me = ctx.me
|
||||
) map { data =>
|
||||
Ok(data)
|
||||
}
|
||||
{ case (game, fen) =>
|
||||
val pov = Pov(game, chess.White)
|
||||
env.api.roundApi.userAnalysisJson(
|
||||
pov,
|
||||
ctx.pref,
|
||||
initialFen = fen,
|
||||
pov.color,
|
||||
owner = false,
|
||||
me = ctx.me
|
||||
) map { data =>
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -174,11 +172,11 @@ final class UserAnalysis(
|
|||
forecasts =>
|
||||
env.round.forecastApi.save(pov, forecasts) >>
|
||||
env.round.forecastApi.loadForDisplay(pov) map {
|
||||
case None => Ok(Json.obj("none" -> true))
|
||||
case Some(fc) => Ok(Json toJson fc) as JSON
|
||||
} recover {
|
||||
case Forecast.OutOfSync => Ok(Json.obj("reload" -> true))
|
||||
}
|
||||
case None => Ok(Json.obj("none" -> true))
|
||||
case Some(fc) => Ok(Json toJson fc) as JSON
|
||||
} recover { case Forecast.OutOfSync =>
|
||||
Ok(Json.obj("reload" -> true))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,10 +33,9 @@ final class Video(env: Env) extends LilaController(env) {
|
|||
}
|
||||
case None =>
|
||||
api.video.byTags(ctx.me, control.filter.tags, getInt("page") | 1) zip
|
||||
api.video.count.apply map {
|
||||
case (videos, count) =>
|
||||
api.video.count.apply map { case (videos, count) =>
|
||||
Ok(html.video.index(videos, count, control))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,10 +49,9 @@ final class Video(env: Env) extends LilaController(env) {
|
|||
api.video.similar(ctx.me, video, 9) zip
|
||||
ctx.userId.?? { userId =>
|
||||
api.view.add(View.make(videoId = video.id, userId = userId))
|
||||
} map {
|
||||
case (similar, _) =>
|
||||
} map { case (similar, _) =>
|
||||
Ok(html.video.show(video, similar, control))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,10 +34,9 @@ final class ErrorHandler(
|
|||
)
|
||||
})
|
||||
else InternalServerError("Sorry, something went wrong.")
|
||||
} recover {
|
||||
case util.control.NonFatal(e) =>
|
||||
lila.log("http").error(s"""Error handler exception on "${exception.getMessage}\"""", e)
|
||||
InternalServerError("Sorry, something went very wrong.")
|
||||
} recover { case util.control.NonFatal(e) =>
|
||||
lila.log("http").error(s"""Error handler exception on "${exception.getMessage}\"""", e)
|
||||
InternalServerError("Sorry, something went very wrong.")
|
||||
}
|
||||
|
||||
override def onClientError(req: RequestHeader, statusCode: Int, msg: String): Fu[Result] =
|
||||
|
|
|
@ -55,38 +55,37 @@ final class Preload(
|
|||
.mon(_.lobby segment "streams")) zip
|
||||
(ctx.userId ?? playbanApi.currentBan).mon(_.lobby segment "playban") zip
|
||||
(ctx.blind ?? ctx.me ?? roundProxy.urgentGames) flatMap {
|
||||
case (
|
||||
data,
|
||||
povs
|
||||
) ~ posts ~ tours ~ events ~ simuls ~ feat ~ entries ~ lead ~ tWinners ~ puzzle ~ streams ~ playban ~ blindGames =>
|
||||
(ctx.me ?? currentGameMyTurn(povs, lightUserApi.sync))
|
||||
.mon(_.lobby segment "currentGame") zip
|
||||
lightUserApi
|
||||
.preloadMany {
|
||||
tWinners.map(_.userId) ::: posts.flatMap(_.userId) ::: entries.flatMap(_.userIds).toList
|
||||
}
|
||||
.mon(_.lobby segment "lightUsers") map {
|
||||
case (currentGame, _) =>
|
||||
Homepage(
|
||||
case (
|
||||
data,
|
||||
entries,
|
||||
posts,
|
||||
tours,
|
||||
events,
|
||||
simuls,
|
||||
feat,
|
||||
lead,
|
||||
tWinners,
|
||||
puzzle,
|
||||
streams.excludeUsers(events.flatMap(_.hostedBy)),
|
||||
lastPostCache.apply,
|
||||
playban,
|
||||
currentGame,
|
||||
simulIsFeaturable,
|
||||
blindGames
|
||||
)
|
||||
}
|
||||
}
|
||||
povs
|
||||
) ~ posts ~ tours ~ events ~ simuls ~ feat ~ entries ~ lead ~ tWinners ~ puzzle ~ streams ~ playban ~ blindGames =>
|
||||
(ctx.me ?? currentGameMyTurn(povs, lightUserApi.sync))
|
||||
.mon(_.lobby segment "currentGame") zip
|
||||
lightUserApi
|
||||
.preloadMany {
|
||||
tWinners.map(_.userId) ::: posts.flatMap(_.userId) ::: entries.flatMap(_.userIds).toList
|
||||
}
|
||||
.mon(_.lobby segment "lightUsers") map { case (currentGame, _) =>
|
||||
Homepage(
|
||||
data,
|
||||
entries,
|
||||
posts,
|
||||
tours,
|
||||
events,
|
||||
simuls,
|
||||
feat,
|
||||
lead,
|
||||
tWinners,
|
||||
puzzle,
|
||||
streams.excludeUsers(events.flatMap(_.hostedBy)),
|
||||
lastPostCache.apply,
|
||||
playban,
|
||||
currentGame,
|
||||
simulIsFeaturable,
|
||||
blindGames
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def currentGameMyTurn(user: User): Fu[Option[CurrentGame]] =
|
||||
gameRepo.playingRealtimeNoAi(user).flatMap {
|
||||
|
|
|
@ -76,10 +76,9 @@ object UserInfo {
|
|||
} zip
|
||||
ctx.userId.?? { myId =>
|
||||
relationApi.fetchBlocks(u.id, myId).mon(_.user segment "blocks")
|
||||
} dmap {
|
||||
case relation ~ notes ~ followable ~ blocked =>
|
||||
} dmap { case relation ~ notes ~ followable ~ blocked =>
|
||||
Social(relation, notes, followable, blocked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class NbGames(
|
||||
|
@ -103,14 +102,14 @@ object UserInfo {
|
|||
gameCached.nbPlaying(u.id).mon(_.user segment "nbPlaying") zip
|
||||
gameCached.nbImportedBy(u.id).mon(_.user segment "nbImported") zip
|
||||
bookmarkApi.countByUser(u).mon(_.user segment "nbBookmarks") dmap {
|
||||
case crosstable ~ playing ~ imported ~ bookmark =>
|
||||
NbGames(
|
||||
crosstable,
|
||||
playing = playing,
|
||||
imported = imported,
|
||||
bookmark = bookmark
|
||||
)
|
||||
}
|
||||
case crosstable ~ playing ~ imported ~ bookmark =>
|
||||
NbGames(
|
||||
crosstable,
|
||||
playing = playing,
|
||||
imported = imported,
|
||||
bookmark = bookmark
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final class UserInfoApi(
|
||||
|
@ -146,31 +145,31 @@ object UserInfo {
|
|||
playbanApi.completionRate(user.id).mon(_.user segment "completion") zip
|
||||
(nbs.playing > 0) ?? isHostingSimul(user.id).mon(_.user segment "simul") zip
|
||||
userCached.rankingsOf(user.id) map {
|
||||
case ratingChart ~ nbFollowers ~ nbBlockers ~ nbPosts ~ nbStudies ~ trophies ~ shields ~ revols ~ teamIds ~ isCoach ~ isStreamer ~ insightVisible ~ completionRate ~ hasSimul ~ ranks =>
|
||||
new UserInfo(
|
||||
user = user,
|
||||
ranks = ranks,
|
||||
nbs = nbs,
|
||||
hasSimul = hasSimul,
|
||||
ratingChart = ratingChart,
|
||||
nbFollowers = nbFollowers,
|
||||
nbBlockers = nbBlockers,
|
||||
nbPosts = nbPosts,
|
||||
nbStudies = nbStudies,
|
||||
trophies = trophies ::: trophyApi.roleBasedTrophies(
|
||||
user,
|
||||
Granter(_.PublicMod)(user),
|
||||
Granter(_.Developer)(user),
|
||||
Granter(_.Verified)(user)
|
||||
),
|
||||
shields = shields,
|
||||
revolutions = revols,
|
||||
teamIds = teamIds,
|
||||
isStreamer = isStreamer,
|
||||
isCoach = isCoach,
|
||||
insightVisible = insightVisible,
|
||||
completionRate = completionRate
|
||||
)
|
||||
}
|
||||
case ratingChart ~ nbFollowers ~ nbBlockers ~ nbPosts ~ nbStudies ~ trophies ~ shields ~ revols ~ teamIds ~ isCoach ~ isStreamer ~ insightVisible ~ completionRate ~ hasSimul ~ ranks =>
|
||||
new UserInfo(
|
||||
user = user,
|
||||
ranks = ranks,
|
||||
nbs = nbs,
|
||||
hasSimul = hasSimul,
|
||||
ratingChart = ratingChart,
|
||||
nbFollowers = nbFollowers,
|
||||
nbBlockers = nbBlockers,
|
||||
nbPosts = nbPosts,
|
||||
nbStudies = nbStudies,
|
||||
trophies = trophies ::: trophyApi.roleBasedTrophies(
|
||||
user,
|
||||
Granter(_.PublicMod)(user),
|
||||
Granter(_.Developer)(user),
|
||||
Granter(_.Verified)(user)
|
||||
),
|
||||
shields = shields,
|
||||
revolutions = revols,
|
||||
teamIds = teamIds,
|
||||
isStreamer = isStreamer,
|
||||
isCoach = isCoach,
|
||||
insightVisible = insightVisible,
|
||||
completionRate = completionRate
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,10 +29,9 @@ trait ChessgroundHelper {
|
|||
val pieces =
|
||||
if (ctx.pref.isBlindfold) ""
|
||||
else
|
||||
board.pieces.map {
|
||||
case (pos, piece) =>
|
||||
val klass = s"${piece.color.name} ${piece.role.name}"
|
||||
s"""<piece class="$klass" style="top:${top(pos)}%;left:${left(pos)}%"></piece>"""
|
||||
board.pieces.map { case (pos, piece) =>
|
||||
val klass = s"${piece.color.name} ${piece.role.name}"
|
||||
s"""<piece class="$klass" style="top:${top(pos)}%;left:${left(pos)}%"></piece>"""
|
||||
} mkString ""
|
||||
s"$highlights$pieces"
|
||||
}
|
||||
|
@ -44,8 +43,8 @@ trait ChessgroundHelper {
|
|||
chessground(
|
||||
board = pov.game.board,
|
||||
orient = pov.color,
|
||||
lastMove = pov.game.history.lastMove.map(_.origDest) ?? {
|
||||
case (orig, dest) => List(orig, dest)
|
||||
lastMove = pov.game.history.lastMove.map(_.origDest) ?? { case (orig, dest) =>
|
||||
List(orig, dest)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -140,12 +140,11 @@ trait FormHelper { self: I18nHelper =>
|
|||
cls := "form-control"
|
||||
)(disabled option (st.disabled := true))(validationModifiers(field))(
|
||||
default map { option(value := "")(_) },
|
||||
options.toSeq map {
|
||||
case (value, name) =>
|
||||
option(
|
||||
st.value := value.toString,
|
||||
field.value.has(value.toString) option selected
|
||||
)(name)
|
||||
options.toSeq map { case (value, name) =>
|
||||
option(
|
||||
st.value := value.toString,
|
||||
field.value.has(value.toString) option selected
|
||||
)(name)
|
||||
}
|
||||
),
|
||||
disabled option hidden(field)
|
||||
|
|
|
@ -179,13 +179,14 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
|
|||
case Some(_) => trans.blackLeftTheGame.txt()
|
||||
case None => trans.draw.txt()
|
||||
}
|
||||
case S.Draw => trans.draw.txt()
|
||||
case S.Outoftime => (game.turnColor, game.loser) match {
|
||||
case (White, Some(_)) => trans.whiteTimeOut.txt()
|
||||
case (White, None) => trans.whiteTimeOut.txt() + " • " + trans.draw.txt()
|
||||
case (Black, Some(_)) => trans.blackTimeOut.txt()
|
||||
case (Black, None) => trans.blackTimeOut.txt() + " • " + trans.draw.txt()
|
||||
}
|
||||
case S.Draw => trans.draw.txt()
|
||||
case S.Outoftime =>
|
||||
(game.turnColor, game.loser) match {
|
||||
case (White, Some(_)) => trans.whiteTimeOut.txt()
|
||||
case (White, None) => trans.whiteTimeOut.txt() + " • " + trans.draw.txt()
|
||||
case (Black, Some(_)) => trans.blackTimeOut.txt()
|
||||
case (Black, None) => trans.blackTimeOut.txt() + " • " + trans.draw.txt()
|
||||
}
|
||||
case S.NoStart =>
|
||||
val color = game.loser.fold(Color.white)(_.color).name.capitalize
|
||||
s"$color didn't move"
|
||||
|
|
|
@ -57,8 +57,8 @@ trait TournamentHelper { self: I18nHelper with DateHelper with UserHelper =>
|
|||
|
||||
def apply(name: String): Frag =
|
||||
raw {
|
||||
replacements.foldLeft(name) {
|
||||
case (n, (from, to)) => n.replace(from, to)
|
||||
replacements.foldLeft(name) { case (n, (from, to)) =>
|
||||
n.replace(from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
|
|||
PerfType(perfKey) map { showPerfRating(u, _) }
|
||||
|
||||
def showBestPerf(u: User)(implicit lang: Lang): Option[Frag] =
|
||||
u.perfs.bestPerf map {
|
||||
case (pt, perf) => showPerfRating(pt, perf)
|
||||
u.perfs.bestPerf map { case (pt, perf) =>
|
||||
showPerfRating(pt, perf)
|
||||
}
|
||||
def showBestPerfs(u: User, nb: Int)(implicit lang: Lang): List[Frag] =
|
||||
u.perfs.bestPerfs(nb) map {
|
||||
case (pt, perf) => showPerfRating(pt, perf)
|
||||
u.perfs.bestPerfs(nb) map { case (pt, perf) =>
|
||||
showPerfRating(pt, perf)
|
||||
}
|
||||
|
||||
def showRatingDiff(diff: Int): Frag =
|
||||
|
@ -219,8 +219,8 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
|
|||
withPerfRating match {
|
||||
case Some(perfType) => renderRating(user.perfs(perfType))
|
||||
case _ if withBestRating =>
|
||||
user.perfs.bestPerf ?? {
|
||||
case (_, perf) => renderRating(perf)
|
||||
user.perfs.bestPerf ?? { case (_, perf) =>
|
||||
renderRating(perf)
|
||||
}
|
||||
case _ => ""
|
||||
}
|
||||
|
@ -266,8 +266,8 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
|
|||
val name = user.titleUsername
|
||||
val nbGames = user.count.game
|
||||
val createdAt = org.joda.time.format.DateTimeFormat forStyle "M-" print user.createdAt
|
||||
val currentRating = user.perfs.bestPerf ?? {
|
||||
case (pt, perf) => s" Current ${pt.trans} rating: ${perf.intRating}."
|
||||
val currentRating = user.perfs.bestPerf ?? { case (pt, perf) =>
|
||||
s" Current ${pt.trans} rating: ${perf.intRating}."
|
||||
}
|
||||
s"$name played $nbGames games since $createdAt.$currentRating"
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ object activity {
|
|||
a.puzzles map renderPuzzles,
|
||||
a.games map renderGames,
|
||||
a.posts map renderPosts,
|
||||
a.corresMoves map {
|
||||
case (nb, povs) => renderCorresMoves(nb, povs)
|
||||
a.corresMoves map { case (nb, povs) =>
|
||||
renderCorresMoves(nb, povs)
|
||||
},
|
||||
a.corresEnds map {
|
||||
case (score, povs) => renderCorresEnds(score, povs)
|
||||
a.corresEnds map { case (score, povs) =>
|
||||
renderCorresEnds(score, povs)
|
||||
},
|
||||
a.follows map renderFollows,
|
||||
a.simuls map renderSimuls(u),
|
||||
|
@ -85,34 +85,32 @@ object activity {
|
|||
)
|
||||
|
||||
private def renderGames(games: Games)(implicit ctx: Context) =
|
||||
games.value.toSeq.sortBy(-_._2.size).map {
|
||||
case (pt, score) =>
|
||||
entryTag(
|
||||
iconTag(pt.iconChar),
|
||||
scoreFrag(score),
|
||||
div(
|
||||
trans.activity.playedNbGames.plural(score.size, score.size, pt.trans),
|
||||
score.rp.filterNot(_.isEmpty).map(ratingProgFrag)
|
||||
)
|
||||
games.value.toSeq.sortBy(-_._2.size).map { case (pt, score) =>
|
||||
entryTag(
|
||||
iconTag(pt.iconChar),
|
||||
scoreFrag(score),
|
||||
div(
|
||||
trans.activity.playedNbGames.plural(score.size, score.size, pt.trans),
|
||||
score.rp.filterNot(_.isEmpty).map(ratingProgFrag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def renderPosts(posts: Map[lila.forum.Topic, List[lila.forum.Post]])(implicit ctx: Context) =
|
||||
ctx.noKid option entryTag(
|
||||
iconTag("d"),
|
||||
div(
|
||||
posts.toSeq.map {
|
||||
case (topic, posts) =>
|
||||
val url = routes.ForumTopic.show(topic.categId, topic.slug)
|
||||
frag(
|
||||
trans.activity.postedNbMessages
|
||||
.plural(posts.size, posts.size, a(href := url)(shorten(topic.name, 70))),
|
||||
subTag(
|
||||
posts.map { post =>
|
||||
div(cls := "line")(a(href := routes.ForumPost.redirect(post.id))(shorten(post.text, 120)))
|
||||
}
|
||||
)
|
||||
posts.toSeq.map { case (topic, posts) =>
|
||||
val url = routes.ForumTopic.show(topic.categId, topic.slug)
|
||||
frag(
|
||||
trans.activity.postedNbMessages
|
||||
.plural(posts.size, posts.size, a(href := url)(shorten(topic.name, 70))),
|
||||
subTag(
|
||||
posts.map { post =>
|
||||
div(cls := "line")(a(href := routes.ForumPost.redirect(post.id))(shorten(post.text, 120)))
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -167,18 +165,17 @@ object activity {
|
|||
entryTag(
|
||||
iconTag("h"),
|
||||
div(
|
||||
List(all.in.map(_ -> true), all.out.map(_ -> false)).flatten map {
|
||||
case (f, in) =>
|
||||
frag(
|
||||
if (in) trans.activity.gainedNbFollowers.pluralSame(f.actualNb)
|
||||
else trans.activity.followedNbPlayers.pluralSame(f.actualNb),
|
||||
subTag(
|
||||
fragList(f.ids.map(id => userIdLink(id.some))),
|
||||
f.nb.map { nb =>
|
||||
frag(" and ", nb - maxSubEntries, " more")
|
||||
}
|
||||
)
|
||||
List(all.in.map(_ -> true), all.out.map(_ -> false)).flatten map { case (f, in) =>
|
||||
frag(
|
||||
if (in) trans.activity.gainedNbFollowers.pluralSame(f.actualNb)
|
||||
else trans.activity.followedNbPlayers.pluralSame(f.actualNb),
|
||||
subTag(
|
||||
fragList(f.ids.map(id => userIdLink(id.some))),
|
||||
f.nb.map { nb =>
|
||||
frag(" and ", nb - maxSubEntries, " more")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -187,24 +184,23 @@ object activity {
|
|||
entryTag(
|
||||
iconTag("f"),
|
||||
div(
|
||||
simuls.groupBy(_.isHost(u.some)).toSeq.map {
|
||||
case (isHost, simuls) =>
|
||||
frag(
|
||||
if (isHost) trans.activity.hostedNbSimuls.pluralSame(simuls.size)
|
||||
else trans.activity.joinedNbSimuls.pluralSame(simuls.size),
|
||||
subTag(
|
||||
simuls.map { s =>
|
||||
div(
|
||||
a(href := routes.Simul.show(s.id))(
|
||||
s.name,
|
||||
" simul by ",
|
||||
userIdLink(s.hostId.some)
|
||||
),
|
||||
scoreFrag(Score(s.wins, s.losses, s.draws, none))
|
||||
)
|
||||
}
|
||||
)
|
||||
simuls.groupBy(_.isHost(u.some)).toSeq.map { case (isHost, simuls) =>
|
||||
frag(
|
||||
if (isHost) trans.activity.hostedNbSimuls.pluralSame(simuls.size)
|
||||
else trans.activity.joinedNbSimuls.pluralSame(simuls.size),
|
||||
subTag(
|
||||
simuls.map { s =>
|
||||
div(
|
||||
a(href := routes.Simul.show(s.id))(
|
||||
s.name,
|
||||
" simul by ",
|
||||
userIdLink(s.hostId.some)
|
||||
),
|
||||
scoreFrag(Score(s.wins, s.losses, s.draws, none))
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -236,12 +236,11 @@ object appeal {
|
|||
div(
|
||||
select(cls := "appeal-presets")(
|
||||
option(st.value := "")("Presets"),
|
||||
ps.value.map {
|
||||
case ModPreset(name, text) =>
|
||||
option(
|
||||
st.value := text,
|
||||
st.title := text
|
||||
)(name)
|
||||
ps.value.map { case ModPreset(name, text) =>
|
||||
option(
|
||||
st.value := text,
|
||||
st.title := text
|
||||
)(name)
|
||||
}
|
||||
),
|
||||
isGranted(_.Presets) option a(href := routes.Mod.presets("appeal"))("Edit presets")
|
||||
|
|
|
@ -48,8 +48,8 @@ object signup {
|
|||
"You must agree to the Lichess policies listed below:"
|
||||
)
|
||||
),
|
||||
agreements.map {
|
||||
case (field, i18n) => form3.checkbox(form(field), i18n())
|
||||
agreements.map { case (field, i18n) =>
|
||||
form3.checkbox(form(field), i18n())
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -46,9 +46,9 @@ object layout {
|
|||
s"font/lichess.woff2"
|
||||
)}" as="font" type="font/woff2" crossorigin>""" +
|
||||
!ctx.pref.pieceNotationIsLetter ??
|
||||
s"""<link rel="preload" href="${assetUrl(
|
||||
s"font/lichess.chess.woff2"
|
||||
)}" as="font" type="font/woff2" crossorigin>"""
|
||||
s"""<link rel="preload" href="${assetUrl(
|
||||
s"font/lichess.chess.woff2"
|
||||
)}" as="font" type="font/woff2" crossorigin>"""
|
||||
}
|
||||
private val manifests = raw(
|
||||
"""<link rel="manifest" href="/manifest.json"><meta name="twitter:site" content="@lichess">"""
|
||||
|
|
|
@ -124,20 +124,19 @@ object student {
|
|||
s" ($nbStudents/${lila.clas.Clas.maxStudents})"
|
||||
),
|
||||
nbStudents > (lila.clas.Clas.maxStudents / 2) option maxStudentsWarning(clas),
|
||||
created map {
|
||||
case Student.WithPassword(student, password) =>
|
||||
flashMessage(cls := "student-add__created")(
|
||||
strong(
|
||||
trans.clas.lichessProfileXCreatedForY(
|
||||
userIdLink(student.userId.some, withOnline = false),
|
||||
student.realName
|
||||
),
|
||||
p(trans.clas.makeSureToCopy()),
|
||||
pre(
|
||||
trans.clas.studentCredentials(student.realName, usernameOrId(student.userId), password.value)
|
||||
)
|
||||
created map { case Student.WithPassword(student, password) =>
|
||||
flashMessage(cls := "student-add__created")(
|
||||
strong(
|
||||
trans.clas.lichessProfileXCreatedForY(
|
||||
userIdLink(student.userId.some, withOnline = false),
|
||||
student.realName
|
||||
),
|
||||
p(trans.clas.makeSureToCopy()),
|
||||
pre(
|
||||
trans.clas.studentCredentials(student.realName, usernameOrId(student.userId), password.value)
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
standardFlash(),
|
||||
(nbStudents <= lila.clas.Clas.maxStudents) option frag(
|
||||
|
@ -222,13 +221,12 @@ object student {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
created map {
|
||||
case Student.WithPassword(student, password) =>
|
||||
tr(
|
||||
td(student.realName),
|
||||
td(usernameOrId(student.userId)),
|
||||
td(password.value)
|
||||
)
|
||||
created map { case Student.WithPassword(student, password) =>
|
||||
tr(
|
||||
td(student.realName),
|
||||
td(usernameOrId(student.userId)),
|
||||
td(password.value)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -76,26 +76,25 @@ object studentDashboard {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
students.sortBy(-_.user.seenAt.??(_.getMillis)).map {
|
||||
case Student.WithUser(student, user) =>
|
||||
tr(
|
||||
td(
|
||||
userLink(
|
||||
user,
|
||||
name = span(
|
||||
strong(user.username),
|
||||
em(student.realName)
|
||||
).some,
|
||||
withTitle = false
|
||||
)
|
||||
),
|
||||
td(dataSort := user.perfs.bestRating, cls := "rating")(cls := "rating")(user.best3Perfs.map {
|
||||
showPerfRating(user, _)
|
||||
}),
|
||||
td(user.count.game.localize),
|
||||
td(user.perfs.puzzle.nb.localize),
|
||||
challengeTd(user)
|
||||
)
|
||||
students.sortBy(-_.user.seenAt.??(_.getMillis)).map { case Student.WithUser(student, user) =>
|
||||
tr(
|
||||
td(
|
||||
userLink(
|
||||
user,
|
||||
name = span(
|
||||
strong(user.username),
|
||||
em(student.realName)
|
||||
).some,
|
||||
withTitle = false
|
||||
)
|
||||
),
|
||||
td(dataSort := user.perfs.bestRating, cls := "rating")(cls := "rating")(user.best3Perfs.map {
|
||||
showPerfRating(user, _)
|
||||
}),
|
||||
td(user.count.game.localize),
|
||||
td(user.perfs.puzzle.nb.localize),
|
||||
challengeTd(user)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -150,21 +150,20 @@ object teacherDashboard {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
students.sortBy(_.user.username).map {
|
||||
case s @ Student.WithUser(_, user) =>
|
||||
val prog = progress(user)
|
||||
tr(
|
||||
studentTd(c, s),
|
||||
td(dataSort := user.perfs(progress.perfType).intRating, cls := "rating")(
|
||||
user.perfs(progress.perfType).showRatingProvisional
|
||||
),
|
||||
td(dataSort := prog.ratingProgress)(
|
||||
ratingProgress(prog.ratingProgress) | trans.clas.na.txt()
|
||||
),
|
||||
td(prog.nb),
|
||||
if (progress.isPuzzle) td(dataSort := prog.winRate)(prog.winRate, "%")
|
||||
else td(dataSort := prog.millis)(showPeriod(prog.period))
|
||||
)
|
||||
students.sortBy(_.user.username).map { case s @ Student.WithUser(_, user) =>
|
||||
val prog = progress(user)
|
||||
tr(
|
||||
studentTd(c, s),
|
||||
td(dataSort := user.perfs(progress.perfType).intRating, cls := "rating")(
|
||||
user.perfs(progress.perfType).showRatingProvisional
|
||||
),
|
||||
td(dataSort := prog.ratingProgress)(
|
||||
ratingProgress(prog.ratingProgress) | trans.clas.na.txt()
|
||||
),
|
||||
td(prog.nb),
|
||||
if (progress.isPuzzle) td(dataSort := prog.winRate)(prog.winRate, "%")
|
||||
else td(dataSort := prog.millis)(showPeriod(prog.period))
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -193,24 +192,23 @@ object teacherDashboard {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
students.sortBy(_.user.username).map {
|
||||
case s @ Student.WithUser(_, user) =>
|
||||
val coord = coordScores.getOrElse(user.id, chess.Color.Map(0, 0))
|
||||
tr(
|
||||
studentTd(c, s),
|
||||
td(dataSort := basicCompletion.getOrElse(user.id, 0))(
|
||||
basicCompletion.getOrElse(user.id, 0).toString,
|
||||
"%"
|
||||
),
|
||||
td(dataSort := practiceCompletion.getOrElse(user.id, 0))(
|
||||
practiceCompletion.getOrElse(user.id, 0).toString,
|
||||
"%"
|
||||
),
|
||||
td(dataSort := coord.white, cls := "coords")(
|
||||
i(cls := "color-icon is white")(coord.white),
|
||||
i(cls := "color-icon is black")(coord.black)
|
||||
)
|
||||
students.sortBy(_.user.username).map { case s @ Student.WithUser(_, user) =>
|
||||
val coord = coordScores.getOrElse(user.id, chess.Color.Map(0, 0))
|
||||
tr(
|
||||
studentTd(c, s),
|
||||
td(dataSort := basicCompletion.getOrElse(user.id, 0))(
|
||||
basicCompletion.getOrElse(user.id, 0).toString,
|
||||
"%"
|
||||
),
|
||||
td(dataSort := practiceCompletion.getOrElse(user.id, 0))(
|
||||
practiceCompletion.getOrElse(user.id, 0).toString,
|
||||
"%"
|
||||
),
|
||||
td(dataSort := coord.white, cls := "coords")(
|
||||
i(cls := "color-icon is white")(coord.white),
|
||||
i(cls := "color-icon is black")(coord.black)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -270,21 +268,20 @@ object teacherDashboard {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
students.sortBy(_.user.username).map {
|
||||
case s @ Student.WithUser(student, user) =>
|
||||
tr(
|
||||
studentTd(c, s),
|
||||
td(dataSort := user.perfs.bestRating, cls := "rating")(user.best3Perfs.map {
|
||||
showPerfRating(user, _)
|
||||
}),
|
||||
td(user.count.game.localize),
|
||||
td(user.perfs.puzzle.nb),
|
||||
td(dataSort := user.seenAt.map(_.getMillis.toString))(user.seenAt.map(momentFromNowOnce)),
|
||||
td(
|
||||
dataSort := (if (student.managed) 1 else 0),
|
||||
student.managed option iconTag("5")(title := trans.clas.managed.txt())
|
||||
)
|
||||
students.sortBy(_.user.username).map { case s @ Student.WithUser(student, user) =>
|
||||
tr(
|
||||
studentTd(c, s),
|
||||
td(dataSort := user.perfs.bestRating, cls := "rating")(user.best3Perfs.map {
|
||||
showPerfRating(user, _)
|
||||
}),
|
||||
td(user.count.game.localize),
|
||||
td(user.perfs.puzzle.nb),
|
||||
td(dataSort := user.seenAt.map(_.getMillis.toString))(user.seenAt.map(momentFromNowOnce)),
|
||||
td(
|
||||
dataSort := (if (student.managed) 1 else 0),
|
||||
student.managed option iconTag("5")(title := trans.clas.managed.txt())
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -50,12 +50,11 @@ object index {
|
|||
"coach-lang",
|
||||
lang.fold("All languages")(LangList.name),
|
||||
langSelections
|
||||
.map {
|
||||
case (code, name) =>
|
||||
a(
|
||||
href := routes.Coach.search(code, order.key),
|
||||
cls := (code == lang.fold("all")(_.code)).option("current")
|
||||
)(name)
|
||||
.map { case (code, name) =>
|
||||
a(
|
||||
href := routes.Coach.search(code, order.key),
|
||||
cls := (code == lang.fold("all")(_.code)).option("current")
|
||||
)(name)
|
||||
}
|
||||
),
|
||||
views.html.base.bits.mselect(
|
||||
|
|
|
@ -95,14 +95,13 @@ object coordinate {
|
|||
List(
|
||||
(trans.coordinates.averageScoreAsWhiteX, score.white),
|
||||
(trans.coordinates.averageScoreAsBlackX, score.black)
|
||||
).map {
|
||||
case (averageScoreX, s) =>
|
||||
div(cls := "chart_container")(
|
||||
s.nonEmpty option frag(
|
||||
p(averageScoreX(raw(s"""<strong>${"%.2f".format(s.sum.toDouble / s.size)}</strong>"""))),
|
||||
div(cls := "user_chart", attr("data-points") := safeJsonValue(Json toJson s))
|
||||
)
|
||||
).map { case (averageScoreX, s) =>
|
||||
div(cls := "chart_container")(
|
||||
s.nonEmpty option frag(
|
||||
p(averageScoreX(raw(s"""<strong>${"%.2f".format(s.sum.toDouble / s.size)}</strong>"""))),
|
||||
div(cls := "user_chart", attr("data-points") := safeJsonValue(Json toJson s))
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -135,15 +135,14 @@ object categ {
|
|||
td(cls := "right")(categ.nbTopics.localize),
|
||||
td(cls := "right")(categ.nbPosts.localize),
|
||||
td(
|
||||
categ.lastPost.map {
|
||||
case (topic, post, page) =>
|
||||
frag(
|
||||
a(href := s"${routes.ForumTopic.show(categ.slug, topic.slug, page)}#${post.number}")(
|
||||
momentFromNow(post.createdAt)
|
||||
),
|
||||
br,
|
||||
trans.by(authorName(post))
|
||||
)
|
||||
categ.lastPost.map { case (topic, post, page) =>
|
||||
frag(
|
||||
a(href := s"${routes.ForumTopic.show(categ.slug, topic.slug, page)}#${post.number}")(
|
||||
momentFromNow(post.createdAt)
|
||||
),
|
||||
br,
|
||||
trans.by(authorName(post))
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -165,28 +165,27 @@ object topic {
|
|||
)
|
||||
)
|
||||
),
|
||||
formWithCaptcha.map {
|
||||
case (form, captcha) =>
|
||||
postForm(
|
||||
cls := "form3 reply",
|
||||
action := s"${routes.ForumPost.create(categ.slug, topic.slug, posts.currentPage)}#reply",
|
||||
novalidate
|
||||
)(
|
||||
form3.group(form("text"), trans.message()) { f =>
|
||||
form3.textarea(f, klass = "post-text-area")(rows := 10, bits.dataTopic := topic.id)
|
||||
},
|
||||
views.html.base.captcha(form, captcha),
|
||||
form3.actions(
|
||||
a(href := routes.ForumCateg.show(categ.slug))(trans.cancel()),
|
||||
isGranted(_.PublicMod) option
|
||||
form3.submit(
|
||||
frag("Reply as a mod"),
|
||||
nameValue = (form("modIcon").name, "true").some,
|
||||
icon = "".some
|
||||
),
|
||||
form3.submit(trans.reply())
|
||||
)
|
||||
formWithCaptcha.map { case (form, captcha) =>
|
||||
postForm(
|
||||
cls := "form3 reply",
|
||||
action := s"${routes.ForumPost.create(categ.slug, topic.slug, posts.currentPage)}#reply",
|
||||
novalidate
|
||||
)(
|
||||
form3.group(form("text"), trans.message()) { f =>
|
||||
form3.textarea(f, klass = "post-text-area")(rows := 10, bits.dataTopic := topic.id)
|
||||
},
|
||||
views.html.base.captcha(form, captcha),
|
||||
form3.actions(
|
||||
a(href := routes.ForumCateg.show(categ.slug))(trans.cancel()),
|
||||
isGranted(_.PublicMod) option
|
||||
form3.submit(
|
||||
frag("Reply as a mod"),
|
||||
nameValue = (form("modIcon").name, "true").some,
|
||||
icon = "".some
|
||||
),
|
||||
form3.submit(trans.reply())
|
||||
)
|
||||
)
|
||||
},
|
||||
pager
|
||||
)
|
||||
|
|
|
@ -21,21 +21,20 @@ object crosstable {
|
|||
}
|
||||
div(cls := "crosstable")(
|
||||
ct.fillSize > 0 option raw { s"""<fill style="flex:${ct.fillSize * 0.75} 1 auto"></fill>""" },
|
||||
ct.results.zipWithIndex.map {
|
||||
case (r, i) =>
|
||||
tag("povs")(
|
||||
cls := List(
|
||||
"sep" -> matchupSepAt.has(i),
|
||||
"current" -> currentId.has(r.gameId)
|
||||
)
|
||||
)(ct.users.toList.map { u =>
|
||||
val (linkClass, text) = r.winnerId match {
|
||||
case Some(w) if w == u.id => "glpt win" -> "1"
|
||||
case None => "glpt" -> "½"
|
||||
case _ => "glpt loss" -> "0"
|
||||
}
|
||||
a(href := s"""${routes.Round.watcher(r.gameId, "white")}?pov=${u.id}""", cls := linkClass)(text)
|
||||
})
|
||||
ct.results.zipWithIndex.map { case (r, i) =>
|
||||
tag("povs")(
|
||||
cls := List(
|
||||
"sep" -> matchupSepAt.has(i),
|
||||
"current" -> currentId.has(r.gameId)
|
||||
)
|
||||
)(ct.users.toList.map { u =>
|
||||
val (linkClass, text) = r.winnerId match {
|
||||
case Some(w) if w == u.id => "glpt win" -> "1"
|
||||
case None => "glpt" -> "½"
|
||||
case _ => "glpt loss" -> "0"
|
||||
}
|
||||
a(href := s"""${routes.Round.watcher(r.gameId, "white")}?pov=${u.id}""", cls := linkClass)(text)
|
||||
})
|
||||
},
|
||||
matchup map { m =>
|
||||
div(cls := "crosstable__matchup", title := trans.currentMatchScore.txt())(ct.users.toList.map { u =>
|
||||
|
|
|
@ -143,14 +143,13 @@ object widgets {
|
|||
aiRating(level)
|
||||
)
|
||||
} getOrElse {
|
||||
(player.nameSplit.fold[Frag](anonSpan) {
|
||||
case (name, rating) =>
|
||||
frag(
|
||||
span(name),
|
||||
rating.map { r =>
|
||||
frag(br, r)
|
||||
}
|
||||
)
|
||||
(player.nameSplit.fold[Frag](anonSpan) { case (name, rating) =>
|
||||
frag(
|
||||
span(name),
|
||||
rating.map { r =>
|
||||
frag(br, r)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,9 +95,9 @@ object home {
|
|||
),
|
||||
currentGame.map(bits.currentGameInfo) orElse
|
||||
playban.map(bits.playbanInfo) getOrElse {
|
||||
if (ctx.blind) blindLobby(blindGames)
|
||||
else bits.lobbyApp
|
||||
},
|
||||
if (ctx.blind) blindLobby(blindGames)
|
||||
else bits.lobbyApp
|
||||
},
|
||||
div(cls := "lobby__side")(
|
||||
ctx.blind option h2("Highlights"),
|
||||
ctx.noKid option st.section(cls := "lobby__streams")(
|
||||
|
|
|
@ -103,36 +103,35 @@ object communication {
|
|||
priv option frag(
|
||||
h2("Recent private chats"),
|
||||
div(cls := "player_chats")(
|
||||
players.map {
|
||||
case (pov, chat) =>
|
||||
div(cls := "game")(
|
||||
a(
|
||||
href := routes.Round.player(pov.fullId),
|
||||
cls := List(
|
||||
"title" -> true,
|
||||
"friend_title" -> pov.game.fromFriend
|
||||
),
|
||||
title := pov.game.fromFriend.option("Friend game")
|
||||
)(
|
||||
usernameOrAnon(pov.opponent.userId),
|
||||
" – ",
|
||||
momentFromNowOnce(pov.game.movedAt)
|
||||
players.map { case (pov, chat) =>
|
||||
div(cls := "game")(
|
||||
a(
|
||||
href := routes.Round.player(pov.fullId),
|
||||
cls := List(
|
||||
"title" -> true,
|
||||
"friend_title" -> pov.game.fromFriend
|
||||
),
|
||||
div(cls := "chat")(
|
||||
chat.lines.map { line =>
|
||||
div(
|
||||
cls := List(
|
||||
"line" -> true,
|
||||
"author" -> (line.author.toLowerCase == u.id)
|
||||
)
|
||||
)(
|
||||
userIdLink(line.author.toLowerCase.some, withOnline = false, withTitle = false),
|
||||
nbsp,
|
||||
richText(line.text)
|
||||
title := pov.game.fromFriend.option("Friend game")
|
||||
)(
|
||||
usernameOrAnon(pov.opponent.userId),
|
||||
" – ",
|
||||
momentFromNowOnce(pov.game.movedAt)
|
||||
),
|
||||
div(cls := "chat")(
|
||||
chat.lines.map { line =>
|
||||
div(
|
||||
cls := List(
|
||||
"line" -> true,
|
||||
"author" -> (line.author.toLowerCase == u.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
)(
|
||||
userIdLink(line.author.toLowerCase.some, withOnline = false, withTitle = false),
|
||||
nbsp,
|
||||
richText(line.text)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
div(cls := "threads")(
|
||||
|
|
|
@ -87,15 +87,14 @@ object gamify {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
leaderboards(period).zipWithIndex.map {
|
||||
case (m, i) =>
|
||||
tr(
|
||||
th(i + 1),
|
||||
th(userIdLink(m.modId.some, withOnline = false)),
|
||||
td(m.action.localize),
|
||||
td(m.report.localize),
|
||||
td(cls := "score")(m.score.localize)
|
||||
)
|
||||
leaderboards(period).zipWithIndex.map { case (m, i) =>
|
||||
tr(
|
||||
th(i + 1),
|
||||
th(userIdLink(m.modId.some, withOnline = false)),
|
||||
td(m.action.localize),
|
||||
td(m.report.localize),
|
||||
td(cls := "score")(m.score.localize)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -27,34 +27,33 @@ object permissions {
|
|||
div(cls := "permission-list")(
|
||||
lila.security.Permission.categorized
|
||||
.filter { case (_, ps) => ps.exists(canGrant(me, _)) }
|
||||
.map {
|
||||
case (categ, perms) =>
|
||||
st.section(
|
||||
h2(categ),
|
||||
perms
|
||||
.filter(canGrant(me, _))
|
||||
.map { perm =>
|
||||
val id = s"permission-${perm.dbKey}"
|
||||
div(
|
||||
cls := isGranted(perm, u) option "granted",
|
||||
title := isGranted(perm, u).?? {
|
||||
Permission.findGranterPackage(userPerms, perm).map { p =>
|
||||
s"Granted by package: $p"
|
||||
}
|
||||
.map { case (categ, perms) =>
|
||||
st.section(
|
||||
h2(categ),
|
||||
perms
|
||||
.filter(canGrant(me, _))
|
||||
.map { perm =>
|
||||
val id = s"permission-${perm.dbKey}"
|
||||
div(
|
||||
cls := isGranted(perm, u) option "granted",
|
||||
title := isGranted(perm, u).?? {
|
||||
Permission.findGranterPackage(userPerms, perm).map { p =>
|
||||
s"Granted by package: $p"
|
||||
}
|
||||
)(
|
||||
span(
|
||||
form3.cmnToggle(
|
||||
id,
|
||||
"permissions[]",
|
||||
checked = u.roles.contains(perm.dbKey),
|
||||
value = perm.dbKey
|
||||
)
|
||||
),
|
||||
label(`for` := id)(perm.name)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)(
|
||||
span(
|
||||
form3.cmnToggle(
|
||||
id,
|
||||
"permissions[]",
|
||||
checked = u.roles.contains(perm.dbKey),
|
||||
value = perm.dbKey
|
||||
)
|
||||
),
|
||||
label(`for` := id)(perm.name)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
form3.actions(
|
||||
|
|
|
@ -24,10 +24,27 @@ object publicChat {
|
|||
div(id := "communication", cls := "page-menu__content public_chat box box-pad")(
|
||||
h2("Tournament Chats"),
|
||||
div(cls := "player_chats")(
|
||||
tourChats.map {
|
||||
case (tournament, chat) =>
|
||||
tourChats.map { case (tournament, chat) =>
|
||||
div(cls := "game")(
|
||||
a(cls := "title", href := routes.Tournament.show(tournament.id))(tournament.name),
|
||||
div(cls := "chat")(
|
||||
chat.lines.filter(_.isVisible).map { line =>
|
||||
div(cls := "line")(
|
||||
userIdLink(line.author.toLowerCase.some, withOnline = false, withTitle = false),
|
||||
" ",
|
||||
richText(line.text)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
div(
|
||||
h2("Simul Chats"),
|
||||
div(cls := "player_chats")(
|
||||
simulChats.map { case (simul, chat) =>
|
||||
div(cls := "game")(
|
||||
a(cls := "title", href := routes.Tournament.show(tournament.id))(tournament.name),
|
||||
a(cls := "title", href := routes.Simul.show(simul.id))(simul.name),
|
||||
div(cls := "chat")(
|
||||
chat.lines.filter(_.isVisible).map { line =>
|
||||
div(cls := "line")(
|
||||
|
@ -38,25 +55,6 @@ object publicChat {
|
|||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
div(
|
||||
h2("Simul Chats"),
|
||||
div(cls := "player_chats")(
|
||||
simulChats.map {
|
||||
case (simul, chat) =>
|
||||
div(cls := "game")(
|
||||
a(cls := "title", href := routes.Simul.show(simul.id))(simul.name),
|
||||
div(cls := "chat")(
|
||||
chat.lines.filter(_.isVisible).map { line =>
|
||||
div(cls := "line")(
|
||||
userIdLink(line.author.toLowerCase.some, withOnline = false, withTitle = false),
|
||||
" ",
|
||||
richText(line.text)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -125,25 +125,24 @@ object search {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
users.map {
|
||||
case lila.user.User.WithEmails(u, emails) =>
|
||||
tr(
|
||||
td(
|
||||
userLink(u, withBestRating = true, params = "?mod"),
|
||||
(isGranted(_.Doxing) && isGranted(_.SetEmail)) option
|
||||
email(emails.list.map(_.value).mkString(", "))
|
||||
),
|
||||
td(u.count.game.localize),
|
||||
td(
|
||||
u.marks.alt option mark("ALT"),
|
||||
u.marks.engine option mark("ENGINE"),
|
||||
u.marks.boost option mark("BOOSTER"),
|
||||
u.marks.troll option mark("SHADOWBAN")
|
||||
),
|
||||
td(u.disabled option mark("CLOSED")),
|
||||
td(momentFromNow(u.createdAt)),
|
||||
td(u.seenAt.map(momentFromNow(_)))
|
||||
)
|
||||
users.map { case lila.user.User.WithEmails(u, emails) =>
|
||||
tr(
|
||||
td(
|
||||
userLink(u, withBestRating = true, params = "?mod"),
|
||||
(isGranted(_.Doxing) && isGranted(_.SetEmail)) option
|
||||
email(emails.list.map(_.value).mkString(", "))
|
||||
),
|
||||
td(u.count.game.localize),
|
||||
td(
|
||||
u.marks.alt option mark("ALT"),
|
||||
u.marks.engine option mark("ENGINE"),
|
||||
u.marks.boost option mark("BOOSTER"),
|
||||
u.marks.troll option mark("SHADOWBAN")
|
||||
),
|
||||
td(u.disabled option mark("CLOSED")),
|
||||
td(momentFromNow(u.createdAt)),
|
||||
td(u.seenAt.map(momentFromNow(_)))
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -100,11 +100,10 @@ object bits {
|
|||
}
|
||||
),
|
||||
div(cls := "now-playing")(
|
||||
playing.partition(_.isMyTurn) pipe {
|
||||
case (myTurn, otherTurn) =>
|
||||
(myTurn ++ otherTurn.take(6 - myTurn.size)) take 9 map {
|
||||
views.html.game.mini(_)
|
||||
}
|
||||
playing.partition(_.isMyTurn) pipe { case (myTurn, otherTurn) =>
|
||||
(myTurn ++ otherTurn.take(6 - myTurn.size)) take 9 map {
|
||||
views.html.game.mini(_)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -41,8 +41,8 @@ private object bits {
|
|||
renderLabel(form("variant"), trans.variant()),
|
||||
renderSelect(
|
||||
form("variant"),
|
||||
variants.filter {
|
||||
case (id, _, _) => ctx.noBlind || lila.game.Game.blindModeVariants.exists(_.id.toString == id)
|
||||
variants.filter { case (id, _, _) =>
|
||||
ctx.noBlind || lila.game.Game.blindModeVariants.exists(_.id.toString == id)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -53,34 +53,32 @@ private object bits {
|
|||
compare: (String, String) => Boolean = (a, b) => a == b
|
||||
) =
|
||||
select(id := s"$prefix${field.id}", name := field.name)(
|
||||
options.map {
|
||||
case (value, name, title) =>
|
||||
option(
|
||||
st.value := value,
|
||||
st.title := title,
|
||||
field.value.exists(v => compare(v, value)) option selected
|
||||
)(name)
|
||||
options.map { case (value, name, title) =>
|
||||
option(
|
||||
st.value := value,
|
||||
st.title := title,
|
||||
field.value.exists(v => compare(v, value)) option selected
|
||||
)(name)
|
||||
}
|
||||
)
|
||||
|
||||
def renderRadios(field: Field, options: Seq[SelectChoice]) =
|
||||
st.group(cls := "radio")(
|
||||
options.map {
|
||||
case (key, name, hint) =>
|
||||
div(
|
||||
input(
|
||||
tpe := "radio",
|
||||
id := s"$prefix${field.id}_$key",
|
||||
st.name := field.name,
|
||||
value := key,
|
||||
field.value.has(key) option checked
|
||||
),
|
||||
label(
|
||||
cls := "required",
|
||||
title := hint,
|
||||
`for` := s"$prefix${field.id}_$key"
|
||||
)(name)
|
||||
)
|
||||
options.map { case (key, name, hint) =>
|
||||
div(
|
||||
input(
|
||||
tpe := "radio",
|
||||
id := s"$prefix${field.id}_$key",
|
||||
st.name := field.name,
|
||||
value := key,
|
||||
field.value.has(key) option checked
|
||||
),
|
||||
label(
|
||||
cls := "required",
|
||||
title := hint,
|
||||
`for` := s"$prefix${field.id}_$key"
|
||||
)(name)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -76,11 +76,10 @@ object filter {
|
|||
options: Seq[(Any, String, Option[String])],
|
||||
checks: Set[String] = Set.empty
|
||||
): Frag =
|
||||
options.zipWithIndex.map {
|
||||
case ((value, text, hint), index) =>
|
||||
div(cls := "checkable")(
|
||||
renderCheckbox(form, key, index, value.toString, raw(text), hint, checks)
|
||||
)
|
||||
options.zipWithIndex.map { case ((value, text, hint), index) =>
|
||||
div(cls := "checkable")(
|
||||
renderCheckbox(form, key, index, value.toString, raw(text), hint, checks)
|
||||
)
|
||||
}
|
||||
|
||||
private def renderCheckbox(
|
||||
|
|
|
@ -68,9 +68,8 @@ object forms {
|
|||
renderRadios(form("level"), lila.setup.AiConfig.levelChoices)
|
||||
),
|
||||
div(cls := "ai_info")(
|
||||
ratings.toList.map {
|
||||
case (level, _) =>
|
||||
div(cls := s"${prefix}level_$level")(trans.aiNameLevelAiLevel("A.I.", level))
|
||||
ratings.toList.map { case (level, _) =>
|
||||
div(cls := s"${prefix}level_$level")(trans.aiNameLevelAiLevel("A.I.", level))
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -138,15 +137,14 @@ object forms {
|
|||
if (ctx.blind) submitButton("Create the game")
|
||||
else
|
||||
div(cls := "color-submits")(
|
||||
translatedSideChoices.map {
|
||||
case (key, name, _) =>
|
||||
submitButton(
|
||||
(typ == "hook") option disabled,
|
||||
title := name,
|
||||
cls := s"color-submits__button button button-metal $key",
|
||||
st.name := "color",
|
||||
value := key
|
||||
)(i)
|
||||
translatedSideChoices.map { case (key, name, _) =>
|
||||
submitButton(
|
||||
(typ == "hook") option disabled,
|
||||
title := name,
|
||||
cls := s"color-submits__button button button-metal $key",
|
||||
st.name := "color",
|
||||
value := key
|
||||
)(i)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -158,30 +158,29 @@ object contact {
|
|||
"trolling" -> trolling(),
|
||||
"insults" -> insults(),
|
||||
"some other reason" -> otherReason()
|
||||
).map {
|
||||
case (reason, name) =>
|
||||
Leaf(
|
||||
reason,
|
||||
frag("Report a player for ", name),
|
||||
frag(
|
||||
p(
|
||||
a(href := routes.Report.form())(toReportAPlayer(name)),
|
||||
"."
|
||||
),
|
||||
p(
|
||||
youCanAlsoReachReportPage(button(cls := "thin button button-empty", dataIcon := "!"))
|
||||
),
|
||||
p(
|
||||
doNotMessageModerators(),
|
||||
br,
|
||||
doNotReportInForum(),
|
||||
br,
|
||||
doNotSendReportEmails(),
|
||||
br,
|
||||
onlyReports()
|
||||
)
|
||||
).map { case (reason, name) =>
|
||||
Leaf(
|
||||
reason,
|
||||
frag("Report a player for ", name),
|
||||
frag(
|
||||
p(
|
||||
a(href := routes.Report.form())(toReportAPlayer(name)),
|
||||
"."
|
||||
),
|
||||
p(
|
||||
youCanAlsoReachReportPage(button(cls := "thin button button-empty", dataIcon := "!"))
|
||||
),
|
||||
p(
|
||||
doNotMessageModerators(),
|
||||
br,
|
||||
doNotReportInForum(),
|
||||
br,
|
||||
doNotSendReportEmails(),
|
||||
br,
|
||||
onlyReports()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
Branch(
|
||||
|
|
|
@ -89,40 +89,39 @@ object edit extends Context.ToLang {
|
|||
)
|
||||
)
|
||||
),
|
||||
modData.map {
|
||||
case (log, notes) =>
|
||||
div(cls := "mod_log status")(
|
||||
strong(cls := "text", dataIcon := "!")(
|
||||
"Moderation history",
|
||||
log.isEmpty option ": nothing to show."
|
||||
),
|
||||
log.nonEmpty option ul(
|
||||
log.map { e =>
|
||||
li(
|
||||
userIdLink(e.mod.some, withTitle = false),
|
||||
" ",
|
||||
b(e.showAction),
|
||||
" ",
|
||||
e.details,
|
||||
" ",
|
||||
momentFromNow(e.date)
|
||||
)
|
||||
}
|
||||
),
|
||||
br,
|
||||
strong(cls := "text", dataIcon := "!")(
|
||||
"Moderator notes",
|
||||
notes.isEmpty option ": nothing to show."
|
||||
),
|
||||
notes.nonEmpty option ul(
|
||||
notes.map { note =>
|
||||
li(
|
||||
p(cls := "meta")(userIdLink(note.from.some), " ", momentFromNow(note.date)),
|
||||
p(cls := "text")(richText(note.text))
|
||||
)
|
||||
}
|
||||
)
|
||||
modData.map { case (log, notes) =>
|
||||
div(cls := "mod_log status")(
|
||||
strong(cls := "text", dataIcon := "!")(
|
||||
"Moderation history",
|
||||
log.isEmpty option ": nothing to show."
|
||||
),
|
||||
log.nonEmpty option ul(
|
||||
log.map { e =>
|
||||
li(
|
||||
userIdLink(e.mod.some, withTitle = false),
|
||||
" ",
|
||||
b(e.showAction),
|
||||
" ",
|
||||
e.details,
|
||||
" ",
|
||||
momentFromNow(e.date)
|
||||
)
|
||||
}
|
||||
),
|
||||
br,
|
||||
strong(cls := "text", dataIcon := "!")(
|
||||
"Moderator notes",
|
||||
notes.isEmpty option ": nothing to show."
|
||||
),
|
||||
notes.nonEmpty option ul(
|
||||
notes.map { note =>
|
||||
li(
|
||||
p(cls := "meta")(userIdLink(note.from.some), " ", momentFromNow(note.date)),
|
||||
p(cls := "text")(richText(note.text))
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
postForm(
|
||||
cls := "form3",
|
||||
|
|
|
@ -73,25 +73,24 @@ object timeline {
|
|||
a(href := routes.Simul.show(simulId))(simulName)
|
||||
)
|
||||
case GameEnd(playerId, opponent, win, perfKey) =>
|
||||
lila.rating.PerfType(perfKey) map {
|
||||
perf =>
|
||||
(win match {
|
||||
case Some(true) => trans.victoryVsYInZ
|
||||
case Some(false) => trans.defeatVsYInZ
|
||||
case None => trans.drawVsYInZ
|
||||
})(
|
||||
a(
|
||||
href := routes.Round.player(playerId),
|
||||
dataIcon := perf.iconChar,
|
||||
cls := "text glpt"
|
||||
)(win match {
|
||||
case Some(true) => trans.victory()
|
||||
case Some(false) => trans.defeat()
|
||||
case None => trans.draw()
|
||||
}),
|
||||
userIdLink(opponent, withOnline = false),
|
||||
perf.trans
|
||||
)
|
||||
lila.rating.PerfType(perfKey) map { perf =>
|
||||
(win match {
|
||||
case Some(true) => trans.victoryVsYInZ
|
||||
case Some(false) => trans.defeatVsYInZ
|
||||
case None => trans.drawVsYInZ
|
||||
})(
|
||||
a(
|
||||
href := routes.Round.player(playerId),
|
||||
dataIcon := perf.iconChar,
|
||||
cls := "text glpt"
|
||||
)(win match {
|
||||
case Some(true) => trans.victory()
|
||||
case Some(false) => trans.defeat()
|
||||
case None => trans.draw()
|
||||
}),
|
||||
userIdLink(opponent, withOnline = false),
|
||||
perf.trans
|
||||
)
|
||||
}
|
||||
case StudyCreate(userId, studyId, studyName) =>
|
||||
trans.xCreatesStudyY(
|
||||
|
|
|
@ -22,22 +22,21 @@ object shields {
|
|||
div(cls := "page-menu__content box box-pad")(
|
||||
h1("Tournament shields"),
|
||||
div(cls := "tournament-shields")(
|
||||
history.sorted.map {
|
||||
case (categ, awards) =>
|
||||
section(
|
||||
h2(
|
||||
a(href := routes.Tournament.categShields(categ.key))(
|
||||
span(cls := "shield-trophy")(categ.iconChar.toString),
|
||||
categ.name
|
||||
)
|
||||
),
|
||||
ol(awards.map { aw =>
|
||||
li(
|
||||
userIdLink(aw.owner.value.some),
|
||||
a(href := routes.Tournament.show(aw.tourId))(showDate(aw.date))
|
||||
)
|
||||
})
|
||||
)
|
||||
history.sorted.map { case (categ, awards) =>
|
||||
section(
|
||||
h2(
|
||||
a(href := routes.Tournament.categShields(categ.key))(
|
||||
span(cls := "shield-trophy")(categ.iconChar.toString),
|
||||
categ.name
|
||||
)
|
||||
),
|
||||
ol(awards.map { aw =>
|
||||
li(
|
||||
userIdLink(aw.owner.value.some),
|
||||
a(href := routes.Tournament.show(aw.tourId))(showDate(aw.date))
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -505,57 +505,56 @@ object mod {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
othersWithEmail.others.map {
|
||||
case other @ UserSpy.OtherUser(o, _, _) =>
|
||||
val dox = isGranted(_.Doxing) || (o.lameOrAlt && !o.hasTitle)
|
||||
val userNotes =
|
||||
notes.filter(n => n.to == o.id && (ctx.me.exists(n.isFrom) || isGranted(_.Doxing)))
|
||||
tr(
|
||||
dataTags := s"${other.ips.mkString(" ")} ${other.fps.mkString(" ")}",
|
||||
cls := (o == u) option "same"
|
||||
othersWithEmail.others.map { case other @ UserSpy.OtherUser(o, _, _) =>
|
||||
val dox = isGranted(_.Doxing) || (o.lameOrAlt && !o.hasTitle)
|
||||
val userNotes =
|
||||
notes.filter(n => n.to == o.id && (ctx.me.exists(n.isFrom) || isGranted(_.Doxing)))
|
||||
tr(
|
||||
dataTags := s"${other.ips.mkString(" ")} ${other.fps.mkString(" ")}",
|
||||
cls := (o == u) option "same"
|
||||
)(
|
||||
if (dox || o == u) td(dataSort := o.id)(userLink(o, withBestRating = true, params = "?mod"))
|
||||
else td,
|
||||
if (dox) td(othersWithEmail emailValueOf o)
|
||||
else td,
|
||||
td(
|
||||
// show prints and ips separately
|
||||
dataSort := other.score + (other.ips.nonEmpty ?? 1000000) + (other.fps.nonEmpty ?? 3000000)
|
||||
)(
|
||||
if (dox || o == u) td(dataSort := o.id)(userLink(o, withBestRating = true, params = "?mod"))
|
||||
else td,
|
||||
if (dox) td(othersWithEmail emailValueOf o)
|
||||
else td,
|
||||
td(
|
||||
// show prints and ips separately
|
||||
dataSort := other.score + (other.ips.nonEmpty ?? 1000000) + (other.fps.nonEmpty ?? 3000000)
|
||||
)(
|
||||
List(other.ips.size -> "IP", other.fps.size -> "Print")
|
||||
.collect {
|
||||
case (nb, name) if nb > 0 => s"$nb $name"
|
||||
}
|
||||
.mkString(", ")
|
||||
),
|
||||
td(dataSort := o.count.game)(o.count.game.localize),
|
||||
markTd(~bans.get(o.id), playban(cls := "text")(~bans.get(o.id))),
|
||||
markTd(o.marks.alt ?? 1, alt),
|
||||
markTd(o.marks.troll ?? 1, shadowban),
|
||||
markTd(o.marks.boost ?? 1, boosting),
|
||||
markTd(o.marks.engine ?? 1, engine),
|
||||
markTd(o.disabled ?? 1, closed),
|
||||
markTd(o.marks.reportban ?? 1, reportban),
|
||||
userNotes.nonEmpty option {
|
||||
td(dataSort := userNotes.size)(
|
||||
a(href := s"${routes.User.show(o.username)}?notes")(
|
||||
notesText(
|
||||
title := s"Notes from ${userNotes.map(_.from).map(usernameOrId).mkString(", ")}",
|
||||
cls := "is-green"
|
||||
),
|
||||
userNotes.size
|
||||
)
|
||||
List(other.ips.size -> "IP", other.fps.size -> "Print")
|
||||
.collect {
|
||||
case (nb, name) if nb > 0 => s"$nb $name"
|
||||
}
|
||||
.mkString(", ")
|
||||
),
|
||||
td(dataSort := o.count.game)(o.count.game.localize),
|
||||
markTd(~bans.get(o.id), playban(cls := "text")(~bans.get(o.id))),
|
||||
markTd(o.marks.alt ?? 1, alt),
|
||||
markTd(o.marks.troll ?? 1, shadowban),
|
||||
markTd(o.marks.boost ?? 1, boosting),
|
||||
markTd(o.marks.engine ?? 1, engine),
|
||||
markTd(o.disabled ?? 1, closed),
|
||||
markTd(o.marks.reportban ?? 1, reportban),
|
||||
userNotes.nonEmpty option {
|
||||
td(dataSort := userNotes.size)(
|
||||
a(href := s"${routes.User.show(o.username)}?notes")(
|
||||
notesText(
|
||||
title := s"Notes from ${userNotes.map(_.from).map(usernameOrId).mkString(", ")}",
|
||||
cls := "is-green"
|
||||
),
|
||||
userNotes.size
|
||||
)
|
||||
} getOrElse td(dataSort := 0),
|
||||
td(dataSort := o.createdAt.getMillis)(momentFromNowServer(o.createdAt)),
|
||||
td(dataSort := o.seenAt.map(_.getMillis.toString))(o.seenAt.map(momentFromNowServer)),
|
||||
isGranted(_.CloseAccount) option td(
|
||||
o.enabled option button(
|
||||
cls := "button button-empty button-thin button-red mark-alt",
|
||||
href := routes.Mod.alt(o.id, !o.marks.alt)
|
||||
)("ALT")
|
||||
)
|
||||
} getOrElse td(dataSort := 0),
|
||||
td(dataSort := o.createdAt.getMillis)(momentFromNowServer(o.createdAt)),
|
||||
td(dataSort := o.seenAt.map(_.getMillis.toString))(o.seenAt.map(momentFromNowServer)),
|
||||
isGranted(_.CloseAccount) option td(
|
||||
o.enabled option button(
|
||||
cls := "button button-empty button-thin button-red mark-alt",
|
||||
href := routes.Mod.alt(o.id, !o.marks.alt)
|
||||
)("ALT")
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
),
|
||||
|
|
|
@ -29,14 +29,13 @@ object top {
|
|||
h1(a(href := routes.User.list(), dataIcon := "I"), title),
|
||||
table(cls := "slist slist-pad")(
|
||||
tbody(
|
||||
users.zipWithIndex.map {
|
||||
case (u, i) =>
|
||||
tr(
|
||||
td(i + 1),
|
||||
td(lightUserLink(u.user)),
|
||||
td(u.rating),
|
||||
td(ratingProgress(u.progress))
|
||||
)
|
||||
users.zipWithIndex.map { case (u, i) =>
|
||||
tr(
|
||||
td(i + 1),
|
||||
td(lightUserLink(u.user)),
|
||||
td(u.rating),
|
||||
td(ratingProgress(u.progress))
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -38,15 +38,14 @@ object chart {
|
|||
)
|
||||
),
|
||||
tbody(
|
||||
data.perfResults.map {
|
||||
case (pt, res) =>
|
||||
tr(
|
||||
th(iconTag(pt.iconChar, pt.trans)),
|
||||
td(res.nb.localize),
|
||||
td(res.points.median.map(_.toInt)),
|
||||
td(res.points.sum.localize),
|
||||
td(res.rankPercentMedian, "%")
|
||||
)
|
||||
data.perfResults.map { case (pt, res) =>
|
||||
tr(
|
||||
th(iconTag(pt.iconChar, pt.trans)),
|
||||
td(res.nb.localize),
|
||||
td(res.points.median.map(_.toInt)),
|
||||
td(res.points.sum.localize),
|
||||
td(res.rankPercentMedian, "%")
|
||||
)
|
||||
},
|
||||
tr(
|
||||
th("Total"),
|
||||
|
|
|
@ -53,11 +53,11 @@ final class ActivityReadApi(
|
|||
.mon(_.user segment "activity.posts") dmap some
|
||||
}
|
||||
practice = (for {
|
||||
p <- a.practice
|
||||
struct <- practiceStructure
|
||||
} yield p.value flatMap {
|
||||
case (studyId, nb) => struct study studyId map (_ -> nb)
|
||||
} toMap)
|
||||
p <- a.practice
|
||||
struct <- practiceStructure
|
||||
} yield p.value flatMap { case (studyId, nb) =>
|
||||
struct study studyId map (_ -> nb)
|
||||
} toMap)
|
||||
postView = posts.map { p =>
|
||||
p.groupBy(_.topic)
|
||||
.view
|
||||
|
|
|
@ -22,12 +22,11 @@ private object BSONHandlers {
|
|||
implicit lazy val activityIdHandler = {
|
||||
val sep = ':'
|
||||
tryHandler[Id](
|
||||
{
|
||||
case BSONString(v) =>
|
||||
v split sep match {
|
||||
case Array(userId, dayStr) => Success(Id(userId, Day(Integer.parseInt(dayStr))))
|
||||
case _ => handlerBadValue(s"Invalid activity id $v")
|
||||
}
|
||||
{ case BSONString(v) =>
|
||||
v split sep match {
|
||||
case Array(userId, dayStr) => Success(Id(userId, Day(Integer.parseInt(dayStr))))
|
||||
case _ => handlerBadValue(s"Invalid activity id $v")
|
||||
}
|
||||
},
|
||||
id => BSONString(s"${id.userId}$sep${id.day.value}")
|
||||
)
|
||||
|
@ -35,12 +34,11 @@ private object BSONHandlers {
|
|||
|
||||
implicit private lazy val ratingHandler = BSONIntegerHandler.as[Rating](Rating.apply, _.value)
|
||||
implicit private lazy val ratingProgHandler = tryHandler[RatingProg](
|
||||
{
|
||||
case v: BSONArray =>
|
||||
for {
|
||||
before <- v.getAsTry[Rating](0)
|
||||
after <- v.getAsTry[Rating](1)
|
||||
} yield RatingProg(before, after)
|
||||
{ case v: BSONArray =>
|
||||
for {
|
||||
before <- v.getAsTry[Rating](0)
|
||||
after <- v.getAsTry[Rating](1)
|
||||
} yield RatingProg(before, after)
|
||||
},
|
||||
o => BSONArray(o.before, o.after)
|
||||
)
|
||||
|
|
|
@ -32,8 +32,8 @@ final class JsonView(
|
|||
implicit val scoreWrites = Json.writes[Score]
|
||||
implicit val gamesWrites = OWrites[Games] { games =>
|
||||
JsObject {
|
||||
games.value.toList.sortBy(-_._2.size).map {
|
||||
case (pt, score) => pt.key -> scoreWrites.writes(score)
|
||||
games.value.toList.sortBy(-_._2.size).map { case (pt, score) =>
|
||||
pt.key -> scoreWrites.writes(score)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,43 +104,41 @@ final class JsonView(
|
|||
.add("tournaments", a.tours)
|
||||
.add(
|
||||
"practice",
|
||||
a.practice.map(_.toList.sortBy(-_._2) map {
|
||||
case (study, nb) =>
|
||||
Json.obj(
|
||||
"url" -> s"/practice/-/${study.slug}/${study.id}",
|
||||
"name" -> study.name,
|
||||
"nbPositions" -> nb
|
||||
)
|
||||
a.practice.map(_.toList.sortBy(-_._2) map { case (study, nb) =>
|
||||
Json.obj(
|
||||
"url" -> s"/practice/-/${study.slug}/${study.id}",
|
||||
"name" -> study.name,
|
||||
"nbPositions" -> nb
|
||||
)
|
||||
})
|
||||
)
|
||||
.add("simuls", a.simuls.map(_ map simulWrites(user).writes))
|
||||
.add(
|
||||
"correspondenceMoves",
|
||||
a.corresMoves.map {
|
||||
case (nb, povs) => Json.obj("nb" -> nb, "games" -> povs)
|
||||
a.corresMoves.map { case (nb, povs) =>
|
||||
Json.obj("nb" -> nb, "games" -> povs)
|
||||
}
|
||||
)
|
||||
.add(
|
||||
"correspondenceEnds",
|
||||
a.corresEnds.map {
|
||||
case (score, povs) => Json.obj("score" -> score, "games" -> povs)
|
||||
a.corresEnds.map { case (score, povs) =>
|
||||
Json.obj("score" -> score, "games" -> povs)
|
||||
}
|
||||
)
|
||||
.add("follows" -> a.follows)
|
||||
.add("studies" -> a.studies)
|
||||
.add("teams" -> a.teams)
|
||||
.add("posts" -> a.posts.map(_ map {
|
||||
case (topic, posts) =>
|
||||
Json.obj(
|
||||
"topicUrl" -> s"/forum/${topic.categId}/${topic.slug}",
|
||||
"topicName" -> topic.name,
|
||||
"posts" -> posts.map { p =>
|
||||
Json.obj(
|
||||
"url" -> s"/forum/redirect/post/${p.id}",
|
||||
"text" -> p.text.take(500)
|
||||
)
|
||||
}
|
||||
)
|
||||
.add("posts" -> a.posts.map(_ map { case (topic, posts) =>
|
||||
Json.obj(
|
||||
"topicUrl" -> s"/forum/${topic.categId}/${topic.slug}",
|
||||
"topicName" -> topic.name,
|
||||
"posts" -> posts.map { p =>
|
||||
Json.obj(
|
||||
"url" -> s"/forum/redirect/post/${p.id}",
|
||||
"text" -> p.text.take(500)
|
||||
)
|
||||
}
|
||||
)
|
||||
}))
|
||||
.add("patron" -> a.patron)
|
||||
.add("stream" -> a.stream)
|
||||
|
|
|
@ -23,12 +23,12 @@ sealed trait Advice {
|
|||
case MateAdvice(seq, _, _, _) => seq.desc
|
||||
case CpAdvice(judgment, _, _) => judgment.toString
|
||||
}) + "." + {
|
||||
withBestMove ?? {
|
||||
info.variation.headOption ?? { move =>
|
||||
s" $move was best."
|
||||
withBestMove ?? {
|
||||
info.variation.headOption ?? { move =>
|
||||
s" $move was best."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def evalComment: Option[String] = {
|
||||
List(prev.evalComment, info.evalComment).flatten mkString " → "
|
||||
|
|
|
@ -25,17 +25,17 @@ final class Analyser(
|
|||
gameRepo.setAnalysed(game.id)
|
||||
analysisRepo.save(analysis) >>
|
||||
sendAnalysisProgress(analysis, complete = true) >>- {
|
||||
Bus.publish(actorApi.AnalysisReady(game, analysis), "analysisReady")
|
||||
Bus.publish(InsertGame(game), "gameSearchInsert")
|
||||
requesterApi save analysis
|
||||
}
|
||||
Bus.publish(actorApi.AnalysisReady(game, analysis), "analysisReady")
|
||||
Bus.publish(InsertGame(game), "gameSearchInsert")
|
||||
requesterApi save analysis
|
||||
}
|
||||
}
|
||||
}
|
||||
case Some(_) =>
|
||||
analysisRepo.save(analysis) >>
|
||||
sendAnalysisProgress(analysis, complete = true) >>- {
|
||||
requesterApi save analysis
|
||||
}
|
||||
requesterApi save analysis
|
||||
}
|
||||
}
|
||||
|
||||
def progress(analysis: Analysis): Funit = sendAnalysisProgress(analysis, complete = false)
|
||||
|
@ -44,20 +44,19 @@ final class Analyser(
|
|||
analysis.studyId match {
|
||||
case None =>
|
||||
gameRepo gameWithInitialFen analysis.id map {
|
||||
_ ?? {
|
||||
case (game, initialFen) =>
|
||||
Bus.publish(
|
||||
TellIfExists(
|
||||
analysis.id,
|
||||
actorApi.AnalysisProgress(
|
||||
analysis = analysis,
|
||||
game = game,
|
||||
variant = game.variant,
|
||||
initialFen = initialFen | FEN(game.variant.initialFen)
|
||||
)
|
||||
),
|
||||
"roundSocket"
|
||||
)
|
||||
_ ?? { case (game, initialFen) =>
|
||||
Bus.publish(
|
||||
TellIfExists(
|
||||
analysis.id,
|
||||
actorApi.AnalysisProgress(
|
||||
analysis = analysis,
|
||||
game = game,
|
||||
variant = game.variant,
|
||||
initialFen = initialFen | FEN(game.variant.initialFen)
|
||||
)
|
||||
),
|
||||
"roundSocket"
|
||||
)
|
||||
}
|
||||
}
|
||||
case Some(_) =>
|
||||
|
|
|
@ -21,11 +21,10 @@ case class Analysis(
|
|||
def providedByLichess = by exists (_ startsWith "lichess-")
|
||||
|
||||
lazy val infoAdvices: InfoAdvices = {
|
||||
(Info.start(startPly) :: infos) sliding 2 collect {
|
||||
case List(prev, info) =>
|
||||
info -> {
|
||||
info.hasVariation ?? Advice(prev, info)
|
||||
}
|
||||
(Info.start(startPly) :: infos) sliding 2 collect { case List(prev, info) =>
|
||||
info -> {
|
||||
info.hasVariation ?? Advice(prev, info)
|
||||
}
|
||||
}
|
||||
}.toList
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ final class AnalysisRepo(coll: Coll)(implicit ec: scala.concurrent.ExecutionCont
|
|||
|
||||
def associateToGames(games: List[Game]): Fu[List[Analysis.Analyzed]] =
|
||||
byIds(games.map(_.id)) map { as =>
|
||||
games zip as collect {
|
||||
case (game, Some(analysis)) => Analysis.Analyzed(game, analysis)
|
||||
games zip as collect { case (game, Some(analysis)) =>
|
||||
Analysis.Analyzed(game, analysis)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,21 +33,20 @@ final class Annotator(netDomain: lila.common.config.NetDomain) {
|
|||
}
|
||||
|
||||
private def annotateTurns(p: Pgn, advices: List[Advice]): Pgn =
|
||||
advices.foldLeft(p) {
|
||||
case (pgn, advice) =>
|
||||
pgn.updateTurn(
|
||||
advice.turn,
|
||||
turn =>
|
||||
turn.update(
|
||||
advice.color,
|
||||
move =>
|
||||
move.copy(
|
||||
glyphs = Glyphs.fromList(advice.judgment.glyph :: Nil),
|
||||
comments = advice.makeComment(withEval = true, withBestMove = true) :: move.comments,
|
||||
variations = makeVariation(turn, advice) :: Nil
|
||||
)
|
||||
)
|
||||
)
|
||||
advices.foldLeft(p) { case (pgn, advice) =>
|
||||
pgn.updateTurn(
|
||||
advice.turn,
|
||||
turn =>
|
||||
turn.update(
|
||||
advice.color,
|
||||
move =>
|
||||
move.copy(
|
||||
glyphs = Glyphs.fromList(advice.judgment.glyph :: Nil),
|
||||
comments = advice.makeComment(withEval = true, withBestMove = true) :: move.comments,
|
||||
variations = makeVariation(turn, advice) :: Nil
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def makeVariation(turn: Turn, advice: Advice): List[Turn] =
|
||||
|
|
|
@ -80,8 +80,8 @@ object Info {
|
|||
}
|
||||
|
||||
def decodeList(str: String, fromPly: Int): Option[List[Info]] = {
|
||||
str.split(listSeparator).toList.zipWithIndex map {
|
||||
case (infoStr, index) => decode(index + 1 + fromPly, infoStr)
|
||||
str.split(listSeparator).toList.zipWithIndex map { case (infoStr, index) =>
|
||||
decode(index + 1 + fromPly, infoStr)
|
||||
}
|
||||
}.sequence
|
||||
|
||||
|
|
|
@ -8,29 +8,28 @@ import lila.tree.Eval.JsonHandlers._
|
|||
object JsonView {
|
||||
|
||||
def moves(analysis: Analysis, withGlyph: Boolean = true) =
|
||||
JsArray(analysis.infoAdvices map {
|
||||
case (info, adviceOption) =>
|
||||
Json
|
||||
.obj()
|
||||
.add("eval" -> info.cp)
|
||||
.add("mate" -> info.mate)
|
||||
.add("best" -> info.best.map(_.uci))
|
||||
.add("variation" -> info.variation.nonEmpty.option(info.variation mkString " "))
|
||||
.add("judgment" -> adviceOption.map { a =>
|
||||
Json
|
||||
.obj(
|
||||
"name" -> a.judgment.name,
|
||||
"comment" -> a.makeComment(withEval = false, withBestMove = true)
|
||||
)
|
||||
.add(
|
||||
"glyph" -> withGlyph.option(
|
||||
Json.obj(
|
||||
"name" -> a.judgment.glyph.name,
|
||||
"symbol" -> a.judgment.glyph.symbol
|
||||
)
|
||||
JsArray(analysis.infoAdvices map { case (info, adviceOption) =>
|
||||
Json
|
||||
.obj()
|
||||
.add("eval" -> info.cp)
|
||||
.add("mate" -> info.mate)
|
||||
.add("best" -> info.best.map(_.uci))
|
||||
.add("variation" -> info.variation.nonEmpty.option(info.variation mkString " "))
|
||||
.add("judgment" -> adviceOption.map { a =>
|
||||
Json
|
||||
.obj(
|
||||
"name" -> a.judgment.name,
|
||||
"comment" -> a.makeComment(withEval = false, withBestMove = true)
|
||||
)
|
||||
.add(
|
||||
"glyph" -> withGlyph.option(
|
||||
Json.obj(
|
||||
"name" -> a.judgment.glyph.name,
|
||||
"symbol" -> a.judgment.glyph.symbol
|
||||
)
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
import Accuracy.povToPovLike
|
||||
|
@ -40,8 +39,8 @@ object JsonView {
|
|||
.find(_._1 == pov.color)
|
||||
.map(_._2)
|
||||
.map(s =>
|
||||
JsObject(s map {
|
||||
case (nag, nb) => nag.toString.toLowerCase -> JsNumber(nb)
|
||||
JsObject(s map { case (nag, nb) =>
|
||||
nag.toString.toLowerCase -> JsNumber(nb)
|
||||
}).add("acpl" -> lila.analyse.Accuracy.mean(pov, analysis))
|
||||
)
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ final private[api] class Cli(
|
|||
|
||||
private def run(args: List[String]): Fu[String] = {
|
||||
(processors lift args) | fufail("Unknown command: " + args.mkString(" "))
|
||||
} recover {
|
||||
case e: Exception => "ERROR " + e
|
||||
} recover { case e: Exception =>
|
||||
"ERROR " + e
|
||||
}
|
||||
|
||||
private def processors =
|
||||
|
|
|
@ -90,8 +90,8 @@ final class Env(
|
|||
|
||||
private lazy val linkCheck = wire[LinkCheck]
|
||||
|
||||
Bus.subscribeFun("chatLinkCheck") {
|
||||
case GetLinkCheck(line, source, promise) => promise completeWith linkCheck(line, source)
|
||||
Bus.subscribeFun("chatLinkCheck") { case GetLinkCheck(line, source, promise) =>
|
||||
promise completeWith linkCheck(line, source)
|
||||
}
|
||||
|
||||
system.scheduler.scheduleWithFixedDelay(1 minute, 1 minute) { () =>
|
||||
|
|
|
@ -183,9 +183,8 @@ final private[api] class GameApi(
|
|||
else fuccess(List.fill(games.size)(none[Analysis]))
|
||||
allAnalysis flatMap { analysisOptions =>
|
||||
(games map gameRepo.initialFen).sequenceFu map { initialFens =>
|
||||
games zip analysisOptions zip initialFens map {
|
||||
case ((g, analysisOption), initialFen) =>
|
||||
gameToJson(g, analysisOption, initialFen, checkToken(withFlags))
|
||||
games zip analysisOptions zip initialFens map { case ((g, analysisOption), initialFen) =>
|
||||
gameToJson(g, analysisOption, initialFen, checkToken(withFlags))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,18 +68,17 @@ final class GameApiV2(
|
|||
|
||||
private val fileR = """[\s,]""".r
|
||||
def filename(game: Game, format: Format): Fu[String] =
|
||||
gameLightUsers(game) map {
|
||||
case List(wu, bu) =>
|
||||
fileR.replaceAllIn(
|
||||
"lichess_pgn_%s_%s_vs_%s.%s.%s".format(
|
||||
Tag.UTCDate.format.print(game.createdAt),
|
||||
pgnDump.dumper.player(game.whitePlayer, wu),
|
||||
pgnDump.dumper.player(game.blackPlayer, bu),
|
||||
game.id,
|
||||
format.toString.toLowerCase
|
||||
),
|
||||
"_"
|
||||
)
|
||||
gameLightUsers(game) map { case List(wu, bu) =>
|
||||
fileR.replaceAllIn(
|
||||
"lichess_pgn_%s_%s_vs_%s.%s.%s".format(
|
||||
Tag.UTCDate.format.print(game.createdAt),
|
||||
pgnDump.dumper.player(game.whitePlayer, wu),
|
||||
pgnDump.dumper.player(game.blackPlayer, bu),
|
||||
game.id,
|
||||
format.toString.toLowerCase
|
||||
),
|
||||
"_"
|
||||
)
|
||||
}
|
||||
def filename(tour: Tournament, format: Format): String =
|
||||
fileR.replaceAllIn(
|
||||
|
@ -154,44 +153,42 @@ final class GameApiV2(
|
|||
playerRepo.teamsOfPlayers(config.tournamentId, pairings.flatMap(_.users).distinct).dmap(_.toMap)
|
||||
} flatMap { playerTeams =>
|
||||
gameRepo.gameOptionsFromSecondary(pairings.map(_.gameId)) map {
|
||||
_.zip(pairings) collect {
|
||||
case (Some(game), pairing) =>
|
||||
import cats.implicits._
|
||||
_.zip(pairings) collect { case (Some(game), pairing) =>
|
||||
import cats.implicits._
|
||||
(
|
||||
game,
|
||||
pairing,
|
||||
(
|
||||
game,
|
||||
pairing,
|
||||
(
|
||||
playerTeams.get(pairing.user1),
|
||||
playerTeams.get(
|
||||
pairing.user2
|
||||
)
|
||||
) mapN chess.Color.Map.apply[String]
|
||||
)
|
||||
playerTeams.get(pairing.user1),
|
||||
playerTeams.get(
|
||||
pairing.user2
|
||||
)
|
||||
) mapN chess.Color.Map.apply[String]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.mapConcat(identity)
|
||||
.mapAsync(4) {
|
||||
case (game, pairing, teams) => enrich(config.flags)(game) dmap { (_, pairing, teams) }
|
||||
.mapAsync(4) { case (game, pairing, teams) =>
|
||||
enrich(config.flags)(game) dmap { (_, pairing, teams) }
|
||||
}
|
||||
.mapAsync(4) {
|
||||
case ((game, fen, analysis), pairing, teams) =>
|
||||
config.format match {
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis, teams, none)
|
||||
case Format.JSON =>
|
||||
def addBerserk(color: chess.Color)(json: JsObject) =
|
||||
if (pairing berserkOf color)
|
||||
json deepMerge Json.obj(
|
||||
"players" -> Json.obj(color.name -> Json.obj("berserk" -> true))
|
||||
)
|
||||
else json
|
||||
toJson(game, fen, analysis, config.flags, teams) dmap
|
||||
addBerserk(chess.White) dmap
|
||||
addBerserk(chess.Black) dmap { json =>
|
||||
.mapAsync(4) { case ((game, fen, analysis), pairing, teams) =>
|
||||
config.format match {
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis, teams, none)
|
||||
case Format.JSON =>
|
||||
def addBerserk(color: chess.Color)(json: JsObject) =
|
||||
if (pairing berserkOf color)
|
||||
json deepMerge Json.obj(
|
||||
"players" -> Json.obj(color.name -> Json.obj("berserk" -> true))
|
||||
)
|
||||
else json
|
||||
toJson(game, fen, analysis, config.flags, teams) dmap
|
||||
addBerserk(chess.White) dmap
|
||||
addBerserk(chess.Black) dmap { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,22 +204,21 @@ final class GameApiV2(
|
|||
.mapAsync(1)(gameRepo.gamesFromSecondary)
|
||||
.mapConcat(identity)
|
||||
.mapAsync(4)(enrich(config.flags))
|
||||
.mapAsync(4) {
|
||||
case (game, fen, analysis) =>
|
||||
config.format match {
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis, none, none)
|
||||
case Format.JSON =>
|
||||
toJson(game, fen, analysis, config.flags, None) dmap { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
}
|
||||
}
|
||||
.mapAsync(4) { case (game, fen, analysis) =>
|
||||
config.format match {
|
||||
case Format.PGN => pgnDump.formatter(config.flags)(game, fen, analysis, none, none)
|
||||
case Format.JSON =>
|
||||
toJson(game, fen, analysis, config.flags, None) dmap { json =>
|
||||
s"${Json.stringify(json)}\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def preparationFlow(config: Config, realPlayers: Option[RealPlayers]) =
|
||||
Flow[Game]
|
||||
.mapAsync(4)(enrich(config.flags))
|
||||
.mapAsync(4) {
|
||||
case (game, fen, analysis) => formatterFor(config)(game, fen, analysis, None, realPlayers)
|
||||
.mapAsync(4) { case (game, fen, analysis) =>
|
||||
formatterFor(config)(game, fen, analysis, None, realPlayers)
|
||||
}
|
||||
|
||||
private def enrich(flags: WithFlags)(game: Game) =
|
||||
|
@ -280,19 +276,18 @@ final class GameApiV2(
|
|||
"createdAt" -> g.createdAt,
|
||||
"lastMoveAt" -> g.movedAt,
|
||||
"status" -> g.status.name,
|
||||
"players" -> JsObject(g.players zip lightUsers map {
|
||||
case (p, user) =>
|
||||
p.color.name -> Json
|
||||
.obj()
|
||||
.add("user", user)
|
||||
.add("rating", p.rating)
|
||||
.add("ratingDiff", p.ratingDiff)
|
||||
.add("name", p.name)
|
||||
.add("provisional" -> p.provisional)
|
||||
.add("aiLevel" -> p.aiLevel)
|
||||
.add("analysis" -> analysisOption.flatMap(analysisJson.player(g pov p.color)))
|
||||
.add("team" -> teams.map(_(p.color)))
|
||||
// .add("moveCentis" -> withFlags.moveTimes ?? g.moveTimes(p.color).map(_.map(_.centis)))
|
||||
"players" -> JsObject(g.players zip lightUsers map { case (p, user) =>
|
||||
p.color.name -> Json
|
||||
.obj()
|
||||
.add("user", user)
|
||||
.add("rating", p.rating)
|
||||
.add("ratingDiff", p.ratingDiff)
|
||||
.add("name", p.name)
|
||||
.add("provisional" -> p.provisional)
|
||||
.add("aiLevel" -> p.aiLevel)
|
||||
.add("analysis" -> analysisOption.flatMap(analysisJson.player(g pov p.color)))
|
||||
.add("team" -> teams.map(_(p.color)))
|
||||
// .add("moveCentis" -> withFlags.moveTimes ?? g.moveTimes(p.color).map(_.map(_.centis)))
|
||||
})
|
||||
)
|
||||
.add("initialFen" -> initialFen.map(_.value))
|
||||
|
|
|
@ -13,8 +13,7 @@ final class LobbyApi(
|
|||
|
||||
def apply(implicit ctx: Context): Fu[(JsObject, List[Pov])] =
|
||||
ctx.me.fold(seekApi.forAnon)(seekApi.forUser).mon(_.lobby segment "seeks") zip
|
||||
(ctx.me ?? gameProxyRepo.urgentGames).mon(_.lobby segment "urgentGames") flatMap {
|
||||
case (seeks, povs) =>
|
||||
(ctx.me ?? gameProxyRepo.urgentGames).mon(_.lobby segment "urgentGames") flatMap { case (seeks, povs) =>
|
||||
val displayedPovs = povs take 9
|
||||
lightUserApi.preloadMany(displayedPovs.flatMap(_.opponent.userId)) inject {
|
||||
implicit val lang = ctx.lang
|
||||
|
@ -27,7 +26,7 @@ final class LobbyApi(
|
|||
"nbNowPlaying" -> povs.size
|
||||
) -> displayedPovs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def nowPlaying(pov: Pov) =
|
||||
Json
|
||||
|
|
|
@ -81,9 +81,8 @@ final class PersonalDataExport(
|
|||
def privateMessages(msgs: Seq[(User.ID, String, DateTime)]) =
|
||||
List(
|
||||
textTitle(s"${msgs.size} Direct messages"),
|
||||
msgs.map {
|
||||
case (to, text, date) =>
|
||||
s"$to ${textDate(date)}\n$text"
|
||||
msgs.map { case (to, text, date) =>
|
||||
s"$to ${textDate(date)}\n$text"
|
||||
} mkString bigSep
|
||||
)
|
||||
|
||||
|
|
|
@ -42,23 +42,22 @@ final class PgnDump(
|
|||
}
|
||||
|
||||
private def addEvals(p: Pgn, analysis: Analysis): Pgn =
|
||||
analysis.infos.foldLeft(p) {
|
||||
case (pgn, info) =>
|
||||
pgn.updateTurn(
|
||||
info.turn,
|
||||
turn =>
|
||||
turn.update(
|
||||
info.color,
|
||||
move => {
|
||||
val comment = info.cp
|
||||
.map(_.pawns.toString)
|
||||
.orElse(info.mate.map(m => s"#${m.value}"))
|
||||
move.copy(
|
||||
comments = comment.map(c => s"[%eval $c]").toList ::: move.comments
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
analysis.infos.foldLeft(p) { case (pgn, info) =>
|
||||
pgn.updateTurn(
|
||||
info.turn,
|
||||
turn =>
|
||||
turn.update(
|
||||
info.color,
|
||||
move => {
|
||||
val comment = info.cp
|
||||
.map(_.pawns.toString)
|
||||
.orElse(info.mate.map(m => s"#${m.value}"))
|
||||
move.copy(
|
||||
comments = comment.map(c => s"[%eval $c]").toList ::: move.comments
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def formatter(flags: WithFlags) =
|
||||
|
|
|
@ -51,17 +51,17 @@ final private[api] class RoundApi(
|
|||
(ctx.me.ifTrue(ctx.isMobileApi) ?? (me => noteApi.get(pov.gameId, me.id))) zip
|
||||
forecastApi.loadForDisplay(pov) zip
|
||||
bookmarkApi.exists(pov.game, ctx.me) map {
|
||||
case json ~ simul ~ swiss ~ note ~ forecast ~ bookmarked =>
|
||||
(
|
||||
withTournament(pov, tour) _ compose
|
||||
withSwiss(swiss) compose
|
||||
withSimul(simul) compose
|
||||
withSteps(pov, initialFen) compose
|
||||
withNote(note) compose
|
||||
withBookmark(bookmarked) compose
|
||||
withForecastCount(forecast.map(_.steps.size))
|
||||
)(json)
|
||||
}
|
||||
case json ~ simul ~ swiss ~ note ~ forecast ~ bookmarked =>
|
||||
(
|
||||
withTournament(pov, tour) _ compose
|
||||
withSwiss(swiss) compose
|
||||
withSimul(simul) compose
|
||||
withSteps(pov, initialFen) compose
|
||||
withNote(note) compose
|
||||
withBookmark(bookmarked) compose
|
||||
withForecastCount(forecast.map(_.steps.size))
|
||||
)(json)
|
||||
}
|
||||
}
|
||||
.mon(_.round.api.player)
|
||||
|
||||
|
@ -88,8 +88,7 @@ final private[api] class RoundApi(
|
|||
(pov.game.simulId ?? simulApi.find) zip
|
||||
swissApi.gameView(pov) zip
|
||||
(ctx.me.ifTrue(ctx.isMobileApi) ?? (me => noteApi.get(pov.gameId, me.id))) zip
|
||||
bookmarkApi.exists(pov.game, ctx.me) map {
|
||||
case json ~ simul ~ swiss ~ note ~ bookmarked =>
|
||||
bookmarkApi.exists(pov.game, ctx.me) map { case json ~ simul ~ swiss ~ note ~ bookmarked =>
|
||||
(
|
||||
withTournament(pov, tour) _ compose
|
||||
withSwiss(swiss) compose
|
||||
|
@ -98,7 +97,7 @@ final private[api] class RoundApi(
|
|||
withBookmark(bookmarked) compose
|
||||
withSteps(pov, initialFen)
|
||||
)(json)
|
||||
}
|
||||
}
|
||||
}
|
||||
.mon(_.round.api.watcher)
|
||||
|
||||
|
@ -127,8 +126,7 @@ final private[api] class RoundApi(
|
|||
(pov.game.simulId ?? simulApi.find) zip
|
||||
swissApi.gameView(pov) zip
|
||||
ctx.userId.ifTrue(ctx.isMobileApi).?? { noteApi.get(pov.gameId, _) } zip
|
||||
bookmarkApi.exists(pov.game, ctx.me) map {
|
||||
case json ~ tour ~ simul ~ swiss ~ note ~ bookmarked =>
|
||||
bookmarkApi.exists(pov.game, ctx.me) map { case json ~ tour ~ simul ~ swiss ~ note ~ bookmarked =>
|
||||
(
|
||||
withTournament(pov, tour) _ compose
|
||||
withSwiss(swiss) compose
|
||||
|
@ -138,7 +136,7 @@ final private[api] class RoundApi(
|
|||
withTree(pov, analysis, initialFen, withFlags) compose
|
||||
withAnalysis(pov.game, analysis)
|
||||
)(json)
|
||||
}
|
||||
}
|
||||
}
|
||||
.mon(_.round.api.watcher)
|
||||
|
||||
|
|
|
@ -58,43 +58,43 @@ final private[api] class UserApi(
|
|||
.map(_.map { cr =>
|
||||
math.round(cr * 100)
|
||||
}) map {
|
||||
case gameOption ~ nbGamesWithMe ~ following ~ followers ~ followable ~ relation ~
|
||||
isFollowed ~ nbBookmarks ~ nbPlaying ~ nbImported ~ completionRate =>
|
||||
jsonView(u) ++ {
|
||||
Json
|
||||
.obj(
|
||||
"url" -> makeUrl(s"@/${u.username}"), // for app BC
|
||||
"playing" -> gameOption.map(g => makeUrl(s"${g.gameId}/${g.color.name}")),
|
||||
"nbFollowing" -> following,
|
||||
"nbFollowers" -> followers,
|
||||
"completionRate" -> completionRate,
|
||||
"count" -> Json.obj(
|
||||
"all" -> u.count.game,
|
||||
"rated" -> u.count.rated,
|
||||
"ai" -> u.count.ai,
|
||||
"draw" -> u.count.draw,
|
||||
"drawH" -> u.count.drawH,
|
||||
"loss" -> u.count.loss,
|
||||
"lossH" -> u.count.lossH,
|
||||
"win" -> u.count.win,
|
||||
"winH" -> u.count.winH,
|
||||
"bookmark" -> nbBookmarks,
|
||||
"playing" -> nbPlaying,
|
||||
"import" -> nbImported,
|
||||
"me" -> nbGamesWithMe
|
||||
case gameOption ~ nbGamesWithMe ~ following ~ followers ~ followable ~ relation ~
|
||||
isFollowed ~ nbBookmarks ~ nbPlaying ~ nbImported ~ completionRate =>
|
||||
jsonView(u) ++ {
|
||||
Json
|
||||
.obj(
|
||||
"url" -> makeUrl(s"@/${u.username}"), // for app BC
|
||||
"playing" -> gameOption.map(g => makeUrl(s"${g.gameId}/${g.color.name}")),
|
||||
"nbFollowing" -> following,
|
||||
"nbFollowers" -> followers,
|
||||
"completionRate" -> completionRate,
|
||||
"count" -> Json.obj(
|
||||
"all" -> u.count.game,
|
||||
"rated" -> u.count.rated,
|
||||
"ai" -> u.count.ai,
|
||||
"draw" -> u.count.draw,
|
||||
"drawH" -> u.count.drawH,
|
||||
"loss" -> u.count.loss,
|
||||
"lossH" -> u.count.lossH,
|
||||
"win" -> u.count.win,
|
||||
"winH" -> u.count.winH,
|
||||
"bookmark" -> nbBookmarks,
|
||||
"playing" -> nbPlaying,
|
||||
"import" -> nbImported,
|
||||
"me" -> nbGamesWithMe
|
||||
)
|
||||
)
|
||||
)
|
||||
.add("streaming", liveStreamApi.isStreaming(u.id)) ++
|
||||
as.isDefined.??(
|
||||
Json.obj(
|
||||
"followable" -> followable,
|
||||
"following" -> relation.has(true),
|
||||
"blocking" -> relation.has(false),
|
||||
"followsYou" -> isFollowed
|
||||
.add("streaming", liveStreamApi.isStreaming(u.id)) ++
|
||||
as.isDefined.??(
|
||||
Json.obj(
|
||||
"followable" -> followable,
|
||||
"following" -> relation.has(true),
|
||||
"blocking" -> relation.has(false),
|
||||
"followsYou" -> isFollowed
|
||||
)
|
||||
)
|
||||
)
|
||||
}.noNull
|
||||
}
|
||||
}.noNull
|
||||
}
|
||||
}
|
||||
|
||||
private def addPlayingStreaming(js: JsObject, id: User.ID) =
|
||||
|
|
|
@ -33,7 +33,8 @@ final class ChallengeApi(
|
|||
def create(c: Challenge): Fu[Boolean] =
|
||||
isLimitedByMaxPlaying(c) flatMap {
|
||||
case true => fuFalse
|
||||
case false => {
|
||||
case false =>
|
||||
{
|
||||
repo like c flatMap { _ ?? repo.cancel }
|
||||
} >> (repo insert c) >>- {
|
||||
uncacheAndNotify(c)
|
||||
|
|
|
@ -53,22 +53,22 @@ final class ChallengeGranter(
|
|||
.fold[Fu[Option[ChallengeDenied.Reason]]](fuccess(YouAreAnon.some)) { from =>
|
||||
relationApi.fetchRelation(dest, from) zip
|
||||
prefApi.getPref(dest).map(_.challenge) map {
|
||||
case (Some(Block), _) => YouAreBlocked.some
|
||||
case (_, Pref.Challenge.NEVER) => TheyDontAcceptChallenges.some
|
||||
case (Some(Follow), _) => none // always accept from followed
|
||||
case (_, _) if from.marks.engine && !dest.marks.engine => YouAreBlocked.some
|
||||
case (_, Pref.Challenge.FRIEND) => FriendsOnly.some
|
||||
case (_, Pref.Challenge.RATING) =>
|
||||
perfType ?? { pt =>
|
||||
if (from.perfs(pt).provisional || dest.perfs(pt).provisional)
|
||||
RatingIsProvisional(pt).some
|
||||
else {
|
||||
val diff = math.abs(from.perfs(pt).intRating - dest.perfs(pt).intRating)
|
||||
(diff > ratingThreshold) option RatingOutsideRange(pt)
|
||||
case (Some(Block), _) => YouAreBlocked.some
|
||||
case (_, Pref.Challenge.NEVER) => TheyDontAcceptChallenges.some
|
||||
case (Some(Follow), _) => none // always accept from followed
|
||||
case (_, _) if from.marks.engine && !dest.marks.engine => YouAreBlocked.some
|
||||
case (_, Pref.Challenge.FRIEND) => FriendsOnly.some
|
||||
case (_, Pref.Challenge.RATING) =>
|
||||
perfType ?? { pt =>
|
||||
if (from.perfs(pt).provisional || dest.perfs(pt).provisional)
|
||||
RatingIsProvisional(pt).some
|
||||
else {
|
||||
val diff = math.abs(from.perfs(pt).intRating - dest.perfs(pt).intRating)
|
||||
(diff > ratingThreshold) option RatingOutsideRange(pt)
|
||||
}
|
||||
}
|
||||
}
|
||||
case (_, Pref.Challenge.ALWAYS) => none
|
||||
}
|
||||
case (_, Pref.Challenge.ALWAYS) => none
|
||||
}
|
||||
}
|
||||
.map {
|
||||
case None if dest.isBot && perfType.has(PerfType.UltraBullet) => BotUltraBullet.some
|
||||
|
|
|
@ -57,8 +57,8 @@ final private class ChallengeRepo(coll: Coll, maxPerUser: Max)(implicit
|
|||
.void
|
||||
|
||||
private[challenge] def allWithUserId(userId: String): Fu[List[Challenge]] =
|
||||
createdByChallengerId(userId) zip createdByDestId(userId) dmap {
|
||||
case (x, y) => x ::: y
|
||||
createdByChallengerId(userId) zip createdByDestId(userId) dmap { case (x, y) =>
|
||||
x ::: y
|
||||
}
|
||||
|
||||
@nowarn("cat=unused") def like(c: Challenge) =
|
||||
|
@ -87,9 +87,8 @@ final private class ChallengeRepo(coll: Coll, maxPerUser: Max)(implicit
|
|||
.hint(coll hint $doc("seenAt" -> 1)) // partial index
|
||||
.cursor[Challenge]()
|
||||
.list(max)
|
||||
.recoverWith {
|
||||
case _: reactivemongo.core.errors.DatabaseException =>
|
||||
coll.list[Challenge](selector, max)
|
||||
.recoverWith { case _: reactivemongo.core.errors.DatabaseException =>
|
||||
coll.list[Challenge](selector, max)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ final private class ChallengeSocket(
|
|||
|
||||
private lazy val send: String => Unit = remoteSocketApi.makeSender("chal-out").apply _
|
||||
|
||||
private lazy val challengeHandler: Handler = {
|
||||
case Protocol.In.OwnerPings(ids) => ids foreach api.ping
|
||||
private lazy val challengeHandler: Handler = { case Protocol.In.OwnerPings(ids) =>
|
||||
ids foreach api.ping
|
||||
}
|
||||
|
||||
remoteSocketApi.subscribe("chal-in", Protocol.In.reader)(
|
||||
|
|
|
@ -65,16 +65,15 @@ private object Joiner {
|
|||
)
|
||||
.withId(c.id)
|
||||
.pipe { g =>
|
||||
state.fold(g) {
|
||||
case sit @ SituationPlus(Situation(board, _), _) =>
|
||||
g.copy(
|
||||
chess = g.chess.copy(
|
||||
situation = g.situation.copy(
|
||||
board = g.board.copy(history = board.history)
|
||||
),
|
||||
turns = sit.turns
|
||||
)
|
||||
state.fold(g) { case sit @ SituationPlus(Situation(board, _), _) =>
|
||||
g.copy(
|
||||
chess = g.chess.copy(
|
||||
situation = g.situation.copy(
|
||||
board = g.board.copy(history = board.history)
|
||||
),
|
||||
turns = sit.turns
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
.start
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue