templating refactoring

more-scalatags
Thibault Duplessis 2019-04-08 16:54:45 +07:00
parent b1315d97db
commit a410c7b905
29 changed files with 173 additions and 199 deletions

View File

@ -6,6 +6,7 @@ import play.api.mvc.RequestHeader
import play.twirl.api.Html
import lila.api.Context
import lila.app.ui.ScalatagsTemplate._
import lila.common.{ AssetVersion, ContentSecurityPolicy }
trait AssetHelper { self: I18nHelper with SecurityHelper =>
@ -26,25 +27,25 @@ trait AssetHelper { self: I18nHelper with SecurityHelper =>
def dbImageUrl(path: String) = s"$assetBaseUrl/image/$path"
def responsiveCssTag(name: String)(implicit ctx: Context): Html =
cssAt(s"css/$name.${ctx.currentBg}.${if (isProd) "min" else "dev"}.css")
def responsiveCssTag(name: String)(implicit ctx: Context): Frag =
responsiveCssTagWithTheme(name, ctx.currentBg)
def responsiveCssTagNoTheme(name: String)(implicit ctx: Context): Html =
def responsiveCssTagWithTheme(name: String, theme: String): Frag =
cssAt(s"css/$name.$theme.${if (isProd) "min" else "dev"}.css")
def responsiveCssTagNoTheme(name: String)(implicit ctx: Context): Frag =
cssAt(s"css/$name.${if (isProd) "min" else "dev"}.css")
def cssTag(name: String): Html = cssAt("stylesheets/" + name)
def cssTag(name: String): Frag = cssAt("stylesheets/" + name)
def cssTags(names: String*): Html = Html {
names.map { name =>
cssTag(name).body
} mkString ""
}
def cssTags(names: List[(String, Boolean)]): Html =
def cssTags(names: String*): Frag = names map cssTag
def cssTags(names: List[(String, Boolean)]): Frag =
cssTags(names.collect { case (k, true) => k }: _*)
def cssVendorTag(name: String) = cssAt("vendor/" + name)
def cssVendorTag(name: String): Frag = cssAt("vendor/" + name)
def cssAt(path: String): Html = Html {
def cssAt(path: String): Frag = raw {
s"""<link href="${assetUrl(path)}" type="text/css" rel="stylesheet"/>"""
}

View File

@ -20,18 +20,12 @@ object bits {
def layout(
title: String,
side: Option[Frag] = None,
chat: Option[Frag] = None,
underchat: Option[Frag] = None,
moreCss: Html = emptyHtml,
moreJs: Html = emptyHtml,
moreCss: Frag = emptyFrag,
moreJs: Frag = emptyFrag,
openGraph: Option[lila.app.ui.OpenGraph] = None
)(body: Html)(implicit ctx: Context): Frag =
views.html.base.layout(
title = title,
side = side.map(_.toHtml),
chat = chat,
underchat = underchat,
moreCss = moreCss,
moreJs = moreJs,
responsive = true,

View File

@ -28,7 +28,7 @@ object embed {
metaCsp(none),
st.headTitle(s"${playerText(pov.game.whitePlayer)} vs ${playerText(pov.game.blackPlayer)} in ${pov.gameId} : ${pov.game.opening.fold(trans.analysis.txt())(_.opening.ecoName)}"),
fontStylesheets,
currentBgCss,
// currentBgCss,
cssTags("common.css", "board.css", "analyse.css", "analyse-embed.css"),
pieceSprite
),
@ -75,7 +75,7 @@ i18n: ${views.html.board.userAnalysisI18n(withCeval = false, withExplorer = fals
metaCsp(none),
st.headTitle("404 - Game not found"),
fontStylesheets,
currentBgCss,
// currentBgCss,
cssTags("common.css", "analyse-embed.css")
),
body(cls := bodyClass)(

View File

@ -23,9 +23,6 @@ object replayBot {
views.html.analyse.bits.layout(
title = s"${playerText(pov.player)} vs ${playerText(pov.opponent)} in $gameId : ${game.opening.fold(trans.analysis.txt())(_.opening.ecoName)}",
side = views.html.game.side(pov, initialFen, none, simul = simul, bookmarked = false),
chat = none,
underchat = Some(views.html.game.bits.watchers),
moreCss = cssTag("analyse.css"),
openGraph = povOpenGraph(pov).some
) {

View File

@ -17,17 +17,15 @@ object layout {
val topComment = raw("""<!-- Lichess is open source! See https://github.com/ornicar/lila -->""")
val charset = raw("""<meta charset="utf-8">""")
val viewport = raw("""<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"/>""")
def metaCsp(csp: Option[ContentSecurityPolicy])(implicit ctx: Context): Option[Frag] =
def metaCsp(csp: ContentSecurityPolicy): Option[Frag] =
cspEnabled() option raw(
s"""<meta http-equiv="Content-Security-Policy" content="${csp.getOrElse(defaultCsp)}">"""
s"""<meta http-equiv="Content-Security-Policy" content="$csp">"""
)
def currentBgCss(implicit ctx: Context) = ctx.currentBg match {
case "dark" => cssTag("dark.css")
case "transp" => cssTags("dark.css", "transp.css")
case _ => emptyHtml
}
def pieceSprite(implicit ctx: Context) =
link(id := "piece-sprite", href := assetUrl(s"stylesheets/piece/${ctx.currentPieceSet}.css"), `type` := "text/css", rel := "stylesheet")
def metaCsp(csp: Option[ContentSecurityPolicy])(implicit ctx: Context): Option[Frag] =
metaCsp(csp.getOrElse(defaultCsp))
def pieceSprite(implicit ctx: Context): Frag = pieceSprite(ctx.currentPieceSet)
def pieceSprite(ps: lila.pref.PieceSet): Frag =
link(id := "piece-sprite", href := assetUrl(s"stylesheets/piece/$ps.css"), tpe := "text/css", rel := "stylesheet")
val fontStylesheets = raw(List(
"""<link href="https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Roboto:300" rel="stylesheet">""",
"""<link href="https://fonts.googleapis.com/css?family=Roboto+Mono:500&text=0123456789:." rel="stylesheet">"""
@ -103,13 +101,13 @@ object layout {
def apply(
title: String,
fullTitle: Option[String] = None,
side: Option[Html] = None,
menu: Option[Html] = None,
chat: Option[Frag] = None,
underchat: Option[Frag] = None,
// side: Option[Html] = None,
// menu: Option[Html] = None,
// chat: Option[Frag] = None,
// underchat: Option[Frag] = None,
robots: Boolean = isGloballyCrawlable,
moreCss: Html = emptyHtml,
moreJs: Html = emptyHtml,
moreCss: Frag = emptyFrag,
moreJs: Frag = emptyFrag,
playing: Boolean = false,
openGraph: Option[lila.app.ui.OpenGraph] = None,
chessground: Boolean = true,
@ -157,9 +155,9 @@ object layout {
st.body(
cls := List(
"preload base" -> true,
ctx.currentTheme.cssClass -> true,
// ctx.currentTheme.cssClass -> true,
// (if (ctx.currentBg == "transp") "dark transp" else ctx.currentBg) -> true,
ctx.currentTheme3d.cssClass -> true,
(if (ctx.currentBg == "transp") "dark transp" else ctx.currentBg) -> true,
ctx.currentPieceSet3d.toString -> true,
"piece_letter" -> ctx.pref.pieceNotationIsLetter,
"zen" -> ctx.pref.isZen,

View File

@ -32,13 +32,6 @@ object userAnalysis {
)
},explorer:{endpoint:"$explorerEndpoint",tablebaseEndpoint:"$tablebaseEndpoint"}};""")
),
side = pov.game.synthetic option views.html.base.bits.mselect(
"analyse-variant",
span(dataIcon := iconByVariant(pov.game.variant))(pov.game.variant.name),
chess.variant.Variant.all.filter(chess.variant.FromPosition !=).map { v =>
a(dataIcon := iconByVariant(v), href := routes.UserAnalysis.parse(v.key))(v.name)
}
),
responsive = true,
chessground = false,
openGraph = lila.app.ui.OpenGraph(
@ -50,6 +43,13 @@ object userAnalysis {
) {
main(cls := "analyse")(
st.aside(cls := "analyse__side")(spinner),
// side = pov.game.synthetic option views.html.base.bits.mselect(
// "analyse-variant",
// span(dataIcon := iconByVariant(pov.game.variant))(pov.game.variant.name),
// chess.variant.Variant.all.filter(chess.variant.FromPosition !=).map { v =>
// a(dataIcon := iconByVariant(v), href := routes.UserAnalysis.parse(v.key))(v.name)
// }
// ),
div(cls := "analyse__board main-board")(chessgroundSvg),
div(cls := "analyse__tools"),
div(cls := "analyse__controls")

View File

@ -13,7 +13,6 @@ object index {
def apply(data: Option[play.api.libs.json.JsValue])(implicit ctx: Context) = views.html.base.layout(
title = s"${trans.learn.learnChess.txt()} - ${trans.learn.byPlaying.txt()}",
side = Some(div(id := "learn_side", cls := "side_box")),
moreJs = frag(
jsAt(s"compiled/lichess.learn${isProd ?? (".min")}.js"),
embedJs(s"""$$(function() {

View File

@ -2,20 +2,9 @@
@import lila.hub.actorApi.shutup.PublicSource
@moreCss = {
@cssTag("mod-communication.css")
}
@side = {
<br />
<br />
<a data-icon="i" class="text" href="@routes.Report.list">Return to the report list</a>
}
@base.layout(
title = u.username + " communications",
moreCss = moreCss,
side = side.some) {
moreCss = cssTag("mod-communication.css")) {
<div id="communication" class="content_box">
<h1>

View File

@ -10,12 +10,8 @@ lichess.checkout("@stripePublicKey");
}
}
@side = {
}
@base.layout(
title = title,
side = side.some,
moreCss = responsiveCssTag("plan"),
moreJs = moreJs,
responsive = true,

View File

@ -12,23 +12,23 @@ object index {
def apply(data: lila.practice.UserPractice)(implicit ctx: Context) = views.html.base.layout(
title = "Practice chess positions",
side = Some(
div(id := "practice_side", cls := "side_box")(
div(cls := "home")(
i(cls := "fat"),
h1("Practice"),
h2("makes your chess perfect"),
div(cls := "progress")(
div(cls := "text")("Progress: ", data.progressPercent, "%"),
div(cls := "bar", style := s"width: ${data.progressPercent}%")
),
form(id := "practice_reset", cls := "actions", action := routes.Practice.reset, method := "post")(
if (ctx.isAuth) (data.nbDoneChapters > 0) option a(cls := "do-reset")("Reset my progress")
else a(href := routes.Auth.signup)("Sign up to save your progress")
)
)
)
),
// side = Some(
// div(id := "practice_side", cls := "side_box")(
// div(cls := "home")(
// i(cls := "fat"),
// h1("Practice"),
// h2("makes your chess perfect"),
// div(cls := "progress")(
// div(cls := "text")("Progress: ", data.progressPercent, "%"),
// div(cls := "bar", style := s"width: ${data.progressPercent}%")
// ),
// form(id := "practice_reset", cls := "actions", action := routes.Practice.reset, method := "post")(
// if (ctx.isAuth) (data.nbDoneChapters > 0) option a(cls := "do-reset")("Reset my progress")
// else a(href := routes.Auth.signup)("Sign up to save your progress")
// )
// )
// )
// ),
moreCss = cssTag("practice.css"),
moreJs = embedJs(s"""$$('#practice_reset .do-reset').on('click', function() {
if (confirm('You will lose your practice progress!')) this.parentNode.submit();

View File

@ -15,7 +15,7 @@ object show {
data: lila.practice.JsonView.JsData
)(implicit ctx: Context) = views.html.base.layout(
title = us.practiceStudy.name,
side = div(cls := "side_box study_box").toHtml.some,
// side = div(cls := "side_box study_box").toHtml.some,
moreCss = cssTags("analyse.css", "study.css", "practice.css"),
moreJs = frag(
analyseTag,

View File

@ -20,17 +20,17 @@ object show {
streams: List[lila.streamer.Stream]
)(implicit ctx: Context) = views.html.base.layout(
title = r.name,
side = Some(frag(
div(cls := "side_box study_box"),
streams.map { s =>
a(href := routes.Streamer.show(s.streamer.userId), cls := "context-streamer text side_box", dataIcon := "")(
usernameOrId(s.streamer.userId),
" is streaming"
)
}
)),
chat = chat.frag.some,
underchat = Some(views.html.game.bits.watchers),
// side = Some(frag(
// div(cls := "side_box study_box"),
// streams.map { s =>
// a(href := routes.Streamer.show(s.streamer.userId), cls := "context-streamer text side_box", dataIcon := "")(
// usernameOrId(s.streamer.userId),
// " is streaming"
// )
// }
// )),
// chat = chat.frag.some,
// underchat = Some(views.html.game.bits.watchers),
moreCss = cssTags("analyse.css", "study.css", "relay.css", "chat.css"),
moreJs = frag(
analyseTag,

View File

@ -21,12 +21,11 @@ object show {
responsive = true,
moreCss = responsiveCssTag("simul.show"),
title = sim.fullName,
underchat = Some(div(
cls := "watchers none",
aria.live := "off",
aria.relevant := "additions removals text"
)(span(cls := "list inline_userlist"))),
chat = views.html.chat.frag.some,
// underchat = Some(div(
// cls := "watchers none",
// aria.live := "off",
// aria.relevant := "additions removals text"
// )(span(cls := "list inline_userlist"))),
moreJs = frag(
jsAt(s"compiled/lichess.simul${isProd ?? (".min")}.js"),
embedJs(s"""lichess.simul={

View File

@ -1,8 +1,6 @@
package views
package html.site
import play.twirl.api.Html
import controllers.routes
import lila.api.Context
import lila.app.templating.Environment._
@ -15,7 +13,7 @@ object message {
title: String,
back: Boolean = true,
icon: Option[String] = None,
moreCss: Option[Html] = None
moreCss: Option[Frag] = None
)(message: Frag)(implicit ctx: Context) =
views.html.base.layout(title = title, moreCss = ~moreCss, responsive = true) {
main(cls := "box box-pad")(

View File

@ -28,7 +28,7 @@ object embed {
metaCsp(none),
st.headTitle(s"${s.name} ${chapter.name}"),
fontStylesheets,
currentBgCss,
// currentBgCss,
cssTags("common.css", "board.css", "analyse.css", "analyse-embed.css"),
pieceSprite
),
@ -75,7 +75,7 @@ userId: null
metaCsp(none),
st.headTitle("404 - Study not available"),
fontStylesheets,
currentBgCss,
// currentBgCss,
cssTags("common.css", "analyse-embed.css")
),
body(cls := bodyClass)(

View File

@ -121,14 +121,14 @@ object list {
searchFilter: String
)(titleFrag: Frag)(implicit ctx: Context) = views.html.base.layout(
title = title,
menu = Some(frag(
a(
cls := active.active("all"),
href := routes.Study.all(order.key)
)("All studies"),
ctx.me.map { bits.authLinks(_, active, order) },
a(cls := "text", dataIcon := "", href := "//lichess.org/blog/V0KrLSkAAMo3hsi4/study-chess-the-lichess-way")("What are studies?")
)),
// menu = Some(frag(
// a(
// cls := active.active("all"),
// href := routes.Study.all(order.key)
// )("All studies"),
// ctx.me.map { bits.authLinks(_, active, order) },
// a(cls := "text", dataIcon := "", href := "//lichess.org/blog/V0KrLSkAAMo3hsi4/study-chess-the-lichess-way")("What are studies?")
// )),
moreCss = cssTag("studyList.css"),
moreJs = infiniteScrollTag
) {

View File

@ -16,11 +16,11 @@ object search {
def apply(pag: Paginator[lila.study.Study.WithChaptersAndLiked], text: String)(implicit ctx: Context) =
views.html.base.layout(
title = text,
menu = Some(frag(
a(href := routes.Study.all(Order.default.key))("All studies"),
ctx.me.map { bits.authLinks(_, "search", Order.default) },
a(cls := "text", dataIcon := "", href := "//lichess.org/blog/V0KrLSkAAMo3hsi4/study-chess-the-lichess-way")("What are studies?")
)),
// menu = Some(frag(
// a(href := routes.Study.all(Order.default.key))("All studies"),
// ctx.me.map { bits.authLinks(_, "search", Order.default) },
// a(cls := "text", dataIcon := "", href := "//lichess.org/blog/V0KrLSkAAMo3hsi4/study-chess-the-lichess-way")("What are studies?")
// )),
moreCss = cssTag("studyList.css"),
moreJs = infiniteScrollTag
) {

View File

@ -19,20 +19,20 @@ object show {
streams: List[lila.streamer.Stream]
)(implicit ctx: Context) = views.html.base.layout(
title = s.name.value,
side = Some(div(cls := "side_box study_box")(
streams.map { s =>
a(
href := routes.Streamer.show(s.streamer.userId),
cls := "context-streamer text side_box",
dataIcon := ""
)(
usernameOrId(s.streamer.userId),
" is streaming"
)
}
)),
chat = views.html.chat.frag.some,
underchat = Some(views.html.game.bits.watchers),
// side = Some(div(cls := "side_box study_box")(
// streams.map { s =>
// a(
// href := routes.Streamer.show(s.streamer.userId),
// cls := "context-streamer text side_box",
// dataIcon := ""
// )(
// usernameOrId(s.streamer.userId),
// " is streaming"
// )
// }
// )),
// chat = views.html.chat.frag.some,
// underchat = Some(views.html.game.bits.watchers),
moreCss = cssTags("analyse.css", "study.css", "chat.css"),
moreJs = frag(
analyseTag,

View File

@ -10,28 +10,6 @@ import controllers.routes
object bits {
def layout(
title: String,
moreJs: Html = emptyHtml,
moreCss: String,
side: Option[Html] = None,
chat: Option[Frag] = None,
underchat: Option[Frag] = None,
chessground: Boolean = true,
openGraph: Option[lila.app.ui.OpenGraph] = None
)(body: Frag)(implicit ctx: Context) =
views.html.base.layout(
title = title,
responsive = true,
moreCss = responsiveCssTag(moreCss),
moreJs = moreJs,
side = side,
chat = chat,
underchat = underchat,
openGraph = openGraph,
chessground = chessground
)(body)
def miniGame(pov: lila.game.Pov)(implicit ctx: Context) = frag(
gameFen(pov),
div(cls := "vstext")(

View File

@ -22,11 +22,11 @@ object show {
shieldOwner: Option[lila.tournament.TournamentShield.OwnerId]
)(implicit ctx: Context) = views.html.base.layout(
title = s"${tour.fullName} #${tour.id}",
underchat = Some(div(
cls := "watchers hidden",
aria.live := "off",
aria.relevant := "additions removals text"
)(span(cls := "list inline_userlist"))),
// underchat = Some(div(
// cls := "watchers hidden",
// aria.live := "off",
// aria.relevant := "additions removals text"
// )(span(cls := "list inline_userlist"))),
moreJs = frag(
jsAt(s"compiled/lichess.tournament${isProd ?? (".min")}.js"),
embedJs(s"""lichess=lichess||{};lichess.tournament={

View File

@ -0,0 +1,37 @@
package views.html.tv
import play.api.mvc.RequestHeader
import lila.api.Context
import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._
import views.html.base.layout.bits
import controllers.routes
object embed {
private val dataStreamUrl = attr("data-stream-url")
def apply(pov: lila.game.Pov, bg: String, theme: String)(implicit req: RequestHeader) = frag(
bits.doctype,
html(
head(
bits.charset,
bits.metaCsp(basicCsp),
st.headTitle("lichess.org TV"),
bits.pieceSprite(lila.pref.PieceSet.default),
responsiveCssTagWithTheme("tv.embed", bg)
),
body(cls := "base", dataStreamUrl := routes.Tv.feed)(
div(id := "featured_game", title := "lichess.org TV")(
gameFenNoCtx(pov, tv = true, blank = true),
views.html.game.bits.vstext(pov)(none)
),
jQueryTag,
jsAt("javascripts/vendor/chessground.min.js", false),
jsAt("compiled/tv.js", false)
)
)
)
}

View File

@ -1,27 +0,0 @@
@(pov: Pov, bg: String, theme: String)(implicit req: RequestHeader)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="Content-Security-Policy" content="@basicCsp">
<title>lichess.org TV</title>
@if(bg == "dark") {
@cssAt("stylesheets/dark.css")
}
@cssAt(s"stylesheets/piece/merida.css")
@cssAt("stylesheets/common.css")
@cssAt("stylesheets/board.css")
</head>
<body
class="base highlight @bg"
style="width: 224px; height: 264px; overflow: hidden; background: none!important"
data-stream-url="@routes.Tv.feed">
<div id="featured_game" class="is2d highlight @bg @theme merida" title="lichess.org TV">
@gameFenNoCtx(pov, tv = true, blank = true).toHtml
@game.bits.vstext(pov)(none).toHtml
</div>
@jQueryTag
@jsAt("javascripts/vendor/chessground.min.js", false)
@jsAt("compiled/tv.js", false)
</body>
</html>

View File

@ -0,0 +1,13 @@
/* Common imports for all embedded widgets */
@import '../../../node_modules/breakpoint-sass/stylesheets/breakpoint';
@import 'abstract/all';
@import 'base/elements';
@import 'base/fonts';
@import 'base/typography';
@import 'base/data-icon';
@include theme-style;
@import 'vendor/chessground/chessground';

View File

@ -25,12 +25,3 @@ h1 {
h2 {
@include fluid-size('font-size', 23px, 40px);
}
// h3 {
// font-weight: bold;
// color: $c-font-dim;
// @include fluid-size('font-size', 20px, 30px);
// }
// h4 {
// color: $c-font-dim;
// @include fluid-size('font-size', 20px, 26px);
// }

View File

@ -64,7 +64,7 @@ module.exports = function(cfg, element) {
filterStreams();
},
featured: function(o) {
$('#featured_game').html(o.html);
$('.lobby__tv').html(o.html);
lichess.pubsub.emit('content_loaded')();
},
redirect: function(e) {

View File

@ -0,0 +1,3 @@
@import '../../../common/css/embed';
@import 'component/board';
@import '../tv/embed';

View File

View File

@ -24,7 +24,7 @@ function parseFen($elem) {
});
}
$(function() {
var $featured = $('#featured_game');
var $featured = $('#featured-game');
var board = function() {
return $featured.find('.mini-board');
};
@ -34,7 +34,7 @@ $(function() {
source.addEventListener('message', function(e) {
var data = JSON.parse(e.data);
if (data.t == "featured") {
$('#featured_game').html(data.d.html).find('a').attr('target', '_blank');
$('#featured-game').html(data.d.html).find('a').attr('target', '_blank');
parseFen(board());
} else if (data.t == "fen") {
parseFen(board().data("fen", data.d.fen).data("lastmove", data.d.lm));

8
v2-todo 100644
View File

@ -0,0 +1,8 @@
analysis replay-bot
all embeds
user analysis variant selection
relay
practice
study
simul underchat (show.scala)
tournament underchat (show.scala)