Skip to content

Commit

Permalink
Merge branch 'master' into puzzle-dashboard-api
Browse files Browse the repository at this point in the history
* master:
  api endpoint to give opponent more time - closes lichess-org#7955
  simplify contact page, remove duplicate report entries - fixes lichess-org#7962
  show swiss streamers - closes lichess-org#7485
  fix lichess-org#7958
  refactor socket redis sender
  feature tournaments for up to 24h
  add broadcast event icon and fix event icons styles - closes lichess-org#7964
  New translations: puzzleTheme.xml (Basque) (lichess-org#7960)
  always send game messages on the same redis channel
  • Loading branch information
ornicar committed Jan 21, 2021
2 parents 72c9f59 + a0fb3fc commit 652c173
Show file tree
Hide file tree
Showing 27 changed files with 163 additions and 134 deletions.
14 changes: 14 additions & 0 deletions app/controllers/Round.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controllers

import play.api.libs.json._
import play.api.mvc._
import scala.concurrent.duration._
import views._

import lila.api.Context
Expand Down Expand Up @@ -350,4 +351,17 @@ final class Round(
html.game.mini(_)
)
}

def apiAddTime(anyId: String, seconds: Int) =
Scoped(_.Challenge.Write) { implicit req => me =>
import lila.round.actorApi.round.Moretime
if (seconds < 1 || seconds > 86400) BadRequest.fuccess
else
env.round.proxyRepo.game(lila.game.Game takeGameId anyId) map {
_.flatMap { Pov(_, me) }.?? { pov =>
env.round.tellRound(pov.gameId, Moretime(pov.typedPlayerId, seconds.seconds))
jsonOkResult
}
}
}
}
14 changes: 13 additions & 1 deletion app/controllers/Swiss.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ final class Swiss(
_ <- chat ?? { c =>
env.user.lightUserApi.preloadMany(c.chat.userIds)
}
streamers <- streamerCache get swiss.id
isLocalMod <- canChat ?? canModChat(swiss)
} yield Ok(html.swiss.show(swiss, verdicts, json, chat, isLocalMod))
} yield Ok(html.swiss.show(swiss, verdicts, json, chat, streamers, isLocalMod))
},
api = _ =>
swissOption.fold(notFoundJson("No such swiss tournament")) { swiss =>
Expand Down Expand Up @@ -274,4 +275,15 @@ final class Swiss(
private def canModChat(swiss: SwissModel)(implicit ctx: Context): Fu[Boolean] =
if (isGranted(_.ChatTimeout)) fuTrue
else ctx.userId ?? { env.team.cached.isLeader(swiss.teamId, _) }

private val streamerCache =
env.memo.cacheApi[SwissModel.Id, List[lila.user.User.ID]](64, "swiss.streamers") {
_.refreshAfterWrite(15.seconds)
.maximumSize(64)
.buildAsyncFuture { id =>
env.streamer.liveStreamApi.all.flatMap { streams =>
env.swiss.api.filterPlaying(id, streams.streams.map(_.streamer.userId))
}
}
}
}
21 changes: 13 additions & 8 deletions app/views/event.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import play.api.data.Form
import lila.api.Context
import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._
import lila.event.EventForm
import lila.event.{ Event, EventForm }

object event {

Expand All @@ -20,7 +20,7 @@ object event {
)
}

def edit(event: lila.event.Event, form: Form[_])(implicit ctx: Context) =
def edit(event: Event, form: Form[_])(implicit ctx: Context) =
layout(title = event.title, css = "mod.form") {
div(cls := "crud edit page-menu__content box box-pad")(
div(cls := "box__top")(
Expand All @@ -37,17 +37,22 @@ object event {
)
}

def show(e: lila.event.Event)(implicit ctx: Context) =
def iconOf(e: Event) =
e.icon match {
case None => i(cls := "img", dataIcon := "")
case Some(c) if c == EventForm.icon.broadcast => i(cls := "img", dataIcon := "")
case Some(c) => img(cls := "img", src := assetUrl(s"images/$c"))
}

def show(e: Event)(implicit ctx: Context) =
views.html.base.layout(
title = e.title,
moreCss = cssTag("event"),
moreJs = jsTag("event-countdown.js")
) {
main(cls := "page-small event box box-pad")(
div(cls := "box__top")(
e.icon map { i =>
img(cls := "img", src := assetUrl(s"images/$i"))
} getOrElse i(cls := "img", dataIcon := ""),
iconOf(e),
div(
h1(e.title),
strong(cls := "headline")(e.headline)
Expand All @@ -67,7 +72,7 @@ object event {
)
}

def manager(events: List[lila.event.Event])(implicit ctx: Context) = {
def manager(events: List[Event])(implicit ctx: Context) = {
val title = "Event manager"
layout(title = title) {
div(cls := "crud page-menu__content box")(
Expand Down Expand Up @@ -134,7 +139,7 @@ object event {
frag("Icon"),
half = true,
help = frag("Displayed on the homepage button").some
)(form3.select(_, EventForm.iconChoices))
)(form3.select(_, EventForm.icon.choices))
),
form3.group(
form("headline"),
Expand Down
4 changes: 1 addition & 3 deletions app/views/lobby/bits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,7 @@ object bits {
"invert" -> e.isNowOrSoon
)
)(
e.icon map { i =>
img(cls := "img", src := assetUrl(s"images/$i"))
} getOrElse i(cls := "img", dataIcon := ""),
views.html.event.iconOf(e),
span(cls := "content")(
span(cls := "name")(e.title),
span(cls := "headline")(e.headline),
Expand Down
27 changes: 8 additions & 19 deletions app/views/puzzle/dashboard.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,13 @@ object dashboard {
)
}
) { dash =>
frag(
dash.mostPlayed.size > 2 option
div(cls := s"${baseClass}__global")(
metricsOf(days, PuzzleTheme.mix.key, dash.global),
canvas(cls := s"${baseClass}__radar")
)
)
}

// data: {
// labels: ['Running', 'Swimming', 'Eating', 'Cycling'],
// datasets: [{
// data: [20, 10, 4, 2]
// }]
// }

def improvementAreas(user: User, dashOpt: Option[PuzzleDashboard], days: Int)(implicit ctx: Context) =
dashboardLayout(
user = user,
Expand All @@ -83,7 +75,7 @@ object dashboard {
subtitle = "Train these to optimize your progress!",
dashOpt = dashOpt
) { dash =>
themeSelection(days, dash.weakThemes)
dash.weakThemes.nonEmpty option themeSelection(days, dash.weakThemes)
}

def strengths(user: User, dashOpt: Option[PuzzleDashboard], days: Int)(implicit ctx: Context) =
Expand All @@ -97,7 +89,7 @@ object dashboard {
subtitle = "You perform the best in these themes",
dashOpt = dashOpt
) { dash =>
themeSelection(days, dash.strongThemes)
dash.strongThemes.nonEmpty option themeSelection(days, dash.strongThemes)
}

private def dashboardLayout(
Expand All @@ -109,7 +101,7 @@ object dashboard {
dashOpt: Option[PuzzleDashboard],
moreJs: Frag = emptyFrag
)(
body: PuzzleDashboard => Frag
body: PuzzleDashboard => Option[Frag]
)(implicit ctx: Context) =
views.html.base.layout(
title = title,
Expand All @@ -136,13 +128,10 @@ object dashboard {
}
)
),
dashOpt match {
case None =>
div(cls := s"${baseClass}__empty")(
a(href := routes.Puzzle.home())("Nothing to show, go play some puzzles first!")
)
case Some(dash) => body(dash)
}
dashOpt.flatMap(body) |
div(cls := s"${baseClass}__empty")(
a(href := routes.Puzzle.home())("Nothing to show, go play some puzzles first!")
)
)
)
)
Expand Down
50 changes: 19 additions & 31 deletions app/views/site/contact.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package views
package html.site

import controllers.routes
import scala.util.chaining._

import controllers.routes
import lila.api.Context
import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._
Expand Down Expand Up @@ -149,39 +149,27 @@ object contact {
)
)
),
Branch(
Leaf(
"report",
wantReport(),
List(
"cheating" -> reportCheating,
"sandbagging" -> reportSandbagging,
"trolling" -> reportTrolling,
"insults" -> reportInsults,
"some other reason" -> reportOtherReason
).map { case (reason, name) =>
Leaf(
reason,
name(),
frag(
p(
a(href := routes.Report.form())(toReportAPlayer(name())),
"."
),
p(
youCanAlsoReachReportPage(button(cls := "thin button button-empty", dataIcon := "!"))
),
p(
doNotMessageModerators(),
br,
doNotReportInForum(),
br,
doNotSendReportEmails(),
br,
onlyReports()
)
)
frag(
p(
a(href := routes.Report.form())(toReportAPlayerUseForm()),
"."
),
p(
youCanAlsoReachReportPage(button(cls := "thin button button-empty", dataIcon := "!"))
),
p(
doNotMessageModerators(),
br,
doNotReportInForum(),
br,
doNotSendReportEmails(),
br,
onlyReports()
)
}
)
),
Branch(
"bug",
Expand Down
3 changes: 2 additions & 1 deletion app/views/swiss/show.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ object show {
verdicts: SwissCondition.All.WithVerdicts,
data: play.api.libs.json.JsObject,
chatOption: Option[lila.chat.UserChat.Mine],
streamers: List[lila.user.User.ID],
isLocalMod: Boolean
)(implicit ctx: Context): Frag = {
val isDirector = ctx.userId.has(s.createdBy)
Expand Down Expand Up @@ -66,7 +67,7 @@ object show {
)(
main(cls := "swiss")(
st.aside(cls := "swiss__side")(
swiss.side(s, verdicts, chatOption.isDefined)
swiss.side(s, verdicts, streamers, chatOption.isDefined)
),
div(cls := "swiss__main")(div(cls := "box"))
)
Expand Down
10 changes: 9 additions & 1 deletion app/views/swiss/side.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ object side {

private val separator = ""

def apply(s: Swiss, verdicts: SwissCondition.All.WithVerdicts, chat: Boolean)(implicit
def apply(
s: Swiss,
verdicts: SwissCondition.All.WithVerdicts,
streamers: List[lila.user.User.ID],
chat: Boolean
)(implicit
ctx: Context
) =
frag(
Expand Down Expand Up @@ -88,6 +93,9 @@ object side {
else br,
absClientDateTime(s.startsAt)
),
streamers.nonEmpty option div(cls := "context-streamers")(
streamers map views.html.streamer.bits.contextual
),
chat option views.html.chat.frag
)
}
1 change: 0 additions & 1 deletion conf/base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,6 @@ gameSearch {
actor.name = game-search
}
round {
moretime = 15 seconds
collection {
note = game_note
forecast = forecast
Expand Down
3 changes: 2 additions & 1 deletion conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ POST /api/challenge/:user controllers.Challenge.apiCreate(user: Str
POST /api/challenge/$id<\w{8}>/accept controllers.Challenge.apiAccept(id: String)
POST /api/challenge/$id<\w{8}>/decline controllers.Challenge.apiDecline(id: String)
POST /api/challenge/$id<\w{8}>/cancel controllers.Challenge.apiCancel(id: String)
POST /api/challenge/$id<\w{8}>/start-clocks controllers.Challenge.apiStartClocks(id: String)
POST /api/challenge/$id<\w{8}>/start-clocks controllers.Challenge.apiStartClocks(id: String)
POST /api/round/$id<\w{8}>/add-time/:seconds controllers.Round.apiAddTime(id: String, seconds: Int)
GET /api/cloud-eval controllers.Api.cloudEval
GET /api/broadcast controllers.Relay.apiIndex
POST /api/import controllers.Importer.apiSendGame
Expand Down
20 changes: 12 additions & 8 deletions modules/event/src/main/EventForm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ import lila.user.User

object EventForm {

val iconChoices = List(
"" -> "Microphone",
"lichess.event.png" -> "Lichess",
"trophy.event.png" -> "Trophy",
"offerspill.logo.png" -> "Offerspill"
)
val imageDefault = ""
object icon {
val default = ""
val broadcast = "broadcast.icon"
val choices = List(
default -> "Microphone",
"lichess.event.png" -> "Lichess",
"trophy.event.png" -> "Trophy",
broadcast -> "Broadcast",
"offerspill.logo.png" -> "Offerspill"
)
}

val form = Form(
mapping(
Expand All @@ -35,7 +39,7 @@ object EventForm {
lila.user.UserForm.historicalUsernameField
.transform[User.ID](_.toLowerCase, identity)
},
"icon" -> stringIn(iconChoices),
"icon" -> stringIn(icon.choices),
"countdown" -> boolean
)(Data.apply)(Data.unapply)
) fill Data(
Expand Down
2 changes: 2 additions & 0 deletions modules/game/src/main/Pov.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ case class Pov(game: Game, color: Color) {

def playerId = player.id

def typedPlayerId = Game.PlayerId(player.id)

def fullId = game fullIdOf color

def gameId = game.id
Expand Down
7 changes: 1 addition & 6 deletions modules/i18n/src/main/I18nKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1225,12 +1225,7 @@ val `orCloseAccount` = new I18nKey("contact:orCloseAccount")
val `wantClearHistory` = new I18nKey("contact:wantClearHistory")
val `cantClearHistory` = new I18nKey("contact:cantClearHistory")
val `wantReport` = new I18nKey("contact:wantReport")
val `reportCheating` = new I18nKey("contact:reportCheating")
val `reportSandbagging` = new I18nKey("contact:reportSandbagging")
val `reportTrolling` = new I18nKey("contact:reportTrolling")
val `reportInsults` = new I18nKey("contact:reportInsults")
val `reportOtherReason` = new I18nKey("contact:reportOtherReason")
val `toReportAPlayer` = new I18nKey("contact:toReportAPlayer")
val `toReportAPlayerUseForm` = new I18nKey("contact:toReportAPlayerUseForm")
val `youCanAlsoReachReportPage` = new I18nKey("contact:youCanAlsoReachReportPage")
val `doNotReportInForum` = new I18nKey("contact:doNotReportInForum")
val `doNotSendReportEmails` = new I18nKey("contact:doNotSendReportEmails")
Expand Down
4 changes: 1 addition & 3 deletions modules/round/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import lila.user.User
private class RoundConfig(
@ConfigName("collection.note") val noteColl: CollName,
@ConfigName("collection.forecast") val forecastColl: CollName,
@ConfigName("collection.alarm") val alarmColl: CollName,
@ConfigName("moretime") val moretimeDuration: MoretimeDuration
@ConfigName("collection.alarm") val alarmColl: CollName
)

@Module
Expand Down Expand Up @@ -59,7 +58,6 @@ final class Env(
scheduler: akka.actor.Scheduler
) {

implicit private val moretimeLoader = durationLoader(MoretimeDuration.apply)
implicit private val animationLoader = durationLoader(AnimationDuration.apply)
private val config = appConfig.get[RoundConfig]("round")(AutoConfig.loader)

Expand Down
Loading

0 comments on commit 652c173

Please sign in to comment.