lila/modules/pref/src/main/Pref.scala

476 lines
10 KiB
Scala

package lila.pref
import org.joda.time.DateTime
import lila.user.User
case class Pref(
_id: String, // user id
bg: Int,
bgImg: Option[String],
is3d: Boolean,
theme: String,
pieceSet: String,
theme3d: String,
pieceSet3d: String,
soundSet: String,
blindfold: Int,
autoQueen: Int,
autoThreefold: Int,
takeback: Int,
moretime: Int,
clockTenths: Int,
clockBar: Boolean,
clockSound: Boolean,
premove: Boolean,
animation: Int,
captured: Boolean,
follow: Boolean,
highlight: Boolean,
destination: Boolean,
coords: Int,
replay: Int,
challenge: Int,
message: Int,
studyInvite: Int,
coordColor: Int,
submitMove: Int,
confirmResign: Int,
mention: Boolean,
insightShare: Int,
keyboardMove: Int,
zen: Int,
ratings: Int,
rookCastle: Int,
moveEvent: Int,
pieceNotation: Int,
resizeHandle: Int,
tags: Map[String, String] = Map.empty
) {
import Pref._
def id = _id
def realTheme = Theme(theme)
def realPieceSet = PieceSet(pieceSet)
def realTheme3d = Theme3d(theme3d)
def realPieceSet3d = PieceSet3d(pieceSet3d)
def themeColor = if (bg == Bg.LIGHT) "#dbd7d1" else "#2e2a24"
def realSoundSet = SoundSet(soundSet)
def coordColorName = Color.choices.toMap.get(coordColor).fold("random")(_.toLowerCase)
def coordsClass = Coords classOf coords
def hasDgt = tags contains Tag.dgt
def set(name: String, value: String): Option[Pref] =
name match {
case "bg" => Pref.Bg.fromString.get(value).map { bg => copy(bg = bg) }
case "bgImg" => copy(bgImg = value.some).some
case "theme" =>
Theme.allByName get value map { t =>
copy(theme = t.name)
}
case "pieceSet" =>
PieceSet.allByName get value map { p =>
copy(pieceSet = p.name)
}
case "theme3d" =>
Theme3d.allByName get value map { t =>
copy(theme3d = t.name)
}
case "pieceSet3d" =>
PieceSet3d.allByName get value map { p =>
copy(pieceSet3d = p.name)
}
case "is3d" => copy(is3d = value == "true").some
case "soundSet" =>
SoundSet.allByKey get value map { s =>
copy(soundSet = s.key)
}
case "zen" => copy(zen = if (value == "1") 1 else 0).some
case _ => none
}
def animationMillis: Int =
animation match {
case Animation.NONE => 0
case Animation.FAST => 120
case Animation.NORMAL => 250
case Animation.SLOW => 500
case _ => 250
}
def animationMillisForSpeedPuzzles: Int =
animation match {
case Animation.NONE => 0
case Animation.SLOW => 120
case _ => 70
}
def isBlindfold = blindfold == Pref.Blindfold.YES
def bgImgOrDefault = bgImg | Pref.defaultBgImg
def pieceNotationIsLetter = pieceNotation == PieceNotation.LETTER
def isZen = zen == Zen.YES
val showRatings = ratings == Ratings.YES
def is2d = !is3d
// atob("aHR0cDovL2NoZXNzLWNoZWF0LmNvbS9ob3dfdG9fY2hlYXRfYXRfbGljaGVzcy5odG1s")
def botCompatible =
theme == "brown" &&
pieceSet == "cburnett" &&
is2d &&
animation == Animation.NONE &&
highlight &&
coords == Coords.OUTSIDE
}
object Pref {
val defaultBgImg = "//lichess1.org/assets/images/background/landscape.jpg"
trait BooleanPref {
val NO = 0
val YES = 1
val choices = Seq(NO -> "No", YES -> "Yes")
}
object BooleanPref {
val verify = (v: Int) => v == 0 || v == 1
}
object Bg {
val LIGHT = 100
val DARK = 200
val DARKBOARD = 300
val TRANSPARENT = 400
val choices = Seq(
LIGHT -> "Light",
DARK -> "Dark",
DARKBOARD -> "Dark Board",
TRANSPARENT -> "Transparent"
)
val fromString = Map(
"light" -> LIGHT,
"dark" -> DARK,
"darkBoard" -> DARKBOARD,
"transp" -> TRANSPARENT
)
val asString = fromString.map(_.swap)
}
object Tag {
val dgt = "dgt"
}
object Color {
val WHITE = 1
val RANDOM = 2
val BLACK = 3
val choices = Seq(
WHITE -> "White",
RANDOM -> "Random",
BLACK -> "Black"
)
}
object AutoQueen {
val NEVER = 1
val PREMOVE = 2
val ALWAYS = 3
val choices = Seq(
NEVER -> "Never",
ALWAYS -> "Always",
PREMOVE -> "When premoving"
)
}
object SubmitMove {
val NEVER = 0
val CORRESPONDENCE_ONLY = 4
val CORRESPONDENCE_UNLIMITED = 1
val ALWAYS = 2
val choices = Seq(
NEVER -> "Never",
CORRESPONDENCE_ONLY -> "Correspondence games only",
CORRESPONDENCE_UNLIMITED -> "Correspondence and unlimited",
ALWAYS -> "Always"
)
}
object ConfirmResign extends BooleanPref
object InsightShare {
val NOBODY = 0
val FRIENDS = 1
val EVERYBODY = 2
val choices = Seq(
NOBODY -> "With nobody",
FRIENDS -> "With friends",
EVERYBODY -> "With everybody"
)
}
object Mention extends BooleanPref
object KeyboardMove extends BooleanPref
object RookCastle {
val NO = 0
val YES = 1
val choices = Seq(
NO -> "Castle by moving by two squares",
YES -> "Castle by moving onto the rook"
)
}
object MoveEvent {
val CLICK = 0
val DRAG = 1
val BOTH = 2
val choices = Seq(
CLICK -> "Click two squares",
DRAG -> "Drag a piece",
BOTH -> "Both clicks and drag"
)
}
object PieceNotation {
val SYMBOL = 0
val LETTER = 1
val choices = Seq(
SYMBOL -> "Chess piece symbol",
LETTER -> "PGN letter (K, Q, R, B, N)"
)
}
object Blindfold extends BooleanPref {
override val choices = Seq(
NO -> "What? No!",
YES -> "Yes, hide the pieces"
)
}
object AutoThreefold {
val NEVER = 1
val TIME = 2
val ALWAYS = 3
val choices = Seq(
NEVER -> "Never",
ALWAYS -> "Always",
TIME -> "When time remaining < 30 seconds"
)
}
object Takeback {
val NEVER = 1
val CASUAL = 2
val ALWAYS = 3
val choices = Seq(
NEVER -> "Never",
ALWAYS -> "Always",
CASUAL -> "In casual games only"
)
}
object Moretime {
val NEVER = 1
val CASUAL = 2
val ALWAYS = 3
val choices = Seq(
NEVER -> "Never",
ALWAYS -> "Always",
CASUAL -> "In casual games only"
)
}
object Animation {
val NONE = 0
val FAST = 1
val NORMAL = 2
val SLOW = 3
val choices = Seq(
NONE -> "None",
FAST -> "Fast",
NORMAL -> "Normal",
SLOW -> "Slow"
)
}
object Coords {
val NONE = 0
val INSIDE = 1
val OUTSIDE = 2
val choices = Seq(
NONE -> "No",
INSIDE -> "Inside the board",
OUTSIDE -> "Outside the board"
)
def classOf(v: Int) =
v match {
case INSIDE => "in"
case OUTSIDE => "out"
case _ => "no"
}
}
object Replay {
val NEVER = 0
val SLOW = 1
val ALWAYS = 2
val choices = Seq(
NEVER -> "Never",
SLOW -> "On slow games",
ALWAYS -> "Always"
)
}
object ClockTenths {
val NEVER = 0
val LOWTIME = 1
val ALWAYS = 2
val choices = Seq(
NEVER -> "Never",
LOWTIME -> "When time remaining < 10 seconds",
ALWAYS -> "Always"
)
}
object Challenge {
val NEVER = 1
val RATING = 2
val FRIEND = 3
val ALWAYS = 4
val ratingThreshold = 300
val choices = Seq(
NEVER -> "Never",
RATING -> s"If rating is ± $ratingThreshold",
FRIEND -> "Only friends",
ALWAYS -> "Always"
)
}
object Message {
val NEVER = 1
val FRIEND = 2
val ALWAYS = 3
val choices = Seq(
NEVER -> "Only existing conversations",
FRIEND -> "Only friends",
ALWAYS -> "Always"
)
}
object StudyInvite {
val NEVER = 1
val FRIEND = 2
val ALWAYS = 3
val choices = Seq(
NEVER -> "Never",
FRIEND -> "Only friends",
ALWAYS -> "Always"
)
}
object ResizeHandle {
val NEVER = 0
val INITIAL = 1
val ALWAYS = 2
val choices = Seq(
NEVER -> "Never",
INITIAL -> "On initial position",
ALWAYS -> "Always"
)
}
object Zen extends BooleanPref {}
object Ratings extends BooleanPref {}
val darkByDefaultSince = new DateTime(2021, 11, 7, 8, 0)
def create(id: User.ID) = default.copy(_id = id)
def create(user: User) = default.copy(
_id = user.id,
bg = if (user.createdAt isAfter darkByDefaultSince) Bg.DARK else Bg.LIGHT
)
lazy val default = Pref(
_id = "",
bg = Bg.DARKBOARD,
bgImg = none,
is3d = false,
theme = Theme.default.name,
pieceSet = PieceSet.default.name,
theme3d = Theme3d.default.name,
pieceSet3d = PieceSet3d.default.name,
soundSet = SoundSet.default.name,
blindfold = Blindfold.NO,
autoQueen = AutoQueen.PREMOVE,
autoThreefold = AutoThreefold.TIME,
takeback = Takeback.ALWAYS,
moretime = Moretime.ALWAYS,
clockBar = true,
clockSound = true,
premove = false,
animation = Animation.NORMAL,
captured = true,
follow = true,
highlight = true,
destination = true,
coords = Coords.INSIDE,
replay = Replay.ALWAYS,
clockTenths = ClockTenths.LOWTIME,
challenge = Challenge.ALWAYS,
message = Message.ALWAYS,
studyInvite = StudyInvite.ALWAYS,
coordColor = Color.RANDOM,
submitMove = SubmitMove.CORRESPONDENCE_ONLY,
confirmResign = ConfirmResign.YES,
mention = true,
insightShare = InsightShare.FRIENDS,
keyboardMove = KeyboardMove.NO,
zen = Zen.NO,
ratings = Ratings.YES,
rookCastle = RookCastle.YES,
moveEvent = MoveEvent.BOTH,
pieceNotation = PieceNotation.SYMBOL,
resizeHandle = ResizeHandle.INITIAL,
tags = Map.empty
)
import ornicar.scalalib.Zero
implicit def PrefZero: Zero[Pref] = Zero.instance(default)
}