diff --git a/app/views/relay/tour.scala b/app/views/relay/tour.scala index 45f571555a..136516b633 100644 --- a/app/views/relay/tour.scala +++ b/app/views/relay/tour.scala @@ -35,7 +35,7 @@ object tour { ), st.section( active.map { tr => - div(cls := "relay-widget relay-widget--active", dataIcon := "")( + div(cls := s"relay-widget relay-widget--active ${tierClass(tr.tour)}", dataIcon := "")( a(cls := "overlay", href := tr.path), div( h2(tr.tour.name), @@ -54,7 +54,7 @@ object tour { ), st.section(cls := "infinite-scroll")( pager.currentPageResults map { rt => - div(cls := "relay-widget paginated", dataIcon := "")( + div(cls := s"relay-widget ${tierClass(rt.tour)} paginated", dataIcon := "")( a(cls := "overlay", href := rt.path), div( h2(rt.tour.name), @@ -70,4 +70,5 @@ object tour { ) } + private def tierClass(tour: RelayTour) = s"tour-tier--${tour.tier | RelayTour.Tier.NORMAL}" } diff --git a/app/views/relay/tourForm.scala b/app/views/relay/tourForm.scala index 61134c4611..b0707d862e 100644 --- a/app/views/relay/tourForm.scala +++ b/app/views/relay/tourForm.scala @@ -62,11 +62,11 @@ object tourForm { ).some )(form3.textarea(_)(rows := 10)), if (isGranted(_.Relay)) - form3.checkbox( - form("official"), - raw("Official Lichess broadcast"), + form3.group( + form("tier"), + raw("Official Lichess broadcast tier"), help = raw("Feature on /broadcast - for admins only").some - ) - else form3.hidden(form("official")) + )(form3.select(_, RelayTour.Tier.options)) + else form3.hidden(form("tier")) ) } diff --git a/bin/mongodb/relay-tour-tier.js b/bin/mongodb/relay-tour-tier.js new file mode 100644 index 0000000000..33944b340c --- /dev/null +++ b/bin/mongodb/relay-tour-tier.js @@ -0,0 +1,7 @@ +db.relay_tour.update({ official: true }, { $set: { tier: NumberInt(3) }, $unset: { official: true } }, { multi: 1 }); +db.relay_tour.dropIndex('active_1_official_1_syncedAt_-1'); +db.relay_tour.createIndex( + { active: 1, tier: 1 }, + { partialFilterExpression: { active: true, tier: { $exists: true } } } +); +db.relay_tour.createIndex({ syncedAt: -1 }, { partialFilterExpression: { tier: { $exists: true } } }); diff --git a/modules/relay/src/main/RelayApi.scala b/modules/relay/src/main/RelayApi.scala index bc4248dd92..14c112fdb9 100644 --- a/modules/relay/src/main/RelayApi.scala +++ b/modules/relay/src/main/RelayApi.scala @@ -85,6 +85,7 @@ final class RelayApi( .aggregateList(20) { framework => import framework._ Match(tourRepo.selectors.official ++ tourRepo.selectors.active) -> List( + Sort(Descending("tier")), PipelineOperator( $doc( "$lookup" -> $doc( diff --git a/modules/relay/src/main/RelayTour.scala b/modules/relay/src/main/RelayTour.scala index 0397cf3040..9d52130508 100644 --- a/modules/relay/src/main/RelayTour.scala +++ b/modules/relay/src/main/RelayTour.scala @@ -11,9 +11,9 @@ case class RelayTour( markup: Option[String] = None, ownerId: User.ID, createdAt: DateTime, - official: Boolean, - active: Boolean, // a round is scheduled or ongoing - syncedAt: Option[DateTime] // last time a round was synced + tier: Option[RelayTour.Tier], // if present, it's an official broadcast + active: Boolean, // a round is scheduled or ongoing + syncedAt: Option[DateTime] // last time a round was synced ) { def id = _id @@ -23,6 +23,8 @@ case class RelayTour( } def withRounds(rounds: List[RelayRound]) = RelayTour.WithRounds(this, rounds) + + def official = tier.isDefined } object RelayTour { @@ -31,6 +33,23 @@ object RelayTour { case class Id(value: String) extends AnyVal with StringValue + type Tier = Int + object Tier { + val NORMAL = 3 + val HIGH = 4 + val BEST = 5 + + val options = List( + "" -> "Non official", + NORMAL.toString -> "Official: normal tier", + HIGH.toString -> "Official: high tier", + BEST.toString -> "Official: best tier" + ) + def name(tier: Tier) = options.collectFirst { + case (t, n) if t == tier => n + } | "???" + } + case class WithRounds(tour: RelayTour, rounds: List[RelayRound]) case class ActiveWithNextRound(tour: RelayTour, round: RelayRound) extends RelayRound.AndTour { diff --git a/modules/relay/src/main/RelayTourForm.scala b/modules/relay/src/main/RelayTourForm.scala index d28d707cf9..113f1a51e8 100644 --- a/modules/relay/src/main/RelayTourForm.scala +++ b/modules/relay/src/main/RelayTourForm.scala @@ -21,7 +21,7 @@ final class RelayTourForm { "name" -> cleanText(minLength = 3, maxLength = 80), "description" -> cleanText(minLength = 3, maxLength = 400), "markup" -> optional(cleanText(maxLength = 20_000)), - "official" -> optional(boolean) + "tier" -> optional(number(min = RelayTour.Tier.NORMAL, max = RelayTour.Tier.BEST)) )(Data.apply)(Data.unapply) ) @@ -36,7 +36,7 @@ object RelayTourForm { name: String, description: String, markup: Option[String], - official: Option[Boolean] + tier: Option[RelayTour.Tier] ) { def update(tour: RelayTour, user: User) = @@ -44,7 +44,7 @@ object RelayTourForm { name = name, description = description, markup = markup, - official = ~official && Granter(_.Relay)(user) + tier = tier ifTrue Granter(_.Relay)(user) ) def make(user: User) = @@ -54,7 +54,7 @@ object RelayTourForm { description = description, markup = markup, ownerId = user.id, - official = ~official && Granter(_.Relay)(user), + tier = tier ifTrue Granter(_.Relay)(user), active = false, createdAt = DateTime.now, syncedAt = none @@ -68,7 +68,7 @@ object RelayTourForm { name = tour.name, description = tour.description, markup = tour.markup, - official = tour.official option true + tier = tour.tier ) } } diff --git a/modules/relay/src/main/RelayTourRepo.scala b/modules/relay/src/main/RelayTourRepo.scala index 61ed155b1f..1e79739841 100644 --- a/modules/relay/src/main/RelayTourRepo.scala +++ b/modules/relay/src/main/RelayTourRepo.scala @@ -19,7 +19,7 @@ final private class RelayTourRepo(val coll: Coll)(implicit ec: scala.concurrent. def lookup(local: String) = $lookup.simple(coll, "tour", local, "_id") private[relay] object selectors { - val official = $doc("official" -> true) + val official = $doc("tier" $exists true) val active = $doc("active" -> true) val inactive = $doc("active" -> false) } diff --git a/ui/site/css/relay/_widget.scss b/ui/site/css/relay/_widget.scss index d12c6c8bb3..f6eb30281d 100644 --- a/ui/site/css/relay/_widget.scss +++ b/ui/site/css/relay/_widget.scss @@ -2,7 +2,7 @@ @extend %flex-center-nowrap, %break-word; border-bottom: $border; - padding: 2em var(--box-padding); + padding: 2em var(--box-padding) 2em 0; position: relative; @include transition; @@ -10,13 +10,19 @@ &::before { color: $c-font-dimmer; font-size: 5em; - margin-right: 0.4em; + width: 12rem; + text-align: center; @include transition; - @include breakpoint($mq-not-xx-small) { + } + + @include breakpoint($mq-not-xx-small) { + &::before { display: none; } + padding-left: var(--box-padding); } + &--active::before { color: mix($c-link, $c-bg-box, 80%); } @@ -55,4 +61,22 @@ text-align: right; } } + + &.tour-tier { + &--3 { + &::before { + font-size: 3.5em; + } + h2 { + @include fluid-size('font-size', 13px, 23px); + } + } + &--5 { + @extend %box-neat; + border: 3px solid $c-border; + &.relay-widget--active { + border-color: $c-primary; + } + } + } }