Skip to content

Commit

Permalink
proper Title type
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Nov 13, 2018
1 parent 9eec0bb commit e9feb8a
Show file tree
Hide file tree
Showing 13 changed files with 50 additions and 48 deletions.
4 changes: 2 additions & 2 deletions app/controllers/Mod.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import lila.chat.Chat
import lila.common.{ IpAddress, EmailAddress, HTTPRequest }
import lila.report.{ Suspect, Mod => AsMod, SuspectId }
import lila.security.Permission
import lila.user.{ UserRepo, User => UserModel }
import lila.user.{ UserRepo, User => UserModel, Title }
import ornicar.scalalib.Zero
import views._

Expand Down Expand Up @@ -133,7 +133,7 @@ object Mod extends LilaController {
implicit def req = ctx.body
lila.user.DataForm.title.bindFromRequest.fold(
err => fuccess(redirect(username, mod = true)),
title => modApi.setTitle(me.id, username, title) >>
title => modApi.setTitle(me.id, username, title map Title.apply) >>
Env.security.automaticEmail.onTitleSet(username) >>-
Env.user.uncacheLightUser(UserModel normalize username) inject
redirect(username, mod = false)
Expand Down
18 changes: 8 additions & 10 deletions app/templating/UserHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,9 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
userId = user.id,
username = user.name,
isPatron = user.isPatron,
title = user.title,
title = withTitle ?? user.title map Title.apply,
cssClass = cssClass,
withOnline = withOnline,
withTitle = withTitle,
truncate = truncate,
params = params,
modIcon = modIcon
Expand All @@ -123,10 +122,9 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
userId = user.id,
username = user.name,
isPatron = user.isPatron,
title = user.title,
title = withTitle ?? user.title map Title.apply,
cssClass = cssClass,
withOnline = withOnline,
withTitle = withTitle,
truncate = truncate,
params = params,
modIcon = false
Expand All @@ -138,28 +136,28 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
cssClass: Option[String]
): Html = userIdLink(userId.some, cssClass)

def titleTag(title: Option[String]) = Html {
def titleTag(title: Option[Title]) = Html {
title.fold("") { t =>
s"""<span class="title" data-title="$t" title="${Title titleName t}">$t</span>&nbsp;"""
}
}
def titleTag(lu: LightUser): Html = titleTag(lu.title map Title.apply)

private def userIdNameLink(
userId: String,
username: String,
isPatron: Boolean,
cssClass: Option[String],
withOnline: Boolean,
withTitle: Boolean,
truncate: Option[Int],
title: Option[String],
title: Option[Title],
params: String,
modIcon: Boolean
): String = {
val klass = userClass(userId, cssClass, withOnline)
val href = userHref(username, params = params)
val content = truncate.fold(username)(username.take)
val titleS = if (withTitle) titleTag(title).body else ""
val titleS = titleTag(title).body
val icon = withOnline ?? (if (modIcon) moderatorIcon else lineIcon(isPatron))
s"""<a $klass $href>$icon$titleS$content</a>"""
}
Expand Down Expand Up @@ -197,7 +195,7 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
val klass = userClass(userId, cssClass, withOnline, withPowerTip)
val href = userHref(name)
val rat = rating ?? { r => s" ($r)" }
val titleS = titleTag(user.flatMap(_.title) ifTrue withTitle).body
val titleS = withTitle ?? user ?? (u => titleTag(u).body)
val icon = withOnline ?? lineIcon(user)
Html(s"""<a $klass $href>$icon$titleS$name$rat</a>""")
}
Expand Down Expand Up @@ -225,7 +223,7 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
val user = lightUser(userId)
val name = user.fold(userId)(_.name)
val content = user.fold(userId)(_.name)
val titleS = user.??(u => titleTag(u.title).body)
val titleS = user.??(u => titleTag(u.title map Title.apply).body)
val klass = userClass(userId, none, withOnline)
val href = s"data-${userHref(name)}"
val icon = withOnline ?? lineIcon(user)
Expand Down
4 changes: 2 additions & 2 deletions app/views/game/vstext.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
<div class="left user_link">
@playerUsername(pov.player, withRating = false, withTitle = false)
<br />
@titleTag(lightUser(pov.player.userId).flatMap(_.title))
@lightUser(pov.player.userId).??(titleTag)
@pov.player.rating@if(pov.player.provisional){?}
</div>
<div class="right user_link">
@playerUsername(pov.opponent, withRating = false, withTitle = false)
<br />
@pov.opponent.rating@if(pov.opponent.provisional){?}
@titleTag(lightUser(pov.opponent.userId).flatMap(_.title))
@lightUser(pov.opponent.userId).??(titleTag)
</div>
@pov.game.clock.map { c =>
<div class="center"><span data-icon="p"> @shortClockName(c.config)</span></div>
Expand Down
2 changes: 1 addition & 1 deletion app/views/user/mod/actions.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
</div>
@if(isGranted(_.SetTitle)) {
<form class="fide_title" method="post" action="@routes.Mod.setTitle(u.username)">
@base.form.select(lila.user.DataForm.title.fill(u.title)("title"), lila.user.Title.all, "No title".some)
@base.form.select(lila.user.DataForm.title.fill(u.title.map(_.value))("title"), lila.user.Title.all, "No title".some)
</form>
}
@if(isGranted(_.SetEmail)) {
Expand Down
4 changes: 1 addition & 3 deletions modules/common/src/main/LightUser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ case class LightUser(
def titleName = title.fold(name)(_ + " " + name)
def titleNameHtml = title.fold(name)(_ + "&nbsp;" + name)

def isBot = title has LightUser.botTitle
def isBot = title has "BOT"
}

object LightUser {

val botTitle = "BOT"

implicit val lightUserWrites = OWrites[LightUser] { u =>
Json.obj(
"id" -> u.id,
Expand Down
4 changes: 2 additions & 2 deletions modules/game/src/main/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ object Namer {
def player(p: Player, withRating: Boolean = true, withTitle: Boolean = true)(implicit lightUser: LightUser.GetterSync) = Html {
p.aiLevel.fold(
p.userId.flatMap(lightUser).fold(lila.user.User.anonymous) { user =>
val title = (user.title ifTrue withTitle) ?? { t =>
s"""<span class="title" data-title="$t" title="${Title titleName t}">$t</span>&nbsp;"""
val title = withTitle ?? user.title ?? { t =>
s"""<span class="title" data-title="$t" title="${Title titleName Title(t)}">$t</span>&nbsp;"""
}
if (withRating) s"$title${user.name}&nbsp;(${ratingString(p)})"
else s"$title${user.name}"
Expand Down
2 changes: 1 addition & 1 deletion modules/mod/src/main/ModApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ final class ModApi(
}
}

def setTitle(mod: String, username: String, title: Option[String]): Funit = withUser(username) { user =>
def setTitle(mod: String, username: String, title: Option[Title]): Funit = withUser(username) { user =>
title match {
case None => {
UserRepo.removeTitle(user.id) >>-
Expand Down
4 changes: 2 additions & 2 deletions modules/report/src/main/ReportScore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package lila.report
import reactivemongo.bson._

import lila.db.dsl._
import lila.user.{ User, UserRepo }
import lila.user.{ User, UserRepo, Title }

private final class ReportScore(
getAccuracy: ReporterId => Fu[Option[Accuracy]]
Expand Down Expand Up @@ -31,7 +31,7 @@ private final class ReportScore(
def reporterScore(r: Reporter) =
titleScore(r.user.title) + flagScore(r.user)

def titleScore(title: Option[String]) =
def titleScore(title: Option[Title]) =
(title.isDefined) ?? 30d

def flagScore(user: User) =
Expand Down
4 changes: 2 additions & 2 deletions modules/simul/src/main/Simul.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package lila.simul

import chess.variant.Variant
import lila.user.User
import lila.user.{ User, Title }
import org.joda.time.DateTime
import ornicar.scalalib.Random

Expand All @@ -16,7 +16,7 @@ case class Simul(
createdAt: DateTime,
hostId: String,
hostRating: Int,
hostTitle: Option[String],
hostTitle: Option[Title],
hostGameId: Option[String], // game the host is focusing on
startedAt: Option[DateTime],
finishedAt: Option[DateTime],
Expand Down
2 changes: 2 additions & 0 deletions modules/user/src/main/JsonView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ final class JsonView(isOnline: User.ID => Boolean) {

object JsonView {

import Title.titleJsonWrites

implicit val nameWrites = Writes[User] { u =>
JsString(u.username)
}
Expand Down
36 changes: 20 additions & 16 deletions modules/user/src/main/Title.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,32 @@ case class Title(value: String) extends AnyVal with StringValue

object Title {

implicit val titleIso = lila.common.Iso.string[Title](Title.apply, _.value)
implicit val titleBsonHandler = lila.db.dsl.stringIsoHandler(Title.titleIso)
implicit val titleJsonWrites = lila.common.PimpedJson.stringIsoWriter(Title.titleIso)

// important: names are as stated on FIDE profile pages
val all = Seq(
"GM" -> "Grandmaster",
"WGM" -> "Woman Grandmaster",
"IM" -> "International Master",
"WIM" -> "Woman Intl. Master",
"FM" -> "FIDE Master",
"WFM" -> "Woman FIDE Master",
"NM" -> "National Master",
"CM" -> "Candidate Master",
"WCM" -> "Woman Candidate Master",
"WNM" -> "Woman National Master",
"LM" -> "Lichess Master",
"BOT" -> "Chess Robot"
Title("GM") -> "Grandmaster",
Title("WGM") -> "Woman Grandmaster",
Title("IM") -> "International Master",
Title("WIM") -> "Woman Intl. Master",
Title("FM") -> "FIDE Master",
Title("WFM") -> "Woman FIDE Master",
Title("NM") -> "National Master",
Title("CM") -> "Candidate Master",
Title("WCM") -> "Woman Candidate Master",
Title("WNM") -> "Woman National Master",
Title("LM") -> "Lichess Master",
Title("BOT") -> "Chess Robot"
)

val bot = lila.common.LightUser.botTitle
val bot = Title("BOT")

val names = all.toMap
lazy val fromNames = all.map(_.swap).toMap

def titleName(title: String) = names get title getOrElse title
def titleName(title: Title): String = names.getOrElse(title, title.value)

object fromUrl {

Expand All @@ -37,12 +41,12 @@ object Title {
// >&nbsp;FIDE title</td><td colspan=3 bgcolor=#efefef>&nbsp;Grandmaster</td>
private val FideProfileTitleRegex = """>&nbsp;FIDE title</td><td colspan=3 bgcolor=#efefef>&nbsp;([^<]+)</td>""".r.unanchored

def apply(url: String): Fu[Option[String]] = url.trim match {
def apply(url: String): Fu[Option[Title]] = url.trim match {
case FideProfileUrlRegex(id) => parseIntOption(id) ?? fromFideProfile
case _ => fuccess(none)
}

private def fromFideProfile(id: Int): Fu[Option[String]] = {
private def fromFideProfile(id: Int): Fu[Option[Title]] = {
WS.url(s"""http:https://ratings.fide.com/card.phtml?event=$id""").get().map(_.body) map {
case FideProfileTitleRegex(name) => Title.fromNames get name
}
Expand Down
9 changes: 4 additions & 5 deletions modules/user/src/main/User.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ case class User(
booster: Boolean = false,
toints: Int = 0,
playTime: Option[User.PlayTime],
title: Option[String] = None,
title: Option[Title] = None,
createdAt: DateTime,
seenAt: Option[DateTime],
kid: Boolean,
Expand All @@ -40,7 +40,7 @@ case class User(
override def toString =
s"User $username(${perfs.bestRating}) games:${count.game}${troll ?? " troll"}${engine ?? " engine"}"

def light = LightUser(id = id, name = username, title = title, isPatron = isPatron)
def light = LightUser(id = id, name = username, title = title.map(_.value), isPatron = isPatron)

def realNameOrUsername = profileOrDefault.nonEmptyRealName | username

Expand Down Expand Up @@ -168,8 +168,6 @@ object User {
case class TotpToken(value: String) extends AnyVal
case class PasswordAndToken(password: ClearPassword, token: Option[TotpToken])

case class Title(value: String) extends AnyVal with StringValue

case class PlayTime(total: Int, tv: Int) {
import org.joda.time.Period
def totalPeriod = new Period(total * 1000l)
Expand Down Expand Up @@ -239,6 +237,7 @@ object User {
private implicit def perfsHandler = Perfs.perfsBSONHandler
private implicit def planHandler = Plan.planBSONHandler
private implicit def totpSecretHandler = TotpSecret.totpSecretBSONHandler
import Title.titleBsonHandler

def reads(r: BSON.Reader): User = User(
id = r str id,
Expand All @@ -258,7 +257,7 @@ object User {
seenAt = r dateO seenAt,
kid = r boolD kid,
lang = r strO lang,
title = r strO title,
title = r.getO[Title](title),
plan = r.getO[Plan](plan) | Plan.empty,
reportban = r boolD reportban,
rankban = r boolD rankban,
Expand Down
5 changes: 3 additions & 2 deletions modules/user/src/main/UserRepo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ object UserRepo {

import User.ID
import User.{ BSONFields => F }
import Title.titleBsonHandler

// dirty
private[user] val coll = Env.current.userColl
Expand Down Expand Up @@ -170,7 +171,7 @@ object UserRepo {
$set(F.profile -> Profile.profileBSONHandler.write(profile))
).void

def addTitle(id: ID, title: String): Funit =
def addTitle(id: ID, title: Title): Funit =
coll.updateField($id(id), F.title, title).void

def removeTitle(id: ID): Funit =
Expand Down Expand Up @@ -355,7 +356,7 @@ object UserRepo {
ReadPreference.secondaryPreferred
)

def getTitle(id: ID): Fu[Option[String]] = coll.primitiveOne[String]($id(id), F.title)
def getTitle(id: ID): Fu[Option[Title]] = coll.primitiveOne[Title]($id(id), F.title)

def setPlan(user: User, plan: Plan): Funit = {
implicit val pbw: BSONValueWriter[Plan] = Plan.planBSONHandler
Expand Down

0 comments on commit e9feb8a

Please sign in to comment.