list all pairings of all rounds of a swiss tournament
parent
40fe1f8772
commit
642c14cb74
|
@ -82,6 +82,18 @@ final class Swiss(
|
|||
private def isCtxInTheTeam(teamId: lila.team.Team.ID)(implicit ctx: Context) =
|
||||
ctx.userId.??(u => env.team.cached.teamIds(u).dmap(_ contains teamId))
|
||||
|
||||
def round(id: String, round: Int) =
|
||||
Open { implicit ctx =>
|
||||
OptionFuResult(env.swiss.api.byId(SwissId(id))) { swiss =>
|
||||
(round > 0 && round <= swiss.round.value).option(lila.swiss.SwissRound.Number(round)) ?? { r =>
|
||||
val page = getInt("page").filter(0.<)
|
||||
env.swiss.roundPager(swiss, r, page | 0) map { pager =>
|
||||
Ok(html.swiss.show.round(swiss, r, pager))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def form(teamId: String) =
|
||||
Open { implicit ctx =>
|
||||
Ok(html.swiss.form.create(env.swiss.forms.create, teamId)).fuccess
|
||||
|
|
|
@ -6,8 +6,6 @@ import lila.common.paginator.Paginator
|
|||
|
||||
trait PaginatorHelper {
|
||||
|
||||
implicit def toRichPager[A](pager: Paginator[A]): RichPager = new RichPager(pager)
|
||||
|
||||
def pagerNext(pager: lila.common.paginator.Paginator[_], url: Int => String): Option[Tag] =
|
||||
pager.nextPage.map { np =>
|
||||
div(cls := "pager")(pagerA(url(np)))
|
||||
|
@ -20,23 +18,3 @@ trait PaginatorHelper {
|
|||
|
||||
private def pagerA(url: String) = a(rel := "next", href := url)("Next")
|
||||
}
|
||||
|
||||
final class RichPager(pager: Paginator[_]) {
|
||||
|
||||
def sliding(length: Int, showPost: Boolean = true): List[Option[Int]] = {
|
||||
val fromPage = 1 max (pager.currentPage - length)
|
||||
val toPage = pager.nbPages min (pager.currentPage + length)
|
||||
val pre = fromPage match {
|
||||
case 1 => Nil
|
||||
case 2 => List(1.some)
|
||||
case _ => List(1.some, none)
|
||||
}
|
||||
val post = toPage match {
|
||||
case x if x == pager.nbPages => Nil
|
||||
case x if x == pager.nbPages - 1 => List(pager.nbPages.some)
|
||||
case _ if showPost => List(none, pager.nbPages.some)
|
||||
case _ => List(none)
|
||||
}
|
||||
pre ::: (fromPage to toPage).view.map(some).toList ::: post
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,11 +129,6 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
|
|||
modIcon = false
|
||||
)
|
||||
|
||||
def userIdLink(
|
||||
userId: String,
|
||||
cssClass: Option[String]
|
||||
)(implicit lang: Lang): Frag = userIdLink(userId.some, cssClass)
|
||||
|
||||
def titleTag(title: Option[Title]): Option[Frag] =
|
||||
title map { t =>
|
||||
frag(userTitleTag(t), nbsp)
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
package views.html.base
|
||||
package views.html
|
||||
package base
|
||||
|
||||
import chess.format.FEN
|
||||
import controllers.routes
|
||||
import play.api.i18n.Lang
|
||||
import play.api.mvc.Call
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.paginator.Paginator
|
||||
|
||||
object bits {
|
||||
|
||||
|
@ -47,4 +52,43 @@ z-index: 99;
|
|||
|
||||
def fenAnalysisLink(fen: FEN)(implicit lang: Lang) =
|
||||
a(href := routes.UserAnalysis.parseArg(fen.value.replace(" ", "_")))(trans.analysis())
|
||||
|
||||
def paginationByQuery(route: Call, pager: Paginator[_], showPost: Boolean): Option[Frag] =
|
||||
pagination(page => s"$route?page=$page", pager, showPost)
|
||||
|
||||
def pagination(url: Int => String, pager: Paginator[_], showPost: Boolean): Option[Frag] =
|
||||
pager.hasToPaginate option pagination(url, pager.currentPage, pager.nbPages, showPost)
|
||||
|
||||
def pagination(url: Int => String, page: Int, nbPages: Int, showPost: Boolean): Tag =
|
||||
st.nav(cls := "pagination")(
|
||||
if (page > 1) a(href := url(page - 1), dataIcon := "I")
|
||||
else span(cls := "disabled", dataIcon := "I"),
|
||||
sliding(page, nbPages, 3, showPost = showPost).map {
|
||||
case None => raw(" … ")
|
||||
case Some(p) if p == page => span(cls := "current")(p)
|
||||
case Some(p) => a(href := url(p))(p)
|
||||
},
|
||||
if (page < nbPages) a(rel := "next", href := url(page + 1), dataIcon := "H")
|
||||
else span(cls := "disabled", dataIcon := "H")
|
||||
)
|
||||
|
||||
private def sliding(pager: Paginator[_], length: Int, showPost: Boolean): List[Option[Int]] =
|
||||
sliding(pager.currentPage, pager.nbPages, length, showPost)
|
||||
|
||||
private def sliding(page: Int, nbPages: Int, length: Int, showPost: Boolean): List[Option[Int]] = {
|
||||
val fromPage = 1 max (page - length)
|
||||
val toPage = nbPages.min(page + length)
|
||||
val pre = fromPage match {
|
||||
case 1 => Nil
|
||||
case 2 => List(1.some)
|
||||
case _ => List(1.some, none)
|
||||
}
|
||||
val post = toPage match {
|
||||
case x if x == nbPages => Nil
|
||||
case x if x == nbPages - 1 => List(nbPages.some)
|
||||
case _ if showPost => List(none, nbPages.some)
|
||||
case _ => List(none)
|
||||
}
|
||||
pre ::: (fromPage to toPage).view.map(Some.apply).toList ::: post
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package views.html
|
||||
package forum
|
||||
|
||||
import play.api.mvc.Call
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.paginator.Paginator
|
||||
|
||||
import controllers.routes
|
||||
|
||||
|
@ -19,21 +16,6 @@ object bits {
|
|||
)
|
||||
)
|
||||
|
||||
def pagination(route: Call, pager: Paginator[_], showPost: Boolean) =
|
||||
pager.hasToPaginate option {
|
||||
def url(page: Int) = s"$route?page=$page"
|
||||
st.nav(cls := "pagination")(
|
||||
if (pager.hasPreviousPage) a(href := url(pager.previousPage.get), dataIcon := "I")
|
||||
else span(cls := "disabled", dataIcon := "I"),
|
||||
pager.sliding(3, showPost = showPost).map {
|
||||
case None => raw(" … ")
|
||||
case Some(p) if p == pager.currentPage => span(cls := "current")(p)
|
||||
case Some(p) => a(href := url(p))(p)
|
||||
},
|
||||
if (pager.hasNextPage) a(rel := "next", href := url(pager.nextPage.get), dataIcon := "H")
|
||||
else span(cls := "disabled", dataIcon := "H")
|
||||
)
|
||||
}
|
||||
private[forum] val dataTopic = attr("data-topic")
|
||||
private[forum] val dataUnsub = attr("data-unsub")
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ object categ {
|
|||
)
|
||||
)
|
||||
val bar = div(cls := "bar")(
|
||||
bits.pagination(routes.ForumCateg.show(categ.slug, 1), topics, showPost = false),
|
||||
views.html.base.bits.paginationByQuery(routes.ForumCateg.show(categ.slug, 1), topics, showPost = false),
|
||||
newTopicButton
|
||||
)
|
||||
|
||||
|
|
|
@ -93,7 +93,8 @@ object topic {
|
|||
.some
|
||||
) {
|
||||
val teamOnly = categ.team.filterNot(myTeam)
|
||||
val pager = bits.pagination(routes.ForumTopic.show(categ.slug, topic.slug, 1), posts, showPost = true)
|
||||
val pager = views.html.base.bits
|
||||
.paginationByQuery(routes.ForumTopic.show(categ.slug, topic.slug, 1), posts, showPost = true)
|
||||
|
||||
main(cls := "forum forum-topic page-small box box-pad")(
|
||||
h1(
|
||||
|
|
|
@ -9,9 +9,14 @@ import lila.app.templating.Environment._
|
|||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.String.html.safeJsonValue
|
||||
import lila.swiss.{ Swiss, SwissCondition }
|
||||
import lila.swiss.SwissRound
|
||||
import lila.common.paginator.Paginator
|
||||
import lila.swiss.SwissPairing
|
||||
|
||||
object show {
|
||||
|
||||
private def fullName(s: Swiss) = s"${s.name} by ${teamIdToName(s.teamId)}"
|
||||
|
||||
def apply(
|
||||
s: Swiss,
|
||||
verdicts: SwissCondition.All.WithVerdicts,
|
||||
|
@ -23,7 +28,7 @@ object show {
|
|||
val isDirector = ctx.userId.has(s.createdBy)
|
||||
val hasScheduleInput = isDirector && s.settings.manualRounds && s.isNotFinished
|
||||
views.html.base.layout(
|
||||
title = s"${s.name} #${s.id}",
|
||||
title = fullName(s),
|
||||
moreJs = frag(
|
||||
jsModule("swiss"),
|
||||
hasScheduleInput option jsModule("flatpickr"),
|
||||
|
@ -54,7 +59,7 @@ object show {
|
|||
chessground = false,
|
||||
openGraph = lila.app.ui
|
||||
.OpenGraph(
|
||||
title = s"${s.name}: ${s.variant.name} ${s.clock.show} #${s.id}",
|
||||
title = s"${fullName(s)}: ${s.variant.name} ${s.clock.show} #${s.id}",
|
||||
url = s"$netBaseUrl${routes.Swiss.show(s.id.value).url}",
|
||||
description =
|
||||
s"${s.nbPlayers} players compete in the ${showEnglishDate(s.startsAt)} ${s.name} swiss tournament " +
|
||||
|
@ -73,4 +78,36 @@ object show {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
def round(s: Swiss, r: SwissRound.Number, pairings: Paginator[SwissPairing])(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
title = s"${fullName(s)} • Round $r/${s.round}",
|
||||
moreCss = cssTag("swiss.show"),
|
||||
moreJs = infiniteScrollTag
|
||||
) {
|
||||
val pager = views.html.base.bits
|
||||
.pagination(p => routes.Swiss.round(s.id.value, p).url, r.value, s.round.value, showPost = true)
|
||||
main(cls := "box swiss__round")(
|
||||
h1(
|
||||
a(href := routes.Swiss.show(s.id.value))(s.name),
|
||||
s" • Round $r/${s.round}"
|
||||
),
|
||||
pager(cls := "pagination--top"),
|
||||
table(cls := "slist slist-pad")(
|
||||
tbody(cls := "infinite-scroll")(
|
||||
pairings.currentPageResults map { p =>
|
||||
tr(cls := "paginated")(
|
||||
td(a(href := routes.Round.watcher(p.gameId, "white"), cls := "glpt")(s"#${p.gameId}")),
|
||||
td(userIdLink(p.white.some)),
|
||||
td(p strResultOf chess.White),
|
||||
td(p strResultOf chess.Black),
|
||||
td(userIdLink(p.black.some))
|
||||
)
|
||||
},
|
||||
pagerNextTable(pairings, p => routes.Swiss.round(s.id.value, r.value).url)
|
||||
)
|
||||
),
|
||||
pager(cls := "pagination--bottom")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -279,6 +279,7 @@ GET /swiss controllers.Swiss.home
|
|||
GET /swiss/new/:teamId controllers.Swiss.form(teamId: String)
|
||||
POST /swiss/new/:teamId controllers.Swiss.create(teamId: String)
|
||||
GET /swiss/$id<\w{8}> controllers.Swiss.show(id: String)
|
||||
GET /swiss/$id<\w{8}>/round/:round controllers.Swiss.round(id: String, round: Int)
|
||||
GET /swiss/$id<\w{8}>.trf controllers.Swiss.exportTrf(id: String)
|
||||
POST /swiss/$id<\w{8}>/join controllers.Swiss.join(id: String)
|
||||
POST /swiss/$id<\w{8}>/withdraw controllers.Swiss.withdraw(id: String)
|
||||
|
|
|
@ -54,6 +54,8 @@ final class Env(
|
|||
|
||||
val api: SwissApi = wire[SwissApi]
|
||||
|
||||
lazy val roundPager = wire[SwissRoundPager]
|
||||
|
||||
private def teamOf = api.teamOf _
|
||||
|
||||
private lazy val socket = wire[SwissSocket]
|
||||
|
|
|
@ -24,6 +24,7 @@ case class SwissPairing(
|
|||
def whiteWins = status == Right(Some(Color.White))
|
||||
def blackWins = status == Right(Some(Color.Black))
|
||||
def isDraw = status == Right(None)
|
||||
def strResultOf(color: Color) = status.fold(_ => "*", _.fold("1/2")(c => if (c == color) "1" else "0"))
|
||||
}
|
||||
|
||||
object SwissPairing {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package lila.swiss
|
||||
|
||||
import reactivemongo.api.ReadPreference
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
import lila.common.config
|
||||
import lila.common.paginator.Paginator
|
||||
import lila.db.dsl._
|
||||
import lila.db.paginator.Adapter
|
||||
|
||||
final class SwissRoundPager(colls: SwissColls)(implicit ec: ExecutionContext) {
|
||||
|
||||
import BsonHandlers._
|
||||
|
||||
private val maxPerPage = config.MaxPerPage(50)
|
||||
|
||||
def apply(swiss: Swiss, round: SwissRound.Number, page: Int): Fu[Paginator[SwissPairing]] =
|
||||
Paginator(
|
||||
adapter = new Adapter[SwissPairing](
|
||||
collection = colls.pairing,
|
||||
selector = SwissPairing.fields { f =>
|
||||
$doc(f.swissId -> swiss.id, f.round -> round)
|
||||
},
|
||||
projection = none,
|
||||
sort = $empty,
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage
|
||||
)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.forum .pagination {
|
||||
.pagination {
|
||||
color: $c-font-dimmer;
|
||||
display: flex;
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
@import '../../../common/css/plugin';
|
||||
@import '../../../common/css/component/slist';
|
||||
@import '../../../common/css/component/pagination';
|
||||
@import '../../../common/css/form/form3';
|
||||
@import '../../../common/css/form/cmn-toggle';
|
||||
@import '../../../common/css/form/captcha';
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
@import 'post';
|
||||
@import 'completion';
|
||||
@import 'table';
|
||||
@import 'pagination';
|
||||
@import 'mention';
|
||||
@import 'reactions';
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
.pagination {
|
||||
margin-left: var(--box-padding);
|
||||
&--top {
|
||||
padding-bottom: 1em;
|
||||
border-bottom: $border;
|
||||
}
|
||||
&--bottom {
|
||||
padding: 1em 0;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ $mq-col3: $mq-col3-uniboard;
|
|||
@import 'app-standing';
|
||||
@import 'stats';
|
||||
@import 'player-info';
|
||||
@import 'round-pairings';
|
||||
|
||||
.swiss {
|
||||
.pull-quote {
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
background: $c-bg-box;
|
||||
padding: 1.7em 0;
|
||||
align-self: flex-start;
|
||||
text-align: center;
|
||||
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
|
@ -21,4 +22,8 @@
|
|||
text-align: right;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
&__links {
|
||||
margin-top: 2em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
@import '../../../common/css/component/context-streamer';
|
||||
@import '../../../common/css/component/now-playing';
|
||||
@import '../../../common/css/component/podium';
|
||||
@import '../../../common/css/component/pagination';
|
||||
@import '../../../chat/css/chat';
|
||||
@import '../show';
|
||||
|
|
|
@ -244,6 +244,17 @@ function stats(ctrl: SwissCtrl): VNode | undefined {
|
|||
numberRow(noarg('byes'), [s.byes, slots], 'percent'),
|
||||
numberRow(noarg('absences'), [s.absences, slots], 'percent'),
|
||||
]),
|
||||
h('div.swiss__stats__links', [
|
||||
h(
|
||||
'a',
|
||||
{
|
||||
attrs: {
|
||||
href: `/swiss/${ctrl.data.id}/round/1`,
|
||||
},
|
||||
},
|
||||
`View all ${ctrl.data.round} rounds`
|
||||
),
|
||||
]),
|
||||
])
|
||||
: undefined;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue