![Thibault Duplessis](/assets/img/avatar_default.png)
* master: (116 commits) use Game.updatedAt when last move time is not available integrate analysis board with correspondence games improve analysis integration add screenshot fix analysis board highlights more cache tweaks don't show games older than 5 minutes improve current game detection hr "hrvatski" translation #11283. Author: gus_fring. update sl translation hu "Magyar" translation #11279. Author: OMMHOA. Couldn't translate perfectly "proceed" so it's "go" instead. ca "Català, valencià" translation #11278. Author: pedrolo. uk "українська" translation #11276. Author: IvTK. nl "Nederlands" translation #11274. Author: rokbe. correspondensie -> correspondentie sv "svenska" translation #11273. Author: nuwonga. sq "Shqip" translation #11271. Author: xhevati. pl "polski" translation #11268. Author: pirouetti. tr "Türkçe" translation #11265. Author: mabolek. sv "svenska" translation #11264. Author: Weckipecki. ca "Català, valencià" translation #11263. Author: Borchess. ... Conflicts: modules/chess ui/analyse/src/ctrl.js
190 lines
6.5 KiB
Scala
190 lines
6.5 KiB
Scala
package controllers
|
|
|
|
import play.api.mvc._, Results._
|
|
|
|
import lila.api.Context
|
|
import lila.app._
|
|
import lila.app.mashup.GameFilterMenu
|
|
import lila.common.LilaCookie
|
|
import lila.db.api.$find
|
|
import lila.game.{ GameRepo, Pov }
|
|
import lila.rating.PerfType
|
|
import lila.security.Permission
|
|
import lila.user.tube.userTube
|
|
import lila.user.{ User => UserModel, UserRepo }
|
|
import views._
|
|
|
|
object User extends LilaController {
|
|
|
|
private def env = Env.user
|
|
private def gamePaginator = Env.game.paginator
|
|
private def forms = lila.user.DataForm
|
|
private def relationApi = Env.relation.api
|
|
|
|
def tv(username: String) = Open { implicit ctx =>
|
|
OptionFuResult(UserRepo named username) { user =>
|
|
(GameRepo lastPlayed user) orElse
|
|
(GameRepo lastPlayed user) flatMap {
|
|
_.fold(fuccess(Redirect(routes.User.show(username)))) { pov =>
|
|
Round.watch(pov, userTv = user.some)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def show(username: String) = Open { implicit ctx =>
|
|
filter(username, none, 1)
|
|
}
|
|
|
|
def showMini(username: String) = Open { implicit ctx =>
|
|
OptionFuResult(UserRepo named username) { user =>
|
|
GameRepo lastPlayed user zip
|
|
(ctx.userId ?? { relationApi.blocks(user.id, _) }) zip
|
|
(ctx.isAuth ?? { Env.pref.api.followable(user.id) }) zip
|
|
(ctx.userId ?? { relationApi.relation(_, user.id) }) map {
|
|
case (((pov, blocked), followable), relation) =>
|
|
Ok(html.user.mini(user, pov, blocked, followable, relation))
|
|
.withHeaders(CACHE_CONTROL -> "max-age=5")
|
|
}
|
|
}
|
|
}
|
|
|
|
def showFilter(username: String, filterName: String, page: Int) = Open { implicit ctx =>
|
|
filter(username, filterName.some, page)
|
|
}
|
|
|
|
def online = Open { implicit req =>
|
|
val max = 1000
|
|
UserRepo.byIdsSortRating(env.onlineUserIdMemo.keys, max) map { html.user.online(_, max) }
|
|
}
|
|
|
|
private def filter(
|
|
username: String,
|
|
filterOption: Option[String],
|
|
page: Int,
|
|
status: Results.Status = Results.Ok)(implicit ctx: Context) =
|
|
Reasonable(page) {
|
|
OptionFuResult(UserRepo named username) { u =>
|
|
(u.enabled || isGranted(_.UserSpy)).fold({
|
|
if (lila.common.HTTPRequest.isSynchronousHttp(ctx.req))
|
|
userShow(u, filterOption, page)
|
|
else
|
|
userGames(u, filterOption, page)
|
|
} map { status(_) },
|
|
fuccess(NotFound(html.user.disabled(u)))
|
|
)
|
|
}
|
|
}
|
|
|
|
private def userShow(u: UserModel, filterOption: Option[String], page: Int)(implicit ctx: Context) = for {
|
|
info ← Env.current.userInfo(u, ctx)
|
|
filterName = filterOption | "all"
|
|
filters = GameFilterMenu(info, ctx.me, filterName)
|
|
pag <- GameFilterMenu.paginatorOf(
|
|
user = u,
|
|
info = info.some,
|
|
filter = filters.current,
|
|
me = ctx.me,
|
|
page = page)
|
|
relation <- ctx.userId ?? { relationApi.relation(_, u.id) }
|
|
notes <- ctx.me ?? { me =>
|
|
relationApi friends me.id flatMap { env.noteApi.get(u, me, _) }
|
|
}
|
|
followable <- ctx.isAuth ?? { Env.pref.api followable u.id }
|
|
blocked <- ctx.userId ?? { relationApi.blocks(u.id, _) }
|
|
} yield html.user.show(u, info, pag, filters, relation, notes, followable, blocked)
|
|
|
|
private def userGames(u: UserModel, filterOption: Option[String], page: Int)(implicit ctx: Context) = {
|
|
val filterName = filterOption | "all"
|
|
GameFilterMenu.paginatorOf(
|
|
user = u,
|
|
info = none,
|
|
filter = GameFilterMenu.currentOf(GameFilterMenu.all, filterName),
|
|
me = ctx.me,
|
|
page = page
|
|
) map { html.user.games(u, _, filterName) }
|
|
}
|
|
|
|
def list = Open { implicit ctx =>
|
|
val nb = 10
|
|
for {
|
|
bullet ← env.cached topPerf PerfType.Bullet.key
|
|
blitz ← env.cached topPerf PerfType.Blitz.key
|
|
classical ← env.cached topPerf PerfType.Classical.key
|
|
chess960 ← env.cached topPerf PerfType.Chess960.key
|
|
kingOfTheHill ← env.cached topPerf PerfType.KingOfTheHill.key
|
|
threeCheck ← env.cached topPerf PerfType.ThreeCheck.key
|
|
antichess <- env.cached topPerf PerfType.Antichess.key
|
|
nbAllTime ← env.cached topNbGame nb map2 { (user: UserModel) =>
|
|
user -> user.count.game
|
|
}
|
|
nbWeek ← Env.game.cached activePlayerUidsWeek nb flatMap { pairs =>
|
|
UserRepo.byOrderedIds(pairs.map(_.userId)) map (_ zip pairs.map(_.nb))
|
|
}
|
|
tourneyWinners ← Env.tournament.winners scheduled nb
|
|
online ← env.cached topOnline 30
|
|
} yield html.user.list(
|
|
tourneyWinners = tourneyWinners,
|
|
online = online,
|
|
bullet = bullet,
|
|
blitz = blitz,
|
|
classical = classical,
|
|
chess960 = chess960,
|
|
kingOfTheHill = kingOfTheHill,
|
|
threeCheck = threeCheck,
|
|
antichess = antichess,
|
|
nbWeek = nbWeek,
|
|
nbAllTime = nbAllTime)
|
|
}
|
|
|
|
def mod(username: String) = Secure(_.UserSpy) { implicit ctx =>
|
|
me => OptionFuOk(UserRepo named username) { user =>
|
|
Env.evaluation.evaluator find user zip
|
|
(Env.security userSpy user.id) map {
|
|
case (eval, spy) => html.user.mod(user, spy, eval)
|
|
}
|
|
}
|
|
}
|
|
|
|
def evaluate(username: String) = Secure(_.UserEvaluate) { implicit ctx =>
|
|
me => OptionFuResult(UserRepo named username) { user =>
|
|
Env.evaluation.evaluator.generate(user.id, true) inject Redirect(routes.User.show(username).url + "?mod")
|
|
}
|
|
}
|
|
|
|
def writeNote(username: String) = AuthBody { implicit ctx =>
|
|
me => OptionFuResult(UserRepo named username) { user =>
|
|
implicit val req = ctx.body
|
|
env.forms.note.bindFromRequest.fold(
|
|
err => filter(username, none, 1, Results.BadRequest),
|
|
text => env.noteApi.write(user, text, me) inject Redirect(routes.User.show(username).url + "?note")
|
|
)
|
|
}
|
|
}
|
|
|
|
def opponents(username: String) = Open { implicit ctx =>
|
|
OptionFuOk(UserRepo named username) { user =>
|
|
lila.game.BestOpponents(user.id, 50) flatMap { ops =>
|
|
ctx.isAuth.fold(
|
|
Env.pref.api.followables(ops map (_._1.id)),
|
|
fuccess(List.fill(50)(true))
|
|
) flatMap { followables =>
|
|
(ops zip followables).map {
|
|
case ((u, nb), followable) => ctx.userId ?? { myId =>
|
|
relationApi.relation(myId, u.id)
|
|
} map { lila.relation.Related(u, nb, followable, _) }
|
|
}.sequenceFu map { relateds =>
|
|
html.user.opponents(user, relateds)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def autocomplete = Open { implicit ctx =>
|
|
get("term", ctx.req).filter(_.nonEmpty).fold(BadRequest("No search term provided").fuccess: Fu[Result]) { term =>
|
|
JsonOk(UserRepo usernamesLike term)
|
|
}
|
|
}
|
|
}
|