complete user preferences w/ automatic queen option

pull/83/head
Thibault Duplessis 2013-10-20 23:33:55 +02:00
parent 54216e0d56
commit fa4b11f0a1
19 changed files with 88 additions and 119 deletions

View File

@ -27,7 +27,7 @@ object Environment
with PaginatorHelper
with FormHelper
with SetupHelper
with PrefHelper
with lila.pref.PrefHelper
with MessageHelper
with lila.round.RoundHelper
with AiHelper

View File

@ -5,11 +5,11 @@ import lila.message.Env.{ current ⇒ messageEnv }
import lila.report.Env.{ current reportEnv }
import lila.user.Context
trait MessageHelper { self: SecurityHelper =>
trait MessageHelper { self: SecurityHelper
def messageNbUnread(ctx: Context): Int =
ctx.me.??(user messageEnv.api.unreadIds(user.id).await.size)
def reportNbUnprocessed(implicit ctx: Context): Int =
def reportNbUnprocessed(implicit ctx: Context): Int =
isGranted(_.SeeReport) ?? reportEnv.api.nbUnprocessed.await
}

View File

@ -1,6 +1,6 @@
@(field: Field, label: String)(implicit ctx: Context)
<li>
<li class="field">
<label for="@field.id">@label</label>
<input
class="passwd"

View File

@ -1,5 +1,7 @@
@(u: User, form: Form[_])(implicit ctx: Context)
@import lila.pref.Pref
@title = @{ s"${u.username} - ${trans.preferences.str()}" }
@evenMoreCss = {
@ -10,16 +12,12 @@
<div class="content_box small_box">
<div class="signup_box">
<h1 class="lichess_title">@trans.preferences()</h1>
<form action="@routes.Account.profileApply" method="POST">
<form action="@routes.Pref.formApply" method="POST">
<ul>
<li>
@base.checkbox(form("chat"), "Enable chat rooms", 1)
</li>
<li>
@base.checkbox(form("sound"), "Enable sound", 1)
</li>
<li>
@errMsg(form)
<li class="field">
<label for="@form("autoQueen").id">Promotion</label>
@base.select(form("autoQueen"), Pref.AutoQueen.choices)
<p class="help">Automatic promotion or manual choice?</p>
</li>
<li>
<input type="submit" class="submit button" value="@trans.apply()" />

View File

@ -13,26 +13,26 @@
<p>All informations are public and optional.</p>
<form action="@routes.Account.profileApply" method="POST">
<ul>
<li>
<li class="field">
<label for="@form("country").id">@trans.country()</label>
@base.select(form("country"), lila.user.Countries.all, default = "".some)
</li>
<li>
<li class="field">
<label for="@form("location").id">@trans.location()</label>
@base.input(form("location"))
<p class="help">Your city, region, or department.</p>
</li>
<li>
<li class="field">
<label for="@form("bio").id">@trans.biography()</label>
<textarea name="@form("bio").name" id="@form("bio").id" cols="25" rows="7">@form("bio").value</textarea>
<p class="help">Tell about you, what you like in chess, your favorite openings, games, players...</p>
<p class="help">Maximum: 400 characters.</p>
</li>
<li>
<li class="field">
<label for="@form("firstName").id">@trans.firstName()</label>
@base.input(form("firstName"))
</li>
<li>
<li class="field">
<label for="@form("lastName").id">@trans.lastName()</label>
@base.input(form("lastName"))
</li>

View File

@ -8,24 +8,20 @@ import lila.user.User
private[pref] final class DataForm(api: PrefApi) {
val pref = Form(mapping(
"chat" -> boolean,
"sound" -> boolean
"autoQueen" -> number.verifying(Pref.AutoQueen.choices.toMap contains _)
)(PrefData.apply)(PrefData.unapply))
case class PrefData(
chat: Boolean,
sound: Boolean) {
autoQueen: Int) {
def apply(pref: Pref) = pref.copy(
chat = chat,
sound = sound)
autoQueen = autoQueen)
}
object PrefData {
def apply(pref: Pref): PrefData = PrefData(
chat = pref.chat,
sound = pref.sound)
autoQueen = pref.autoQueen)
}
def prefOf(user: User): Fu[Form[PrefData]] = api getPref user map { p

View File

@ -5,26 +5,20 @@ import scala._
case class Pref(
id: String, // user id
dark: Boolean,
chat: Boolean,
sound: Boolean,
theme: String) {
theme: String,
autoQueen: Int) {
def realTheme = Theme(theme)
def toggleDark = copy(dark = !dark)
def toggleChat = copy(chat = !chat)
def toggleSound = copy(sound = !sound)
def get(name: String): Option[String] = name match {
case "dark" dark.toString.some
case "chat" chat.toString.some
case "sound" sound.toString.some
case "theme" theme.some
case _ none
}
def set(name: String, value: String): Option[Pref] = name match {
case "dark" Pref.booleans get value map { b copy(dark = b) }
case "chat" Pref.booleans get value map { b copy(chat = b) }
case "sound" Pref.booleans get value map { b copy(sound = b) }
case "bg" Pref.bgs get value map { b copy(dark = b) }
case "theme" Theme.allByName get value map { t copy(theme = t.name) }
case _ none
}
@ -32,14 +26,27 @@ case class Pref(
object Pref {
val default = Pref(
id = "",
object AutoQueen {
val NEVER = 1
val PREMOVE = 2
val ALWAYS = 3
val choices = Seq(
NEVER -> "Always choose manually",
PREMOVE -> "Automatic queen on premove",
ALWAYS -> "Always automatic queen")
}
def create(id: String) = Pref(
id = id,
dark = false,
chat = true,
sound = false,
theme = Theme.default.name)
theme = Theme.default.name,
autoQueen = AutoQueen.PREMOVE)
val default = create("")
private val booleans = Map("true" -> true, "false" -> false)
private val bgs = Map("light" -> false, "dark" -> true)
import lila.db.Tube
import Tube.Helpers._
@ -52,7 +59,6 @@ object Pref {
private def defaults = Json.obj(
"dark" -> default.dark,
"chat" -> default.chat,
"sound" -> default.sound,
"theme" -> default.theme)
"theme" -> default.theme,
"autoQueen" -> default.autoQueen)
}

View File

@ -1,24 +1,25 @@
package lila.pref
import lila.db.api._
import lila.user.User
import tube.prefTube
import lila.db.api._
final class PrefApi {
def getPref(id: String): Fu[Pref] = $find byId id map (_ | Pref.default)
def getPref(id: String): Fu[Pref] = $find byId id map (_ | Pref.create(id))
def getPref(user: User): Fu[Pref] = getPref(user.id)
def getPref[A](user: User, pref: Pref A): Fu[A] = getPref(user) map pref
def getPrefString(user: User, name: String): Fu[Option[String]] =
def getPrefString(user: User, name: String): Fu[Option[String]] =
getPref(user) map (_ get name)
def setPref(pref: Pref): Funit = $update(pref)
def setPref(pref: Pref): Funit = $save(pref)
def setPref(user: User, change: Pref Pref): Funit =
def setPref(user: User, change: Pref Pref): Funit =
getPref(user) map change flatMap setPref
def setPrefString(user: User, name: String, value: String): Funit =
getPref(user) map (_ get name)
def setPrefString(user: User, name: String, value: String): Funit =
getPref(user) map { _.set(name, value) } flatten
s"Bad pref ${user.id} $name -> $value" flatMap setPref
}

View File

@ -1,23 +1,23 @@
package lila.app
package templating
package lila.pref
import lila.pref.Env.{ current env }
import lila.user.Context
import lila.pref.Theme
import lila.pref.Env.{ current prevEnv }
import play.api.templates.Html
trait PrefHelper {
private def get(name: String)(implicit ctx: Context): Option[String] =
private def get(name: String)(implicit ctx: Context): Option[String] =
ctx.req.session get name orElse {
ctx.me ?? { prevEnv.api.getPrefString(_, name) }
}.await
ctx.me ?? { env.api.getPrefString(_, name) }
}.await
def currentTheme(implicit ctx: Context): Theme = Theme(~get("theme"))
def currentBg(implicit ctx: Context): String =
def currentBg(implicit ctx: Context): String =
if (get("dark") == Some("true")) "dark" else "light"
def userPref(implicit ctx: Context): Pref = {
ctx.me.fold(fuccess(Pref.default))(env.api.getPref)
}.await
def themeList = Theme.list
}

View File

@ -110,7 +110,10 @@ final class Env(
roundMap = roundMap,
socketHub = socketHub)
lazy val messenger = new Messenger(NetDomain, i18nKeys, getUsername)
lazy val messenger = new Messenger(
netDomain = NetDomain,
i18nKeys = i18nKeys,
getUsername = getUsername)
lazy val eloCalculator = new chess.EloCalculator(false)

View File

@ -1,8 +1,8 @@
package lila.round
import chess.Color
import org.apache.commons.lang3.StringEscapeUtils.escapeXml
import chess.Color
import lila.db.api._
import lila.game.Event
import lila.game.{ Game, PovRef, Namer }
@ -21,16 +21,7 @@ final class Messenger(
def init(game: Game): Fu[List[Event]] = systemMessages(game, List(
game.creatorColor.fold(_.whiteCreatesTheGame, _.blackCreatesTheGame),
game.invitedColor.fold(_.whiteJoinsTheGame, _.blackJoinsTheGame)
)) flatMap { events
(Color.all map { color
(game player color).userId ?? { id
UserRepo.getSetting(id, "chat") flatMap {
case Some("false") toggleChat(PovRef(game.id, color), false)
case _ fuccess(Nil)
}
}
}).sequenceFu map { events ::: _.flatten }
}
))
// copies chats then init
// no need to send events back
@ -80,16 +71,6 @@ final class Messenger(
}
}
def toggleChat(ref: PovRef, status: Boolean): Fu[List[Event]] =
"%s chat is %s".format(
ref.color.toString.capitalize,
status.fold("en", "dis") + "abled"
) |> { message
RoomRepo.addSystemMessage(ref.gameId, message) inject {
Event.Message("system", message, false) :: Nil
}
}
private def trans(message: SelectI18nKey, args: Any*): String =
message(i18nKeys).en(args: _*)
}

View File

@ -1,19 +1,21 @@
package lila.round
import lila.user.Context
import lila.game.Pov
import lila.round.Env.{ current roundEnv }
import play.api.libs.json.Json
import scala.math.{ min, max, round }
trait RoundHelper {
import play.api.libs.json.Json
import lila.game.Pov
import lila.pref.PrefHelper
import lila.round.Env.{ current roundEnv }
import lila.user.Context
trait RoundHelper { self: PrefHelper
def moretimeSeconds = roundEnv.moretimeSeconds
def gameAnimationDelay = roundEnv.animationDelay
def roundPlayerJsData(pov: Pov, version: Int) = {
def roundPlayerJsData(pov: Pov, version: Int)(implicit ctx: Context) = {
import pov._
Json.obj(
"game" -> Json.obj(
@ -37,6 +39,7 @@ trait RoundHelper {
),
"possible_moves" -> possibleMoves(pov),
"animation_delay" -> animationDelay(pov),
"autoQueen" -> userPref.autoQueen,
"tournament_id" -> game.tournamentId
)
}

View File

@ -75,8 +75,6 @@ private[round] final class SocketHandler(
case ("moretime", _) round(Moretime(playerId))
case ("outoftime", _) round(Outoftime)
case ("bye", _) socket ! Bye(ref.color)
case ("toggle-chat", o)
messenger.toggleChat(ref, ~(o boolean "d")) pipeTo socket
case ("challenge", o) ((o str "d") |@| member.userId).tupled foreach {
case (to, from) hub.actor.challenger ! lila.hub.actorApi.setup.RemindChallenge(gameId, from, to)
}

View File

@ -15,7 +15,6 @@ case class User(
ipBan: Boolean = false,
enabled: Boolean,
roles: List[String],
settings: Map[String, String] = Map.empty,
profile: Option[Profile] = None,
engine: Boolean = false,
toints: Int = 0,
@ -38,8 +37,6 @@ case class User(
def usernameWithElo = "%s (%d)".format(username, elo)
def setting(name: String): Option[Any] = settings get name
def profileOrDefault = profile | Profile.default
def hasGames = count.game > 0
@ -85,7 +82,6 @@ object User {
"variantElos" -> VariantElos.default,
"troll" -> false,
"ipBan" -> false,
"settings" -> Json.obj(),
"engine" -> false,
"toints" -> 0,
"roles" -> Json.arr(),

View File

@ -68,7 +68,6 @@ object UserRepo {
def setProfile(id: ID, profile: Profile): Funit = {
import tube.profileTube
println(profile)
profile.nonEmpty match {
case Some(p) => $update($select(id), $set("profile" -> p))
case None => $update($select(id), $unset("profile"))
@ -109,14 +108,6 @@ object UserRepo {
elos.sum / elos.size.toFloat
}
def saveSetting(id: ID, key: String, value: String): Funit =
$update($select(id), $set(("settings." + key) -> value))
def getSetting(id: ID, key: String): Fu[Option[String]] =
$primitive.one($select(id), "settings") {
_.asOpt[Map[String, String]] flatMap (_ get key)
}
def authenticate(id: ID, password: String): Fu[Option[User]] =
checkPassword(id, password) flatMap { _ ?? ($find byId id) }

View File

@ -88,7 +88,7 @@ object ApplicationBuild extends Build {
)
lazy val round = project("round", Seq(
common, db, memo, hub, socket, chess, game, user, security, i18n, ai)).settings(
common, db, memo, hub, socket, chess, game, user, security, i18n, ai, pref)).settings(
libraryDependencies ++= provided(play.api, reactivemongo, playReactivemongo)
)

View File

@ -827,10 +827,7 @@ var storage = {
} else {
return '<li class="' + u + (u == 'system' ? ' trans_me' : '') + '">' + urlToLink(t) + '</li>';
}
},
onToggle: self.options.player.spectator ? function(e) {} : _.throttle(function(enabled) {
lichess.socket.send("toggle-chat", enabled);
}, 5000)
}
});
}
self.$watchers.watchers();
@ -1241,7 +1238,8 @@ var storage = {
var color = self.options.player.color;
// promotion
if ($piece.hasClass('pawn') && ((color == "white" && squareId[1] == 8) || (color == "black" && squareId[1] == 1))) {
if (isPremove || false) {
var aq = self.options.autoQueen;
if (aq == 3 || (isPremove && aq == 2)) {
moveData.promotion = "queen";
sendMoveRequest(moveData);
} else {
@ -1552,7 +1550,6 @@ var storage = {
_create: function() {
this.options = $.extend({
// render: function(u, t) {},
onToggle: function(enabled) {},
talkMessageType: 'talk',
secure: false,
resize: false
@ -1595,7 +1592,6 @@ var storage = {
$toggle.change(function() {
var enabled = $toggle.is(':checked');
self.element.toggleClass('hidden', !enabled);
self.options.onToggle(enabled);
if (!enabled) storage.set('nochat', 1);
else storage.remove('nochat');
});

View File

@ -6,7 +6,7 @@
list-style: none outside none;
margin: 1em 0;
}
.content_box form label {
.content_box form .field label {
display: block;
float: left;
margin-right: 10px;
@ -14,11 +14,6 @@
text-align: right;
width: 150px;
}
.content_box form input[type="text"] {
border: 1px solid #D4D4D4;
padding: 3px 5px;
width: 200px;
}
.content_box form input.submit {
margin-left: 160px;
margin-right: 20px;

7
todo
View File

@ -73,7 +73,12 @@ no challenge when playing http://en.lichess.org/forum/lichess-feedback/defis#2
user option for auto-promotion (requested by NM :P) (http://en.lichess.org/@/Matetricks)
check filter games icon on windows
people page = trends by day, week, month. user achievments?
user options: tenth of seconds, sounds, sound choice
user options: tenth of seconds, premove, prepromotion, sounds, sound choice
user stats: http://en.lichess.org/forum/lichess-feedback/feature-request-extended-stats
forum improvements http://en.lichess.org/forum/lichess-feedback/suggestions-for-the-forum#1
analysis 64... Kg6 should be blunder, not mistake
DEPLOY
------ order matters
mongo lichess bin/mongodb/pref.js
mongo lichess bin/mongodb/user3.js