Skip to content

Commit

Permalink
Euro 2024 atom header (#27220)
Browse files Browse the repository at this point in the history
Make the Euro 2024 stand out by adding the custom header
on selected sports pages:

/football/euro-2024/live
/football/euro-2024/overview
/football/euro-2024/table
/football/euro-2024/fixtures

Co-Authored-By: Dom Lander <[email protected]>
Co-authored-by: Max Duval <[email protected]>
  • Loading branch information
domlander and mxdvl authored Jun 13, 2024
1 parent 5296948 commit 35551a2
Show file tree
Hide file tree
Showing 17 changed files with 184 additions and 58 deletions.
11 changes: 11 additions & 0 deletions common/app/conf/switches/JournalismSwitches.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package conf.switches

import conf.switches.Expiry.never
import java.time.LocalDate

trait JournalismSwitches {

Expand Down Expand Up @@ -73,4 +74,14 @@ trait JournalismSwitches {
sellByDate = never,
exposeClientSide = true,
)

val Euro2024Header = Switch(
SwitchGroup.Journalism,
name = "euro-2024-header",
description = "Show the Euro 2024 interactive atom header on football pages",
owners = Seq(Owner.withEmail("[email protected]")),
safeState = Off,
sellByDate = LocalDate.of(2024, 7, 15),
exposeClientSide = true,
)
}
2 changes: 1 addition & 1 deletion common/app/views/fragments/atoms/interactive.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
@if(shouldFence) {
<iframe class="interactive-atom-fence" srcdoc="@iframeBody.toString"></iframe>
} else {
<figure class="interactive interactive-atom">
<figure class="interactive interactive-atom" style="margin: 0;">
<style>
@HtmlFormat.raw(interactive.css)
</style>
Expand Down
35 changes: 25 additions & 10 deletions sport/app/football/controllers/FixturesController.scala
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
package football.controllers

import common.Edition
import common.ImplicitControllerExecutionContext
import feed.CompetitionsService
import football.model._
import model._
import model.content.InteractiveAtom
import contentapi.ContentApiClient
import java.time.LocalDate
import pa.FootballTeam
import play.api.mvc.{Action, AnyContent, ControllerComponents}
import scala.concurrent.Future
import conf.switches.Switches

class FixturesController(
val competitionsService: CompetitionsService,
val controllerComponents: ControllerComponents,
val contentApiClient: ContentApiClient,
)(implicit context: ApplicationContext)
extends MatchListController
with CompetitionFixtureFilters {
with CompetitionFixtureFilters
with ImplicitControllerExecutionContext {

private def fixtures(date: LocalDate): FixturesList = FixturesList(date, competitionsService.competitions)
private val page = new FootballPage("football/fixtures", "football", "All fixtures")
Expand Down Expand Up @@ -80,14 +87,22 @@ class FixturesController(

private def renderTagFixtures(date: LocalDate, tag: String): Action[AnyContent] =
getTagFixtures(date, tag)
.map(result =>
Action { implicit request =>
renderMatchList(
result._1,
result._2,
filters,
)
},
)
.map {
case (page, fixtures) =>
Action.async { implicit request =>
tag match {
case "euro-2024" if Switches.Euro2024Header.isSwitchedOn =>
val id = "/atom/interactive/interactives/2023/01/euros-2024/tables-euros-2024-header"
val edition = Edition(request)
contentApiClient
.getResponse(contentApiClient.item(id, edition))
.map(_.interactive.map(InteractiveAtom.make(_)))
.recover { case _ => None }
.map(renderMatchList(page, fixtures, filters, _))
case _ =>
Future.successful(renderMatchList(page, fixtures, filters, None))
}
}
}
.getOrElse(Action(NotFound))
}
55 changes: 42 additions & 13 deletions sport/app/football/controllers/LeagueTableController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@ import conf.switches.Switches
import feed.CompetitionsService
import model._
import play.api.mvc.{Action, AnyContent, BaseController, ControllerComponents}
import model.content.InteractiveAtom
import contentapi.ContentApiClient
import scala.concurrent.Future

case class TablesPage(
page: Page,
tables: Seq[Table],
urlBase: String,
filters: Map[String, Seq[CompetitionFilter]] = Map.empty,
comp: Option[Competition],
atom: Option[InteractiveAtom] = None,
) {
lazy val singleCompetition = tables.size == 1
}

class LeagueTableController(
val competitionsService: CompetitionsService,
val controllerComponents: ControllerComponents,
val contentApiClient: ContentApiClient,
)(implicit context: ApplicationContext)
extends BaseController
with GuLogging
Expand Down Expand Up @@ -80,10 +85,12 @@ class LeagueTableController(

val htmlResponse =
() =>
football.views.html.tablesList.tablesPage(TablesPage(page, groups, "/football", filters(tableOrder), None))
football.views.html.tablesList
.tablesPage(TablesPage(page, groups, "/football", filters(tableOrder), None))
val jsonResponse =
() =>
football.views.html.tablesList.tablesPage(TablesPage(page, groups, "/football", filters(tableOrder), None))
football.views.html.tablesList
.tablesPage(TablesPage(page, groups, "/football", filters(tableOrder), None))
renderFormat(htmlResponse, jsonResponse, page, Switches.all)

}
Expand Down Expand Up @@ -115,7 +122,7 @@ class LeagueTableController(

def renderCompetitionJson(competition: String): Action[AnyContent] = renderCompetition(competition)
def renderCompetition(competition: String): Action[AnyContent] =
Action { implicit request =>
Action.async { implicit request =>
val table = loadTables
.find(_.competition.url.endsWith(s"/$competition"))
.orElse(loadTables.find(_.competition.id == competition))
Expand All @@ -127,13 +134,30 @@ class LeagueTableController(
s"${table.competition.fullName} table",
)

val futureAtom = if (Switches.Euro2024Header.isSwitchedOn && competition == "euro-2024") {
val id = "/atom/interactive/interactives/2023/01/euros-2024/tables-euros-2024-header"
val edition = Edition(request)
contentApiClient
.getResponse(contentApiClient.item(id, edition))
.map(_.interactive.map(InteractiveAtom.make(_)))
.recover { case _ => None }
} else Future.successful(None)

val smallTableGroup =
table.copy(groups = table.groups.map { group => group.copy(entries = group.entries.take(10)) }).groups(0)
val htmlResponse = () =>
football.views.html.tablesList
.tablesPage(
TablesPage(page, Seq(table), table.competition.url, filters(tableOrder), Some(table.competition)),
)
val htmlResponse = (atom: Option[InteractiveAtom]) =>
() =>
football.views.html.tablesList
.tablesPage(
TablesPage(
page,
Seq(table),
table.competition.url,
filters(tableOrder),
Some(table.competition),
atom,
),
)
val jsonResponse = () =>
football.views.html.tablesList.tablesComponent(
table.competition,
Expand All @@ -142,14 +166,13 @@ class LeagueTableController(
multiGroup = table.multiGroup,
)

renderFormat(htmlResponse, jsonResponse, page)

futureAtom.map(maybeAtom => renderFormat(htmlResponse(maybeAtom), jsonResponse, page))
}
.getOrElse(
if (request.isJson) {
Cached(60)(JsonNotFound())
Future.successful(Cached(60)(JsonNotFound()))
} else {
Redirect("/football/tables")
Future.successful(Redirect("/football/tables"))
},
)
}
Expand Down Expand Up @@ -181,7 +204,13 @@ class LeagueTableController(
val htmlResponse = () =>
football.views.html.tablesList
.tablesPage(
TablesPage(page, Seq(groupTable), table.competition.url, filters(tableOrder), Some(table.competition)),
TablesPage(
page,
Seq(groupTable),
table.competition.url,
filters(tableOrder),
Some(table.competition),
),
)
val jsonResponse = () =>
football.views.html.tablesList.tablesComponent(
Expand Down
23 changes: 19 additions & 4 deletions sport/app/football/controllers/MatchDayController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ import java.time.LocalDate
import model._
import football.model._
import common.{Edition, JsonComponent}
import contentapi.ContentApiClient
import model.content.InteractiveAtom
import common.ImplicitControllerExecutionContext
import scala.concurrent.Future
import conf.switches.Switches

class MatchDayController(
val competitionsService: CompetitionsService,
val controllerComponents: ControllerComponents,
val contentApiClient: ContentApiClient,
)(implicit context: ApplicationContext)
extends MatchListController
with CompetitionLiveFilters {
with CompetitionLiveFilters
with ImplicitControllerExecutionContext {

def liveMatchesJson(): Action[AnyContent] = liveMatches()
def liveMatches(): Action[AnyContent] =
Expand Down Expand Up @@ -41,18 +48,26 @@ class MatchDayController(
competitionMatchesFor(competitionTag, year, month, day)

private def renderCompetitionMatches(competitionTag: String, date: LocalDate): Action[AnyContent] =
Action { implicit request =>
Action.async { implicit request =>
lookupCompetition(competitionTag)
.map { competition =>
val webTitle =
if (date == LocalDate.now(Edition.defaultEdition.timezoneId)) s"Today's ${competition.fullName} matches"
else s" ${competition.fullName} matches"
val page = new FootballPage(s"football/$competitionTag/live", "football", webTitle)
val matches = CompetitionMatchDayList(competitionsService.competitions, competition.id, date)
renderMatchList(page, matches, filters)
if (Switches.Euro2024Header.isSwitchedOn) {
val id = "/atom/interactive/interactives/2023/01/euros-2024/match-centre-euros-2024-header"
val edition = Edition(request)
contentApiClient
.getResponse(contentApiClient.item(id, edition))
.map(_.interactive.map(InteractiveAtom.make(_)))
.recover { case _ => None }
.map(renderMatchList(page, matches, filters, _))
} else Future.successful(renderMatchList(page, matches, filters))
}
.getOrElse {
NotFound
Future.successful(NotFound)
}
}

Expand Down
8 changes: 6 additions & 2 deletions sport/app/football/controllers/MatchListController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import play.api.mvc.{BaseController, RequestHeader}
import play.twirl.api.Html

import java.time.format.DateTimeFormatter
import model.content.InteractiveAtom

trait MatchListController extends BaseController with Requests {
def competitionsService: Competitions
Expand All @@ -25,23 +26,26 @@ trait MatchListController extends BaseController with Requests {
page: FootballPage,
matchesList: MatchesList,
filters: Map[String, Seq[CompetitionFilter]],
atom: Option[InteractiveAtom] = None,
)(implicit request: RequestHeader, context: ApplicationContext) = {
Cached(10) {
if (request.isJson)
JsonComponent(
"html" -> football.views.html.matchList.matchesComponent(matchesList),
"next" -> Html(matchesList.nextPage.getOrElse("")),
"previous" -> Html(matchesList.previousPage.getOrElse("")),
"atom" -> atom.isDefined,
)
else
RevalidatableResult.Ok(football.views.html.matchList.matchesPage(page, matchesList, filters))
RevalidatableResult.Ok(football.views.html.matchList.matchesPage(page, matchesList, filters, atom))
}
}

protected def renderMoreMatches(
page: FootballPage,
matchesList: MatchesList,
filters: Map[String, Seq[CompetitionFilter]],
atom: Option[InteractiveAtom] = None,
)(implicit request: RequestHeader, context: ApplicationContext) = {
Cached(10) {
if (request.isJson)
Expand All @@ -51,7 +55,7 @@ trait MatchListController extends BaseController with Requests {
"previous" -> Html(matchesList.previousPage.getOrElse("")),
)
else
RevalidatableResult.Ok(football.views.html.matchList.matchesPage(page, matchesList, filters))
RevalidatableResult.Ok(football.views.html.matchList.matchesPage(page, matchesList, filters, atom))
}
}

Expand Down
5 changes: 3 additions & 2 deletions sport/app/football/controllers/ResultsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import model._
import football.model._
import pa.FootballTeam
import model.Competition
import model.content.InteractiveAtom

class ResultsController(
val competitionsService: CompetitionsService,
Expand Down Expand Up @@ -54,13 +55,13 @@ class ResultsController(
}

private def renderWith(
renderFunction: (FootballPage, Results, Map[String, Seq[CompetitionFilter]]) => Result,
renderFunction: (FootballPage, Results, Map[String, Seq[CompetitionFilter]], Option[InteractiveAtom]) => Result,
)(date: LocalDate, tag: Option[String] = None): Result = {
val result = for {
p <- page(tag)
r <- results(date, tag)
} yield {
renderFunction(p, r, filters)
renderFunction(p, r, filters, None)
}
result.getOrElse(NotFound("No results"))
}
Expand Down
40 changes: 29 additions & 11 deletions sport/app/football/controllers/WallchartController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ import football.model.{CompetitionStage, Groups, KnockoutSpider}
import pa.{FootballMatch}

import java.time.ZonedDateTime
import scala.concurrent.Future
import contentapi.ContentApiClient
import conf.switches.Switches
import common.Edition
import model.content.InteractiveAtom

class WallchartController(
competitionsService: CompetitionsService,
val controllerComponents: ControllerComponents,
val contentApiClient: ContentApiClient,
)(implicit context: ApplicationContext)
extends BaseController
with GuLogging
Expand All @@ -28,7 +34,7 @@ class WallchartController(
def renderWallchartEmbed(competitionTag: String): Action[AnyContent] = renderWallchart(competitionTag, true)

def renderWallchart(competitionTag: String, embed: Boolean = false): Action[AnyContent] =
Action { implicit request =>
Action.async { implicit request =>
competitionsService
.competitionsWithTag(competitionTag)
.map { competition =>
Expand All @@ -40,18 +46,30 @@ class WallchartController(
val competitionStages = new CompetitionStage(competitionsService.competitions)
.stagesFromCompetition(competition, KnockoutSpider.orderings)
val nextMatch = WallchartController.nextMatch(competition.matches, ZonedDateTime.now())
Cached(60) {
if (embed)
RevalidatableResult.Ok(
football.views.html.wallchart.embed(page, competition, competitionStages, nextMatch),
)
else
RevalidatableResult.Ok(
football.views.html.wallchart.page(page, competition, competitionStages, nextMatch),
)
val futureAtom = if (Switches.Euro2024Header.isSwitchedOn && competitionTag == "euro-2024") {
val id = "/atom/interactive/interactives/2023/01/euros-2024/tables-euros-2024-header"
val edition = Edition(request)
contentApiClient
.getResponse(contentApiClient.item(id, edition))
.map(_.interactive.map(InteractiveAtom.make(_)))
.recover { case _ => None }
} else Future.successful(None)

futureAtom.map { maybeAtom =>
Cached(60) {
if (embed)
RevalidatableResult.Ok(
football.views.html.wallchart.embed(page, competition, competitionStages, nextMatch),
)
else
RevalidatableResult.Ok(
football.views.html.wallchart.page(page, competition, competitionStages, nextMatch, maybeAtom),
)
}
}

}
.getOrElse(NotFound)
.getOrElse(Future.successful(NotFound))
}

def renderGroupTablesEmbed(competitionTag: String): Action[AnyContent] =
Expand Down
Loading

0 comments on commit 35551a2

Please sign in to comment.