try to unify chat messages / round is OK

This commit is contained in:
Thibault Duplessis 2013-05-14 13:30:55 -03:00
parent 59e1f64012
commit c9484b7552
19 changed files with 156 additions and 135 deletions

View file

@ -36,17 +36,15 @@ trait StringHelper {
private val urlRegex = """(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""".r private val urlRegex = """(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""".r
def addLinks(text: String) = urlRegex.replaceAllIn(text, m "<a href='%s'>%s</a>".format( def addLinks(text: String) = urlRegex.replaceAllIn(text, m {
(prependHttp _ compose delocalize _ compose quoteReplacement _)(m group 1), val url = delocalize(quoteReplacement(m group 1))
(delocalize _ compose quoteReplacement _)(m group 1) "<a href='%s'>%s</a>".format(prependHttp(url), url)
)) })
private def prependHttp(url: String): String = private def prependHttp(url: String): String =
url startsWith "http" fold(url, "http://" + url) url startsWith "http" fold (url, "http://" + url)
private val delocalizeRegex = ("""\w+\.""" + quoteReplacement(netDomain)).r private val delocalize = new lila.common.String.Delocalizer(netDomain)
private def delocalize(url: String) = delocalizeRegex.replaceAllIn(url, netDomain)
def showNumber(n: Int): String = (n > 0).fold("+" + n, n.toString) def showNumber(n: Int): String = (n > 0).fold("+" + n, n.toString)

View file

@ -18,20 +18,17 @@ trait UserHelper {
def isOnline(userId: String) = Env.user isOnline userId def isOnline(userId: String) = Env.user isOnline userId
def userIdLink( def userIdLink(
userId: Option[String], userIdOption: Option[String],
cssClass: Option[String] = None, cssClass: Option[String] = None,
withOnline: Boolean = true, withOnline: Boolean = true,
truncate: Option[Int] = None): Html = Html { truncate: Option[Int] = None): Html = Html {
(userId zmap Env.user.usernameOption) map { userIdOption.fold(User.anonymous) { userId
_.fold(User.anonymous) { username Env.user usernameOption userId map {
"""<a class="user_link%s%s" href="%s">%s</a>""".format( _.fold(User.anonymous) { username
withOnline ?? isOnline(username).fold(" online", " offline"), userIdNameLink(userId, username, cssClass, withOnline, truncate)
cssClass.zmap(" " + _), }
routes.User.show(username), } await
truncate.fold(username)(username.take) }
)
}
} await
} }
def userIdLink( def userIdLink(
@ -47,6 +44,29 @@ trait UserHelper {
} await } await
} }
def usernameLink(
usernameOption: Option[String],
cssClass: Option[String] = None,
withOnline: Boolean = true,
truncate: Option[Int] = None): Html = Html {
usernameOption.fold(User.anonymous) { username
userIdNameLink(username.toLowerCase, username, cssClass, withOnline, truncate)
}
}
private def userIdNameLink(
userId: String,
username: String,
cssClass: Option[String] = None,
withOnline: Boolean = true,
truncate: Option[Int] = None): String =
"""<a class="user_link%s%s" href="%s">%s</a>""".format(
withOnline ?? isOnline(userId).fold(" online", " offline"),
cssClass.zmap(" " + _),
routes.User.show(username),
truncate.fold(username)(username.take)
)
def userLink( def userLink(
user: User, user: User,
cssClass: Option[String] = None, cssClass: Option[String] = None,

View file

@ -4,8 +4,6 @@
@base.layout(title = title) { @base.layout(title = title) {
<style type="text/css">
</style>
<div class="content_box"> <div class="content_box">
<h1>@title</h1> <h1>@title</h1>
<br /><br /> <br /><br />
@ -13,7 +11,7 @@
@messages.map { message => @messages.map { message =>
<tr> <tr>
<td>@showDate(message.date)</td> <td>@showDate(message.date)</td>
<td>@userIdLink(message.userId.some)</td> <td>@userIdLink(message.user)</td>
<td>@Html(message.text)</td> <td>@Html(message.text)</td>
</tr> </tr>
} }

View file

@ -1,12 +1,7 @@
@(messages: List[(Option[String], String)])(implicit ctx: Context) @(messages: List[(Option[String], String)])(implicit ctx: Context)
@messages.map { @messages.map {
case (author, text) => { case (username, text) => {
<li> <li><span>@usernameLink(username, withOnline = false, truncate = 12.some)</span>@text</li>
<span>
@userIdLink(author, withOnline = false, truncate = 12.some)
</span>
@text
</li>
} }
} }

View file

@ -1,6 +1,7 @@
package lila.common package lila.common
import java.text.Normalizer import java.text.Normalizer
import java.util.regex.Matcher.quoteReplacement
object String { object String {
@ -10,4 +11,11 @@ object String {
val slug = """[^\w-]""".r.replaceAllIn(normalized, "") val slug = """[^\w-]""".r.replaceAllIn(normalized, "")
slug.toLowerCase slug.toLowerCase
} }
final class Delocalizer(netDomain: String) {
private val regex = ("""\w+\.""" + quoteReplacement(netDomain)).r
def apply(url: String) = regex.replaceAllIn(url, netDomain)
}
} }

View file

@ -114,23 +114,18 @@ object Event {
case class Message(author: String, text: String) extends Event { case class Message(author: String, text: String) extends Event {
def typ = "message" def typ = "message"
def data = JsString("""<li class="%s%s">%s</li>""".format( def data = Json.obj("u" -> author, "t" -> escapeXml(text))
author, (author == "system") ?? " trans_me", escapeXml(text)))
override def owner = true override def owner = true
} }
case class WatcherMessage(author: Option[String], text: String) extends Event { // it *IS* a username, and not a user ID
// immediately used for rendering
case class WatcherMessage(username: Option[String], text: String) extends Event {
def typ = "message" def typ = "message"
def data = JsString(renderWatcherRoom(author, text)) def data = Json.obj("u" -> username, "t" -> escapeXml(text))
override def watcher = true override def watcher = true
} }
// TODO FIXME the username is @userId and there is no link
private def renderWatcherRoom(author: Option[String], text: String): String =
"""<li><span>%s</span>%s</li>""".format(
author.fold("Anonymous")("@" + _),
escapeXml(text))
object End extends Empty { object End extends Empty {
def typ = "end" def typ = "end"
} }

View file

@ -4,28 +4,31 @@ import play.api.libs.json._
import reactivemongo.bson._ import reactivemongo.bson._
import org.joda.time.DateTime import org.joda.time.DateTime
// it is really a username, not a user ID
case class Message( case class Message(
userId: String, user: Option[String],
text: String, text: String,
date: DateTime) { date: DateTime) {
def render = Json.obj("txt" -> text, "u" -> userId) def render = Json.obj("u" -> user, "t" -> text)
def isEmpty = text.isEmpty def isEmpty = text.isEmpty
} }
object Message { object Message {
def make(userId: String, text: String) = new Message( def make(user: Option[String], text: String) = new Message(
userId = userId, user = user,
text = text, text = text,
date = DateTime.now) date = DateTime.now)
import lila.db.Tube import lila.db.Tube
import Tube.Helpers._ import Tube.Helpers._
private def defaults = Json.obj("user" -> none[String])
private[lobby] lazy val tube = Tube[Message]( private[lobby] lazy val tube = Tube[Message](
(__.json update readDate('date)) andThen Json.reads[Message], (__.json update (merge(defaults) andThen readDate('date))) andThen Json.reads[Message],
Json.writes[Message] andThen (__.json update writeDate('date)), Json.writes[Message] andThen (__.json update writeDate('date)),
flags = Seq(_.NoId)) flags = Seq(_.NoId))
} }

View file

@ -1,24 +1,21 @@
package lila.lobby package lila.lobby
import lila.user.{ UserRepo, User, Room } import lila.user.{ UserRepo, User, Room }
import lila.user.tube.userTube
import tube.messageTube import tube.messageTube
import lila.db.api._ import lila.db.api._
private[lobby] final class Messenger(val netDomain: String) extends Room { private[lobby] final class Messenger(val netDomain: String) extends Room {
def apply(userId: String, text: String): Fu[Message] = for { def apply(userId: String, text: String): Fu[Message] = for {
userOption $find.byId[User](userId) userOption UserRepo byId userId
message (for { message (userMessage(userOption, text) map {
user userOption filter (_.canChat) toValid "This user cannot chat" case (u, t) Message.make(u.some, t)
msg createMessage(user, text) }).future
(u, t) = msg
} yield Message.make(u, t)).future
_ $insert(message) _ $insert(message)
} yield message } yield message
def system(text: String): Fu[Message] = def system(text: String): Fu[Message] =
Message.make(userId = "", text = text) |> { message Message.make(user = none, text = text) |> { message
$insert(message) inject message $insert(message) inject message
} }

View file

@ -39,13 +39,13 @@ private[lobby] final class Socket(
case Talk(u, txt) messenger(u, txt) effectFold ( case Talk(u, txt) messenger(u, txt) effectFold (
e logwarn(e.toString), e logwarn(e.toString),
message notifyVersion("talk", Json.obj( message notifyVersion("talk", Json.obj(
"u" -> message.userId, "u" -> message.user,
"txt" -> message.text "t" -> message.text
)) ))
) )
case SysTalk(txt) messenger system txt foreach { message case SysTalk(txt) messenger system txt foreach { message
notifyVersion("talk", Json.obj("txt" -> message.text)) notifyVersion("talk", Json.obj("t" -> message.text))
} }
case UnTalk(regex) (messenger remove regex) >>- case UnTalk(regex) (messenger remove regex) >>-

View file

@ -16,6 +16,7 @@ final class Env(
db: lila.db.Env, db: lila.db.Env,
hub: lila.hub.Env, hub: lila.hub.Env,
ai: lila.ai.Ai, ai: lila.ai.Ai,
getUsername: String Fu[Option[String]],
i18nKeys: lila.i18n.I18nKeys, i18nKeys: lila.i18n.I18nKeys,
scheduler: lila.common.Scheduler) { scheduler: lila.common.Scheduler) {
@ -31,6 +32,7 @@ final class Env(
val SocketTimeout = config duration "socket.timeout" val SocketTimeout = config duration "socket.timeout"
val FinisherLockTimeout = config duration "finisher.lock.timeout" val FinisherLockTimeout = config duration "finisher.lock.timeout"
val HijackTimeout = config duration "hijack.timeout" val HijackTimeout = config duration "hijack.timeout"
val NetDomain = config getString "net.domain"
} }
import settings._ import settings._
@ -72,7 +74,7 @@ final class Env(
finisher = finisher, finisher = finisher,
socketHub = socketHub) socketHub = socketHub)
lazy val messenger = new Messenger(i18nKeys) lazy val messenger = new Messenger(NetDomain, i18nKeys, getUsername)
lazy val eloCalculator = new chess.EloCalculator(false) lazy val eloCalculator = new chess.EloCalculator(false)
@ -122,6 +124,7 @@ object Env {
db = lila.db.Env.current, db = lila.db.Env.current,
hub = lila.hub.Env.current, hub = lila.hub.Env.current,
ai = lila.ai.Env.current.ai, ai = lila.ai.Env.current.ai,
getUsername = lila.user.Env.current.usernameOption,
i18nKeys = lila.i18n.Env.current.keys, i18nKeys = lila.i18n.Env.current.keys,
scheduler = lila.common.PlayApp.scheduler) scheduler = lila.common.PlayApp.scheduler)
} }

View file

@ -7,8 +7,14 @@ import chess.Color
import lila.game.Event import lila.game.Event
import tube.{ roomTube, watcherRoomTube } import tube.{ roomTube, watcherRoomTube }
import lila.db.api._ import lila.db.api._
import lila.user.UserRepo
final class Messenger(i18nKeys: I18nKeys) { import org.apache.commons.lang3.StringEscapeUtils.escapeXml
final class Messenger(
val netDomain: String,
i18nKeys: I18nKeys,
getUsername: String Fu[Option[String]]) extends lila.user.Room {
private val nbMessagesCopiedToRematch = 20 private val nbMessagesCopiedToRematch = 20
@ -31,7 +37,7 @@ final class Messenger(i18nKeys: I18nKeys) {
} yield () } yield ()
def playerMessage(ref: PovRef, text: String): Fu[List[Event.Message]] = def playerMessage(ref: PovRef, text: String): Fu[List[Event.Message]] =
cleanupText(text) zmap { t cleanupText(text).future flatMap { t
RoomRepo.addMessage(ref.gameId, ref.color.name, t) inject { RoomRepo.addMessage(ref.gameId, ref.color.name, t) inject {
Event.Message(ref.color.name, t) :: Nil Event.Message(ref.color.name, t) :: Nil
} }
@ -40,12 +46,12 @@ final class Messenger(i18nKeys: I18nKeys) {
def watcherMessage( def watcherMessage(
gameId: String, gameId: String,
userId: Option[String], userId: Option[String],
text: String): Fu[List[Event.WatcherMessage]] = text: String): Fu[List[Event.WatcherMessage]] = for {
cleanupText(text) zmap { t userOption userId.zmap(UserRepo.byId)
WatcherRoomRepo.addMessage(gameId, userId, t) inject { message userOrAnonMessage(userOption, text).future
Event.WatcherMessage(userId, text) :: Nil (u, t) = message
} _ WatcherRoomRepo.addMessage(gameId, u, t)
} } yield Event.WatcherMessage(u, t) :: Nil
def systemMessages(game: Game, messages: List[SelectI18nKey]): Fu[List[Event]] = def systemMessages(game: Game, messages: List[SelectI18nKey]): Fu[List[Event]] =
game.hasChat ?? { game.hasChat ?? {
@ -65,11 +71,6 @@ final class Messenger(i18nKeys: I18nKeys) {
} }
} }
private def cleanupText(text: String) = {
val cleanedUp = text.trim.replace(""""""", "'")
(cleanedUp.size <= 140 && cleanedUp.nonEmpty) option cleanedUp
}
private def messageToEn(message: SelectI18nKey): String = private def messageToEn(message: SelectI18nKey): String =
message(i18nKeys).en() message(i18nKeys).en()
} }

View file

@ -20,11 +20,11 @@ object WatcherRoom {
Json.reads[WatcherRoom], Json.reads[WatcherRoom],
Json.writes[WatcherRoom]) Json.writes[WatcherRoom])
def encode(userId: Option[String], text: String): String = def encode(username: Option[String], text: String): String =
~userId + "|" + text ~username + "|" + text
def decode(encoded: String): (Option[String], String) = def decode(encoded: String): (Option[String], String) =
encoded.span('|' !=) match { encoded.span('|' !=) match {
case (userId, rest) Some(userId).filter(_.nonEmpty) -> rest case (username, rest) Some(username).filter(_.nonEmpty) -> rest.drop(1)
} }
} }

View file

@ -10,9 +10,9 @@ object WatcherRoomRepo {
def addMessage( def addMessage(
id: String, id: String,
userId: Option[String], username: Option[String],
text: String): Funit = $update( text: String): Funit = $update(
$select(id), $select(id),
$push("messages", WatcherRoom.encode(userId, text)), $push("messages", WatcherRoom.encode(username, text)),
upsert = true) upsert = true)
} }

View file

@ -97,7 +97,7 @@ final class Env(
} }
} }
lazy val messenger = new Messenger(NetDomain) lazy val messenger = new Messenger(getUsername, NetDomain)
private[tournament] lazy val tournamentColl = db(CollectionTournament) private[tournament] lazy val tournamentColl = db(CollectionTournament)
private[tournament] lazy val roomColl = db(CollectionRoom) private[tournament] lazy val roomColl = db(CollectionRoom)

View file

@ -1,29 +1,31 @@
package lila.tournament package lila.tournament
import lila.db.api.$find import lila.db.api._
import tube.tournamentTube import tube.tournamentTube
import lila.user.{ User, UserRepo, Room UserRoom } import lila.user.{ User, UserRepo, Room UserRoom }
private[tournament] final class Messenger(val netDomain: String) extends UserRoom { private[tournament] final class Messenger(
getUsername: String Fu[Option[String]],
val netDomain: String) extends UserRoom {
import Room._ import Room._
def init(tour: Created): Fu[List[Message]] = for { def init(tour: Created): Fu[List[Message]] = for {
userOption UserRepo named tour.data.createdBy username getUsername(tour.data.createdBy) flatMap {
username = userOption.fold(tour.data.createdBy)(_.username) _.fold[Fu[String]](fufail("No username found"))(fuccess(_))
}
message systemMessage(tour, "%s creates the tournament" format username) message systemMessage(tour, "%s creates the tournament" format username)
} yield List(message) } yield List(message)
def userMessage(tournamentId: String, userId: String, text: String): Fu[Message] = for { def userMessage(tournamentId: String, userId: String, text: String): Fu[Message] = for {
userOption UserRepo named userId userOption UserRepo byId userId
tourOption $find byId tournamentId tourExists $count.exists($select(tournamentId))
message (for { message (for {
user userOption filter (_.canChat) toValid "This user cannot chat" _ Unit.validIf(tourExists, "No such tournament")
_ tourOption toValid "No such tournament" msg userMessage(userOption, text)
msg createMessage(user, text)
(u, t) = msg (u, t) = msg
} yield Message(u.some, t)).future } yield Message(u.some, t)).future
_ RoomRepo.addMessage(tournamentId, message) _ RoomRepo.addMessage(tournamentId, message)
} yield message } yield message
def systemMessage(tour: Tournament, text: String): Fu[Message] = def systemMessage(tour: Tournament, text: String): Fu[Message] =
@ -31,6 +33,6 @@ private[tournament] final class Messenger(val netDomain: String) extends UserRoo
RoomRepo.addMessage(tour.id, message) inject message RoomRepo.addMessage(tour.id, message) inject message
} }
def getMessages(tournamentId: String): Fu[List[Room.Message]] = def getMessages(tournamentId: String): Fu[List[Room.Message]] =
RoomRepo room tournamentId map (_.decodedMessages) RoomRepo room tournamentId map (_.decodedMessages)
} }

View file

@ -1,30 +1,31 @@
package lila.user package lila.user
import org.apache.commons.lang3.StringEscapeUtils.escapeXml
import java.util.regex.Matcher.quoteReplacement import java.util.regex.Matcher.quoteReplacement
trait Room { trait Room {
def netDomain: String def netDomain: String
def createMessage(user: User, text: String): Valid[(String, String)] = def userMessage(userOption: Option[User], text: String): Valid[(String, String)] =
if (user.isChatBan) !!("Chat banned " + user) userOption toValid "Anonymous cannot talk in this room" flatMap { user
else if (user.disabled) !!("User disabled " + user) if (user.isChatBan) !!("Chat banned " + user)
else escapeXml(text.replace(""""""", "'").trim take 140) |> { escaped else if (user.disabled) !!("User disabled " + user)
(escaped.nonEmpty).fold( else cleanupText(text) map { user.username -> _ }
success((
user.username,
urlRegex.replaceAllIn(escaped, m quoteReplacement(netDomain + "/" + (m group 1)))
)),
!!("Empty message")
)
} }
def userOrAnonMessage(userOption: Option[User], text: String): Valid[(Option[String], String)] =
cleanupText(text) map { userOption.map(_.username) -> _ }
def cleanupText(text: String): Valid[String] =
(text.replace(""""""", "'").trim take 140) |> { t
if (t.isEmpty) !!("Empty message")
else success(delocalize(noPrivateUrl(t)))
}
private def noPrivateUrl(str: String): String =
urlRegex.replaceAllIn(str, m quoteReplacement(netDomain + "/" + (m group 1)))
private val delocalize = new lila.common.String.Delocalizer(netDomain)
private val domainRegex = netDomain.replace(".", """\.""") private val domainRegex = netDomain.replace(".", """\.""")
private val urlRegex = (domainRegex + """/([\w-]{8})[\w-]{4}""").r private val urlRegex = (domainRegex + """/([\w-]{8})[\w-]{4}""").r
private def cleanupText(text: String) = {
val cleanedUp = text.trim.replace(""""""", "'")
(cleanedUp.size <= 140 && cleanedUp.nonEmpty) option cleanedUp
}
} }

View file

@ -21,6 +21,8 @@ object UserRepo {
val normalize = User normalize _ val normalize = User normalize _
def byId(id: ID): Fu[Option[User]] = $find byId id
def named(username: String): Fu[Option[User]] = $find byId normalize(username) def named(username: String): Fu[Option[User]] = $find byId normalize(username)
def nameds(usernames: List[String]): Fu[List[User]] = $find byIds usernames.map(normalize) def nameds(usernames: List[String]): Fu[List[User]] = $find byIds usernames.map(normalize)

View file

@ -203,6 +203,10 @@ var lichess_translations = [];
domain: document.domain.replace(/^\w+\.(.+)$/, '$1') domain: document.domain.replace(/^\w+\.(.+)$/, '$1')
}; };
$.userLink = function(u, limit) {
return (u || false) ? '<a class="user_link" href="/@/' + u + '">' + u + '</a>' : 'Anonymous';
}
var lichess = { var lichess = {
socket: null, socket: null,
socketDefaults: { socketDefaults: {
@ -587,7 +591,14 @@ var lichess_translations = [];
self.initTable(); self.initTable();
self.initClocks(); self.initClocks();
if (self.$chat) self.$chat.chat({ if (self.$chat) self.$chat.chat({
resize: true resize: true,
render: function(u, t) {
if (self.options.player.spectator) {
return '<li><span>' + $.userLink(u, 12) + '</span>' + urlToLink(t) + '</li>';
} else {
return '<li class="' + u + (u == 'system' ? ' trans_me' : '') + '">' + urlToLink(t) + '</li>';
}
}
}); });
self.$watchers.watchers(); self.$watchers.watchers();
if (self.isMyTurn() && self.options.game.turns == 0) { if (self.isMyTurn() && self.options.game.turns == 0) {
@ -638,9 +649,9 @@ var lichess_translations = [];
ack: function() { ack: function() {
clearTimeout(self.socketAckTimeout); clearTimeout(self.socketAckTimeout);
}, },
message: function(event) { message: function(e) {
self.element.queue(function() { self.element.queue(function() {
if (self.$chat) self.$chat.chat("append", event); if (self.$chat) self.$chat.chat("append", e.u, e.t);
self.element.dequeue(); self.element.dequeue();
}); });
}, },
@ -1182,18 +1193,7 @@ var lichess_translations = [];
set: function(users) { set: function(users) {
var self = this; var self = this;
if (users.length > 0) { if (users.length > 0) {
var html = [], self.list.html(_.map(users, $.userLink).join(", "));
user, i, w;
for (i in users) {
w = users[i];
if (w.indexOf("Anonymous") == 0) {
user = w;
} else {
user = '<a href="/@/' + w + '">' + w + '</a>';
}
html.push(user);
}
self.list.html(html.join(", "));
self.element.show(); self.element.show();
} else { } else {
self.element.hide(); self.element.hide();
@ -1204,8 +1204,8 @@ var lichess_translations = [];
$.widget("lichess.chat", { $.widget("lichess.chat", {
_create: function() { _create: function() {
this.options = $.extend({ this.options = $.extend({
render: function(t) { render: function(u, t) {
return urlToLink(t); console.debug("How to render " + u + " " + t + " in chat?");
}, },
resize: false resize: false
}, this.options); }, this.options);
@ -1252,15 +1252,14 @@ var lichess_translations = [];
} }
$toggle[0].checked = $toggle.data("enabled"); $toggle[0].checked = $toggle.data("enabled");
}, },
append: function(msg, u) { append: function(u, t) {
this._appendHtml(this.options.render(msg, u)); this._appendHtml(this.options.render(u, t));
}, },
appendMany: function(objs) { appendMany: function(objs) {
var self = this, var self = this,
html = ""; html = "";
$.each(objs, function() { $.each(objs, function() {
if (this.txt) html += self.options.render(this.txt, this.u); html += self.options.render(this.u, this.t);
else if (this.msg) html += self.options.render(this.msg, this.u);
}); });
this._appendHtml(html); this._appendHtml(html);
}, },
@ -1728,10 +1727,8 @@ var lichess_translations = [];
}); });
var $chat = $("div.lichess_chat").chat({ var $chat = $("div.lichess_chat").chat({
render: function(txt, u) { render: function(u, t) {
return (u || false) return (u || false) ? '<li><span><a class="user_link" href="/@/' + u + '">' + u.substr(0, 12) + '</a></span>' + urlToLink(t) + '</li>' : '<li>' + urlToLink(t) + '</li>';
? '<li><span><a class="user_link" href="/@/' + u + '">' + u.substr(0, 12) + '</a></span>' + urlToLink(txt) + '</li>'
: '<li>' + urlToLink(txt) + '</li>';
} }
}); });
@ -1757,7 +1754,7 @@ var lichess_translations = [];
}, },
events: { events: {
talk: function(e) { talk: function(e) {
$chat.chat('append', e.txt, e.u); $chat.chat('append', e.u, e.t);
}, },
untalk: function(e) { untalk: function(e) {
$chat.chat('remove', e.regex); $chat.chat('remove', e.regex);
@ -1927,8 +1924,8 @@ var lichess_translations = [];
var $watchers = $("div.watchers").watchers(); var $watchers = $("div.watchers").watchers();
var $chat = $("div.lichess_chat").chat({ var $chat = $("div.lichess_chat").chat({
render: function(txt, u) { render: function(u, t) {
return '<li><span><a class="user_link" href="/@/' + u + '">' + u.substr(0, 12) + '</a></span>' + urlToLink(txt) + '</li>'; return '<li><span>' + userLink(u, 12) + '</span>' + urlToLink(t) + '</li>';
} }
}); });
@ -1956,7 +1953,7 @@ var lichess_translations = [];
lichess.socket = new strongSocket(lichess.socketUrl + socketUrl, _ld_.version, $.extend(true, lichess.socketDefaults, { lichess.socket = new strongSocket(lichess.socketUrl + socketUrl, _ld_.version, $.extend(true, lichess.socketDefaults, {
events: { events: {
talk: function(e) { talk: function(e) {
$chat.chat('append', e.txt, e.u); $chat.chat('append', e.u, e.t);
}, },
start: start, start: start,
reload: reload, reload: reload,
@ -2004,8 +2001,8 @@ var lichess_translations = [];
ignoreUnknownMessages: true ignoreUnknownMessages: true
}, },
events: { events: {
message: function(event) { message: function(e) {
$chat.chat("append", event); $chat.chat("append", e.u, e.t);
}, },
crowd: function(event) { crowd: function(event) {
$watchers.watchers("set", event.watchers); $watchers.watchers("set", event.watchers);

1
todo
View file

@ -82,6 +82,7 @@ stream game export
show fen only after game is finished http://en.lichess.org/forum/lichess-feedback/please-disable-live-fen-notation?page=1 show fen only after game is finished http://en.lichess.org/forum/lichess-feedback/please-disable-live-fen-notation?page=1
I owe the admins decent tools I owe the admins decent tools
move db game.if (initialFen) to another collection to prevent systematic loading, and doc moving move db game.if (initialFen) to another collection to prevent systematic loading, and doc moving
tell opponent chat is disabled
DEPLOY p21 DEPLOY p21
---------- ----------