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