scalafmt 2.7.1

initial-glicko
Thibault Duplessis 2020-09-21 09:28:28 +02:00
parent c794d88af9
commit aaf88bc62c
310 changed files with 3654 additions and 3979 deletions

View File

@ -1,4 +1,4 @@
version = "2.6.3"
version = "2.7.1"
align.preset = more
maxColumn = 110
spaces.inImportCurlyBraces = true

View File

@ -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"))

View File

@ -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) =

View File

@ -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(
)
)
}
}
}
}
}

View File

@ -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)
}
}
}
)

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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()))
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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) =

View File

@ -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))
}
)
}

View File

@ -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
)
}
}
}

View File

@ -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)
}
}

View File

@ -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 =>

View File

@ -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

View File

@ -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
}
}

View File

@ -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) =

View File

@ -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]) =

View File

@ -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
}

View File

@ -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)
}
}
}
}
}

View File

@ -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) =

View File

@ -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)
}

View File

@ -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 =>

View File

@ -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 = _ =>

View File

@ -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))
}
}
}

View File

@ -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))
}
}
}

View File

@ -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 =>

View File

@ -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()))
}

View File

@ -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 =

View File

@ -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
}
)
}
}

View File

@ -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)
}
}

View File

@ -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 =

View File

@ -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)

View File

@ -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))
}
)
}
}

View File

@ -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))
}
}
}
}
}

View File

@ -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] =

View File

@ -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 {

View File

@ -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
)
}
}
}

View File

@ -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)
}
)

View File

@ -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)

View File

@ -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"

View File

@ -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)
}
}
}

View File

@ -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"
}

View File

@ -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))
)
}
)
)
}
)
)

View File

@ -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")

View File

@ -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())
}
)

View File

@ -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">"""

View File

@ -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)
)
}
)
)

View File

@ -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)
)
}
)
)

View File

@ -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())
)
)
}
)
)

View File

@ -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(

View File

@ -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))
)
)
}
)
}

View File

@ -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))
)
}
)
)

View File

@ -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
)

View File

@ -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 =>

View File

@ -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)
}
)
})
}
}

View File

@ -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")(

View File

@ -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")(

View File

@ -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)
)
}
)
)

View File

@ -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(

View File

@ -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)
)
}
)
)
}
)
)

View File

@ -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(_)))
)
}
)
)

View File

@ -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(_)
}
}
)
)

View File

@ -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)
)
}
)

View File

@ -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(

View File

@ -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)
}
)
)

View File

@ -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(

View File

@ -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",

View File

@ -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(

View File

@ -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))
)
})
)
}
)
)

View File

@ -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")
)
)
}
)
),

View File

@ -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))
)
}
)
)

View File

@ -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"),

View File

@ -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

View File

@ -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)
)

View File

@ -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)

View File

@ -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 " → "

View File

@ -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(_) =>

View File

@ -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

View File

@ -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)
}
}

View File

@ -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] =

View File

@ -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

View File

@ -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))
)

View File

@ -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 =

View File

@ -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) { () =>

View File

@ -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))
}
}
}

View File

@ -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))

View File

@ -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

View File

@ -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
)

View File

@ -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) =

View File

@ -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)

View File

@ -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) =

View File

@ -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)

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)(

View File

@ -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