From 7755cc919eb1fae1656623908798ec2061c959bb Mon Sep 17 00:00:00 2001 From: TheMadSword <2840135+TheMadSword@users.noreply.github.com> Date: Thu, 27 Jul 2023 08:08:59 -0400 Subject: [PATCH 1/4] +AutoZen (Closes #12537) ~scalafmtAll --- app/templating/SetupHelper.scala | 7 +++++++ app/views/account/bits.scala | 10 +++++----- app/views/account/pref.scala | 2 +- app/views/base/layout.scala | 5 +++-- app/views/round/player.scala | 3 ++- modules/i18n/src/main/I18nKeys.scala | 1 + modules/pref/src/main/Pref.scala | 23 ++++++++++++++++++++--- modules/pref/src/main/PrefForm.scala | 4 ++-- translation/source/preferences.xml | 1 + ui/puzzle/src/ctrl.ts | 4 +++- ui/round/src/ctrl.ts | 10 +++++++++- ui/storm/src/ctrl.ts | 4 +++- 12 files changed, 57 insertions(+), 17 deletions(-) diff --git a/app/templating/SetupHelper.scala b/app/templating/SetupHelper.scala index 229539ad9fa27..15548b570e4ec 100644 --- a/app/templating/SetupHelper.scala +++ b/app/templating/SetupHelper.scala @@ -184,6 +184,13 @@ trait SetupHelper: (Pref.Animation.SLOW, trans.slow.txt()) ) + def translatedZenChoices(using Lang) = + List( + (Pref.Zen.NO, trans.no.txt()), + (Pref.Zen.YES, trans.yes.txt()), + (Pref.Zen.GAME_AUTO, trans.preferences.inGameOnly.txt()) + ) + def translatedBoardCoordinateChoices(using Lang) = List( (Pref.Coords.NONE, trans.no.txt()), diff --git a/app/views/account/bits.scala b/app/views/account/bits.scala index 89c3bd7abd8c3..49deed4267968 100644 --- a/app/views/account/bits.scala +++ b/app/views/account/bits.scala @@ -31,10 +31,10 @@ object bits: def setting(name: Frag, body: Frag) = st.section(h2(name), body) - def radios[A](field: play.api.data.Field, options: Iterable[(A, String)], prefix: String = "ir") = + def radios[A](field: play.api.data.Field, options: Iterable[(A, String)]) = st.group(cls := "radio")( options.map { (key, value) => - val id = s"$prefix${field.id}_$key" + val id = s"ir${field.id}_$key" val checked = field.value has key.toString div( input( @@ -49,12 +49,12 @@ object bits: }.toList ) - def bitCheckboxes(field: play.api.data.Field, options: Iterable[(Int, String)], prefix: String = "ir") = + def bitCheckboxes(field: play.api.data.Field, options: Iterable[(Int, String)]) = st.group(cls := "radio")( /// Will hold the value being calculated with the various checkboxes when sending div( input( - st.id := s"$prefix${field.id}_hidden", + st.id := s"ir${field.id}_hidden", true option st.checked, tpe := "hidden", st.value := "", @@ -63,7 +63,7 @@ object bits: st.style := "display: none;" ) :: options .map: (key, value) => - val id = s"$prefix${field.id}_$key" + val id = s"ir${field.id}_$key" val intVal = ~field.value.flatMap(_.toIntOption) val checked = (intVal & key) == key div( diff --git a/app/views/account/pref.scala b/app/views/account/pref.scala index 244ba80cc6918..9a17ca9b88a55 100644 --- a/app/views/account/pref.scala +++ b/app/views/account/pref.scala @@ -56,7 +56,7 @@ object pref: ), setting( zenMode(), - radios(form("display.zen"), booleanChoices) + radios(form("display.zen"), translatedZenChoices) ), setting( displayBoardResizeHandle(), diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index db517a18035f2..3adc48e7c7179 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -294,13 +294,14 @@ object layout: baseClass -> true, "dark-board" -> (pref.bg == lila.pref.Pref.Bg.DARKBOARD), "piece-letter" -> pref.pieceNotationIsLetter, - "zen" -> pref.isZen, "blind-mode" -> ctx.blind, "kid" -> ctx.kid, "mobile" -> lila.common.HTTPRequest.isMobileBrowser(ctx.req), "playing fixed-scroll" -> playing, + "no-rating" -> !pref.showRatings, + "zen" -> (pref.isZen || (playing && pref.isZenAutomatic)), "zenable" -> zenable, - "no-rating" -> !pref.showRatings + "zen-automatic" -> pref.isZenAutomatic ) }, dataDev, diff --git a/app/views/round/player.scala b/app/views/round/player.scala index 58b5686f53168..01d3f68de95bb 100644 --- a/app/views/round/player.scala +++ b/app/views/round/player.scala @@ -44,7 +44,8 @@ object player: bits.layout( variant = pov.game.variant, - title = s"${trans.play.txt()} ${if ctx.pref.isZen then "ZEN" else playerText(pov.opponent)}", + title = s"${trans.play + .txt()} ${if ctx.pref.isZen || ctx.pref.isZenAutomatic then "ZEN" else playerText(pov.opponent)}", moreJs = frag( roundNvuiTag, jsModuleInit( diff --git a/modules/i18n/src/main/I18nKeys.scala b/modules/i18n/src/main/I18nKeys.scala index 8ac6ded918dbb..d13d67989176a 100644 --- a/modules/i18n/src/main/I18nKeys.scala +++ b/modules/i18n/src/main/I18nKeys.scala @@ -1678,6 +1678,7 @@ object I18nKeys: val `explainShowPlayerRatings` = I18nKey("preferences:explainShowPlayerRatings") val `displayBoardResizeHandle` = I18nKey("preferences:displayBoardResizeHandle") val `onlyOnInitialPosition` = I18nKey("preferences:onlyOnInitialPosition") + val `inGameOnly` = I18nKey("preferences:inGameOnly") val `blindfoldChess` = I18nKey("preferences:blindfoldChess") val `chessClock` = I18nKey("preferences:chessClock") val `tenthsOfSeconds` = I18nKey("preferences:tenthsOfSeconds") diff --git a/modules/pref/src/main/Pref.scala b/modules/pref/src/main/Pref.scala index 93f6685300b0f..caea45cf200a4 100644 --- a/modules/pref/src/main/Pref.scala +++ b/modules/pref/src/main/Pref.scala @@ -90,7 +90,13 @@ case class Pref( SoundSet.allByKey get value map { s => copy(soundSet = s.key) } - case "zen" => copy(zen = if value == "1" then 1 else 0).some + case "zen" => + copy(zen = value match + case "1" => 1 + case "2" => 2 + case "3" => 3 + case _ => 0 + ).some case "voice" => copy(voice = if value == "1" then 1.some else 0.some).some case "keyboardMove" => copy(keyboardMove = if value == "1" then 1 else 0).some case _ => none @@ -115,7 +121,8 @@ case class Pref( def pieceNotationIsLetter = pieceNotation == PieceNotation.LETTER - def isZen = zen == Zen.YES + def isZen = zen == Zen.YES + def isZenAutomatic = zen == Zen.GAME_AUTO val showRatings = ratings == Ratings.YES @@ -436,7 +443,17 @@ object Pref: val changedAt = instantOf(2021, 12, 28, 8, 0) val showPrompt = changedAt.isAfter(nowInstant minusMonths 6) - object Zen extends BooleanPref + object Zen: + val NO = 0 + val YES = 1 + val GAME_AUTO = 2 + + val choices = Seq( + NO -> "No", + YES -> "Yes", + GAME_AUTO -> "In-game only" + ) + object Ratings extends BooleanPref val darkByDefaultSince = instantOf(2021, 11, 7, 8, 0) diff --git a/modules/pref/src/main/PrefForm.scala b/modules/pref/src/main/PrefForm.scala index f9d2f529957f6..169d237e959d6 100644 --- a/modules/pref/src/main/PrefForm.scala +++ b/modules/pref/src/main/PrefForm.scala @@ -35,7 +35,7 @@ object PrefForm: "coords" -> checkedNumber(Pref.Coords.choices), "replay" -> checkedNumber(Pref.Replay.choices), "pieceNotation" -> optional(booleanNumber), - "zen" -> optional(booleanNumber), + "zen" -> optional(checkedNumber(Pref.Zen.choices)), "resizeHandle" -> optional(checkedNumber(Pref.ResizeHandle.choices)), "blindfold" -> checkedNumber(Pref.Blindfold.choices) )(DisplayData.apply)(unapply), @@ -245,7 +245,7 @@ object PrefForm: val zen = Form( single( - "zen" -> text.verifying(Set("0", "1") contains _) + "zen" -> text.verifying(Set("0", "1", "2") contains _) ) ) diff --git a/translation/source/preferences.xml b/translation/source/preferences.xml index c5f1d6a580226..f9f0e03f3f435 100644 --- a/translation/source/preferences.xml +++ b/translation/source/preferences.xml @@ -18,6 +18,7 @@ This allows hiding all ratings from the website, to help focus on the chess. Games can still be rated, this is only about what you get to see. Show board resize handle Only on initial position + In-game only Blindfold chess (invisible pieces) Chess clock Tenths of seconds diff --git a/ui/puzzle/src/ctrl.ts b/ui/puzzle/src/ctrl.ts index 6c7cc760a039a..60d16120cbf3e 100644 --- a/ui/puzzle/src/ctrl.ts +++ b/ui/puzzle/src/ctrl.ts @@ -611,7 +611,9 @@ export default function (opts: PuzzleOpts, redraw: Redraw): Controller { lichess.pubsub.on('zen', () => { const zen = $('body').toggleClass('zen').hasClass('zen'); window.dispatchEvent(new Event('resize')); - xhr.setZen(zen); + if (!$('body').hasClass('zen-automatic')) { + xhr.setZen(zen); + } }); $('body').addClass('playing'); // for zen $('#zentog').on('click', () => lichess.pubsub.emit('zen')); diff --git a/ui/round/src/ctrl.ts b/ui/round/src/ctrl.ts index 16dccead5ffd8..c42d41ca37fd5 100644 --- a/ui/round/src/ctrl.ts +++ b/ui/round/src/ctrl.ts @@ -154,7 +154,9 @@ export default class RoundController { lichess.pubsub.on('zen', () => { const zen = $('body').toggleClass('zen').hasClass('zen'); window.dispatchEvent(new Event('resize')); - xhr.setZen(zen); + if (!$('body').hasClass('zen-automatic')) { + xhr.setZen(zen); + } }); if (!this.opts.noab && this.isPlaying()) ab.init(this); @@ -566,6 +568,12 @@ export default class RoundController { ) this.opts.chat?.instance?.then(c => c.post('Good game, well played')); } + + if ($('body').hasClass('zen-automatic') && $('body').hasClass('zen')) { + $('body').toggleClass('zen'); + window.dispatchEvent(new Event('resize')); + } + if (d.crazyhouse) crazyEndHook(); this.clearJust(); this.setTitle(); diff --git a/ui/storm/src/ctrl.ts b/ui/storm/src/ctrl.ts index 74880de29e104..f3967aa4bf2d6 100644 --- a/ui/storm/src/ctrl.ts +++ b/ui/storm/src/ctrl.ts @@ -67,7 +67,9 @@ export default class StormCtrl implements PuzCtrl { lichess.pubsub.on('zen', () => { const zen = $('body').toggleClass('zen').hasClass('zen'); window.dispatchEvent(new Event('resize')); - xhr.setZen(zen); + if (!$('body').hasClass('zen-automatic')) { + xhr.setZen(zen); + } }); $('#zentog').on('click', this.toggleZen); lichess.sound.move(); From 973619339bd00dc42e072db2eea7a39521c3fc88 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 6 Jul 2023 13:14:26 +0200 Subject: [PATCH 2/4] validate zen input using Zen.choices --- modules/pref/src/main/Pref.scala | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/modules/pref/src/main/Pref.scala b/modules/pref/src/main/Pref.scala index caea45cf200a4..b49beb1aabf16 100644 --- a/modules/pref/src/main/Pref.scala +++ b/modules/pref/src/main/Pref.scala @@ -90,13 +90,7 @@ case class Pref( SoundSet.allByKey get value map { s => copy(soundSet = s.key) } - case "zen" => - copy(zen = value match - case "1" => 1 - case "2" => 2 - case "3" => 3 - case _ => 0 - ).some + case "zen" => copy(zen = ~value.toIntOption.filter(Zen.choices.map(_._1).contains)).some case "voice" => copy(voice = if value == "1" then 1.some else 0.some).some case "keyboardMove" => copy(keyboardMove = if value == "1" then 1 else 0).some case _ => none From 230fec94512b11e5e80ab4219dac11ec20a12361 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 27 Jul 2023 16:01:34 +0200 Subject: [PATCH 3/4] only add zen-automatic class to body when relevant --- app/views/base/layout.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index 3adc48e7c7179..c0d86e1175b19 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -301,7 +301,7 @@ object layout: "no-rating" -> !pref.showRatings, "zen" -> (pref.isZen || (playing && pref.isZenAutomatic)), "zenable" -> zenable, - "zen-automatic" -> pref.isZenAutomatic + "zen-automatic" -> (zenable && pref.isZenAutomatic) ) }, dataDev, From 4fdac3a9f865012d8a64bbed13ae86518dd39028 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 27 Jul 2023 16:04:48 +0200 Subject: [PATCH 4/4] rename zen-auto --- app/views/base/layout.scala | 4 ++-- app/views/round/player.scala | 2 +- modules/pref/src/main/Pref.scala | 4 ++-- ui/puzzle/src/ctrl.ts | 2 +- ui/round/src/ctrl.ts | 4 ++-- ui/storm/src/ctrl.ts | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index c0d86e1175b19..9093b2cf38018 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -299,9 +299,9 @@ object layout: "mobile" -> lila.common.HTTPRequest.isMobileBrowser(ctx.req), "playing fixed-scroll" -> playing, "no-rating" -> !pref.showRatings, - "zen" -> (pref.isZen || (playing && pref.isZenAutomatic)), + "zen" -> (pref.isZen || (playing && pref.isZenAuto)), "zenable" -> zenable, - "zen-automatic" -> (zenable && pref.isZenAutomatic) + "zen-auto" -> (zenable && pref.isZenAuto) ) }, dataDev, diff --git a/app/views/round/player.scala b/app/views/round/player.scala index 01d3f68de95bb..220555e2135b9 100644 --- a/app/views/round/player.scala +++ b/app/views/round/player.scala @@ -45,7 +45,7 @@ object player: bits.layout( variant = pov.game.variant, title = s"${trans.play - .txt()} ${if ctx.pref.isZen || ctx.pref.isZenAutomatic then "ZEN" else playerText(pov.opponent)}", + .txt()} ${if ctx.pref.isZen || ctx.pref.isZenAuto then "ZEN" else playerText(pov.opponent)}", moreJs = frag( roundNvuiTag, jsModuleInit( diff --git a/modules/pref/src/main/Pref.scala b/modules/pref/src/main/Pref.scala index b49beb1aabf16..0f2d27ad713cc 100644 --- a/modules/pref/src/main/Pref.scala +++ b/modules/pref/src/main/Pref.scala @@ -115,8 +115,8 @@ case class Pref( def pieceNotationIsLetter = pieceNotation == PieceNotation.LETTER - def isZen = zen == Zen.YES - def isZenAutomatic = zen == Zen.GAME_AUTO + def isZen = zen == Zen.YES + def isZenAuto = zen == Zen.GAME_AUTO val showRatings = ratings == Ratings.YES diff --git a/ui/puzzle/src/ctrl.ts b/ui/puzzle/src/ctrl.ts index 60d16120cbf3e..8fa13fa96b71f 100644 --- a/ui/puzzle/src/ctrl.ts +++ b/ui/puzzle/src/ctrl.ts @@ -611,7 +611,7 @@ export default function (opts: PuzzleOpts, redraw: Redraw): Controller { lichess.pubsub.on('zen', () => { const zen = $('body').toggleClass('zen').hasClass('zen'); window.dispatchEvent(new Event('resize')); - if (!$('body').hasClass('zen-automatic')) { + if (!$('body').hasClass('zen-auto')) { xhr.setZen(zen); } }); diff --git a/ui/round/src/ctrl.ts b/ui/round/src/ctrl.ts index c42d41ca37fd5..b222a13b1c8db 100644 --- a/ui/round/src/ctrl.ts +++ b/ui/round/src/ctrl.ts @@ -154,7 +154,7 @@ export default class RoundController { lichess.pubsub.on('zen', () => { const zen = $('body').toggleClass('zen').hasClass('zen'); window.dispatchEvent(new Event('resize')); - if (!$('body').hasClass('zen-automatic')) { + if (!$('body').hasClass('zen-auto')) { xhr.setZen(zen); } }); @@ -569,7 +569,7 @@ export default class RoundController { this.opts.chat?.instance?.then(c => c.post('Good game, well played')); } - if ($('body').hasClass('zen-automatic') && $('body').hasClass('zen')) { + if ($('body').hasClass('zen-auto') && $('body').hasClass('zen')) { $('body').toggleClass('zen'); window.dispatchEvent(new Event('resize')); } diff --git a/ui/storm/src/ctrl.ts b/ui/storm/src/ctrl.ts index f3967aa4bf2d6..ac7aa108b0ee1 100644 --- a/ui/storm/src/ctrl.ts +++ b/ui/storm/src/ctrl.ts @@ -67,7 +67,7 @@ export default class StormCtrl implements PuzCtrl { lichess.pubsub.on('zen', () => { const zen = $('body').toggleClass('zen').hasClass('zen'); window.dispatchEvent(new Event('resize')); - if (!$('body').hasClass('zen-automatic')) { + if (!$('body').hasClass('zen-auto')) { xhr.setZen(zen); } });