much progress on chat
This commit is contained in:
parent
9600b2383f
commit
3a4647fede
|
@ -175,11 +175,11 @@ private[controllers] trait LilaController
|
|||
chatAndPrefs(ctx) map { case (chat, pref) ⇒ Context(ctx, chat, pref) }
|
||||
}
|
||||
|
||||
import lila.chat.Chat
|
||||
import lila.chat.NamedChat
|
||||
import lila.pref.Pref
|
||||
private def chatAndPrefs(ctx: lila.user.UserContext): Fu[(Option[Chat], Option[Pref])] =
|
||||
ctx.me.fold(fuccess(none[Chat] -> none[Pref])) { me ⇒
|
||||
(HTTPRequest.isSynchronousHttp(ctx.req) ?? (Env.chat.api.get(me, Nil) map (_.some))) zip (Env.pref.api getPref me) map {
|
||||
private def chatAndPrefs(ctx: lila.user.UserContext): Fu[(Option[NamedChat], Option[Pref])] =
|
||||
ctx.me.fold(fuccess(none[NamedChat] -> none[Pref])) { me ⇒
|
||||
(HTTPRequest.isSynchronousHttp(ctx.req) ?? (Env.chat.api.getNamed(me, Nil) map (_.some))) zip (Env.pref.api getPref me) map {
|
||||
case (chat, pref) ⇒ chat -> pref.some
|
||||
}
|
||||
}
|
||||
|
|
13
app/templating/ChatHelper.scala
Normal file
13
app/templating/ChatHelper.scala
Normal file
|
@ -0,0 +1,13 @@
|
|||
package lila.app
|
||||
package templating
|
||||
|
||||
import lila.chat.Env.{ current ⇒ chatEnv }
|
||||
import lila.user.User
|
||||
|
||||
trait ChatHelper {
|
||||
|
||||
def chatNameChans(chans: List[lila.chat.Chan], as: User): List[lila.chat.NamedChan] = chans match {
|
||||
case Nil ⇒ Nil
|
||||
case cs ⇒ chatEnv.namer.chans(cs, as).await
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package lila.app
|
||||
package templating
|
||||
|
||||
import lila.api.Env.{ current ⇒ apiEnv }
|
||||
|
||||
import ornicar.scalalib
|
||||
import play.api.templates.Html
|
||||
|
||||
import lila.api.Env.{ current ⇒ apiEnv }
|
||||
|
||||
object Environment
|
||||
extends scalaz.syntax.ToIdOps
|
||||
with scalaz.std.OptionInstances
|
||||
|
@ -41,7 +41,8 @@ object Environment
|
|||
with AnalysisHelper
|
||||
with RelationHelper
|
||||
with TournamentHelper
|
||||
with IRCHelper {
|
||||
with IRCHelper
|
||||
with ChatHelper {
|
||||
|
||||
implicit val LilaHtmlMonoid = scalaz.Monoid.instance[Html](
|
||||
(a, b) ⇒ Html(a.body + b.body),
|
||||
|
|
|
@ -176,8 +176,8 @@ data-accept-languages="@acceptLanguages.mkString(",")">
|
|||
<div class="lines"></div>
|
||||
<div class="controls">
|
||||
<form action="#">
|
||||
<span class="mainChan"></span>
|
||||
<input class="input" value="" placeholder="@trans.talkInChat()" />
|
||||
<span class="invite"></span>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -188,7 +188,7 @@ data-accept-languages="@acceptLanguages.mkString(",")">
|
|||
@jsTag("deps.min.js")
|
||||
@signedJs.fold(jsTagCompiled("big.js"))(js => jsAt(js, false))
|
||||
@ctx.chat.map { c =>
|
||||
@embedJs("var _lc_ = " + toJson(c.addChans(chans).toJson))
|
||||
@embedJs("var _lc_ = " + toJson(c.addChans(chatNameChans(chans, c.chat.user)).toJson))
|
||||
}
|
||||
@moreJs
|
||||
@if(lang.language != "en") {
|
||||
|
|
|
@ -15,7 +15,8 @@ title = title,
|
|||
goodies = views.html.game.infoBox(pov, tour),
|
||||
chat = roomHtml.map(round.room(_, false)),
|
||||
underchat = underchat.some,
|
||||
signedJs = pov.game.rated option routes.Round.signedJs(pov.gameId) map (_.toString)) {
|
||||
signedJs = pov.game.rated option routes.Round.signedJs(pov.gameId) map (_.toString),
|
||||
chans = List(if (pov.game.hasAi) lila.chat.GameWatcherChan(pov.gameId) else lila.chat.GamePlayerChan(pov.gameId))) {
|
||||
<div class="lichess_game clearfix lichess_player_@color not_spectator"
|
||||
data-socket-url="@routes.Round.websocketPlayer(fullId)"
|
||||
data-table-url="@routes.Round.tablePlayer(fullId)"
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
title = title,
|
||||
goodies = views.html.game.infoBox(pov, tour),
|
||||
chat = round.room(roomHtml, true).some,
|
||||
underchat = underchat.some) {
|
||||
underchat = underchat.some,
|
||||
chans = List(lila.chat.GameWatcherChan(pov.gameId))) {
|
||||
@watcherGame(pov)
|
||||
@embedJs("var _ld_ = " + roundWatcherJsData(pov, version, false, ctx.pref))
|
||||
@analyse.link(pov, analysed)
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
|
||||
@tournament.layout(
|
||||
title = trans.tournaments.str(),
|
||||
goodies = goodies.some) {
|
||||
goodies = goodies.some,
|
||||
chans = List(lila.chat.TournamentLobbyChan)) {
|
||||
<div id="tournament" data-href="@routes.Tournament.homeReload()">
|
||||
@tournament.homeInner(createds, starteds, finisheds)
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(title: String, moreJs: Html = Html(""), chat: Option[Html] = None, goodies: Option[Html] = None, underchat: Option[Html] = None)(body: Html)(implicit ctx: Context)
|
||||
@(title: String, moreJs: Html = Html(""), chat: Option[Html] = None, goodies: Option[Html] = None, underchat: Option[Html] = None, chans: List[lila.chat.Chan] = Nil)(body: Html)(implicit ctx: Context)
|
||||
|
||||
@moreCss = {
|
||||
@cssTag("tournament.css")
|
||||
|
@ -11,6 +11,7 @@ moreJs = moreJs,
|
|||
chat = chat,
|
||||
goodies = goodies,
|
||||
active = siteMenu.tournament.some,
|
||||
underchat = underchat) {
|
||||
underchat = underchat,
|
||||
chans = chans) {
|
||||
@body
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ cssClass = "tournament_chat")(tournament.show.room(messages))
|
|||
title = title,
|
||||
goodies = goodies.some,
|
||||
chat = chat.some,
|
||||
underchat = underchat.some) {
|
||||
underchat = underchat.some,
|
||||
chans = List(lila.chat.TournamentChan(tour.id))) {
|
||||
<div
|
||||
id="tournament"
|
||||
data-href="@routes.Tournament.reload(tour.id)"
|
||||
|
|
|
@ -2,14 +2,14 @@ package lila.api
|
|||
|
||||
import play.api.mvc.{ Request, RequestHeader }
|
||||
|
||||
import lila.chat.Chat
|
||||
import lila.chat.NamedChat
|
||||
import lila.pref.Pref
|
||||
import lila.user.{ UserContext, HeaderUserContext, BodyUserContext }
|
||||
|
||||
sealed trait Context extends lila.user.UserContextWrapper {
|
||||
|
||||
val userContext: UserContext
|
||||
val chat: Option[Chat]
|
||||
val chat: Option[NamedChat]
|
||||
val pref: Pref
|
||||
|
||||
def currentTheme =
|
||||
|
@ -23,15 +23,15 @@ sealed trait Context extends lila.user.UserContextWrapper {
|
|||
|
||||
sealed abstract class BaseContext(
|
||||
val userContext: lila.user.UserContext,
|
||||
val chat: Option[Chat],
|
||||
val chat: Option[NamedChat],
|
||||
val pref: Pref) extends Context
|
||||
|
||||
final class BodyContext(val bodyContext: BodyUserContext, chatOption: Option[Chat], p: Pref)
|
||||
final class BodyContext(val bodyContext: BodyUserContext, chatOption: Option[NamedChat], p: Pref)
|
||||
extends BaseContext(bodyContext, chatOption, p) {
|
||||
def body = bodyContext.body
|
||||
}
|
||||
|
||||
final class HeaderContext(val headerContext: HeaderUserContext, chatOption: Option[Chat], p: Pref)
|
||||
final class HeaderContext(val headerContext: HeaderUserContext, chatOption: Option[NamedChat], p: Pref)
|
||||
extends BaseContext(headerContext, chatOption, p)
|
||||
|
||||
object Context {
|
||||
|
@ -39,9 +39,9 @@ object Context {
|
|||
def apply(req: RequestHeader): HeaderContext =
|
||||
new HeaderContext(UserContext(req, none), none, Pref.default)
|
||||
|
||||
def apply(userContext: HeaderUserContext, chat: Option[Chat], pref: Option[Pref]): HeaderContext =
|
||||
def apply(userContext: HeaderUserContext, chat: Option[NamedChat], pref: Option[Pref]): HeaderContext =
|
||||
new HeaderContext(userContext, chat, pref | Pref.default)
|
||||
|
||||
def apply(userContext: BodyUserContext, chat: Option[Chat], pref: Option[Pref]): BodyContext =
|
||||
def apply(userContext: BodyUserContext, chat: Option[NamedChat], pref: Option[Pref]): BodyContext =
|
||||
new BodyContext(userContext, chat, pref | Pref.default)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import lila.user.{ User, UserRepo }
|
|||
import tube.lineTube
|
||||
|
||||
private[chat] final class Api(
|
||||
namer: Namer,
|
||||
flood: lila.security.Flood,
|
||||
prefApi: lila.pref.PrefApi,
|
||||
netDomain: String) {
|
||||
|
@ -30,6 +31,13 @@ private[chat] final class Api(
|
|||
get(u, extraChans.map(Chan.parse).flatten)
|
||||
}
|
||||
|
||||
def getNamed(user: User, extraChans: List[Chan]): Fu[NamedChat] =
|
||||
get(user, extraChans) flatMap namer.chat
|
||||
def getNamed(userId: String, extraChans: List[String]): Fu[NamedChat] =
|
||||
(UserRepo byId userId) flatten s"No such user: $userId" flatMap { u ⇒
|
||||
getNamed(u, extraChans.map(Chan.parse).flatten)
|
||||
}
|
||||
|
||||
def write(chan: String, userId: String, text: String): Fu[Option[Line]] = {
|
||||
import Writer._
|
||||
UserRepo byId userId flatMap {
|
||||
|
|
|
@ -5,15 +5,8 @@ import play.api.libs.json._
|
|||
sealed trait Chan {
|
||||
def typ: String
|
||||
def key: String
|
||||
def name: String
|
||||
def idOption: Option[String]
|
||||
|
||||
def toJson = Json.obj(
|
||||
"typ" -> typ,
|
||||
"id" -> idOption,
|
||||
"key" -> key,
|
||||
"name" -> name)
|
||||
|
||||
override def toString = key
|
||||
}
|
||||
|
||||
|
@ -22,7 +15,7 @@ sealed abstract class StaticChan(val typ: String, val name: String) extends Chan
|
|||
val idOption = none
|
||||
}
|
||||
|
||||
sealed abstract class IdChan(val typ: String, val id: String, val name: String) extends Chan {
|
||||
sealed abstract class IdChan(val typ: String, val id: String) extends Chan {
|
||||
val key = s"${typ}_$id"
|
||||
val idOption = id.some
|
||||
}
|
||||
|
@ -30,9 +23,17 @@ sealed abstract class IdChan(val typ: String, val id: String, val name: String)
|
|||
object LichessChan extends StaticChan(Chan.typ.lichess, "Lichess")
|
||||
object LobbyChan extends StaticChan(Chan.typ.lobby, "Lobby")
|
||||
object TvChan extends StaticChan(Chan.typ.tv, "TV")
|
||||
case class GameChan(i: String, n: String) extends IdChan(Chan.typ.game, i, n)
|
||||
case class TournamentChan(i: String, n: String) extends IdChan(Chan.typ.game, i, n)
|
||||
case class UserChan(i: String, n: String) extends IdChan(Chan.typ.game, i, n)
|
||||
case class GameWatcherChan(i: String) extends IdChan(Chan.typ.gameWatcher, i)
|
||||
case class GamePlayerChan(i: String) extends IdChan(Chan.typ.gamePlayer, i)
|
||||
object TournamentLobbyChan extends StaticChan(Chan.typ.tournamentLobby, "Tournament Lobby")
|
||||
case class TournamentChan(i: String) extends IdChan(Chan.typ.tournament, i)
|
||||
|
||||
case class NamedChan(chan: Chan, name: String) {
|
||||
|
||||
def toJson = Json.obj(
|
||||
"key" -> chan.key,
|
||||
"name" -> name)
|
||||
}
|
||||
|
||||
object Chan {
|
||||
|
||||
|
@ -40,15 +41,21 @@ object Chan {
|
|||
val lichess = "lichess"
|
||||
val lobby = "lobby"
|
||||
val tv = "tv"
|
||||
val game = "game"
|
||||
val gameWatcher = "gameWatcher"
|
||||
val gamePlayer = "gamePlayer"
|
||||
val tournamentLobby = "tournamentLobby"
|
||||
val tournament = "tournament"
|
||||
}
|
||||
|
||||
def apply(typ: String, idOption: Option[String]): Option[Chan] = typ match {
|
||||
case Chan.typ.lichess ⇒ LichessChan.some
|
||||
case Chan.typ.lobby ⇒ LobbyChan.some
|
||||
case Chan.typ.tv ⇒ TvChan.some
|
||||
case Chan.typ.game ⇒ idOption map { GameChan(_, "Some game") }
|
||||
case _ ⇒ none
|
||||
case Chan.typ.lichess ⇒ LichessChan.some
|
||||
case Chan.typ.lobby ⇒ LobbyChan.some
|
||||
case Chan.typ.tv ⇒ TvChan.some
|
||||
case Chan.typ.gameWatcher ⇒ idOption map GameWatcherChan
|
||||
case Chan.typ.gamePlayer ⇒ idOption map GamePlayerChan
|
||||
case Chan.typ.tournamentLobby ⇒ TournamentLobbyChan.some
|
||||
case Chan.typ.tournament ⇒ idOption map TournamentChan
|
||||
case _ ⇒ none
|
||||
}
|
||||
|
||||
def parse(str: String): Option[Chan] = str.split('_') match {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package lila.chat
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
import play.api.libs.json._
|
||||
|
||||
import lila.user.User
|
||||
|
@ -14,13 +16,18 @@ case class Chat(
|
|||
def chanKeys = chans map (_.key)
|
||||
|
||||
def addChans(cs: List[Chan]) = copy(chans = (chans ::: cs).distinct)
|
||||
}
|
||||
|
||||
case class NamedChat(chat: Chat, namedChans: List[NamedChan]) {
|
||||
|
||||
def toJson = Json.obj(
|
||||
"user" -> user.username,
|
||||
"lines" -> (lines map (_.toJson)),
|
||||
"chans" -> JsObject(chans map { c ⇒ c.key -> c.toJson }),
|
||||
"activeChans" -> activeChanKeys,
|
||||
"mainChan" -> mainChanKey.filter(activeChanKeys.contains))
|
||||
"user" -> chat.user.username,
|
||||
"lines" -> (chat.lines map (_.toJson)),
|
||||
"chans" -> JsObject(namedChans map { c ⇒ c.chan.key -> c.toJson }),
|
||||
"activeChans" -> chat.activeChanKeys,
|
||||
"mainChan" -> chat.mainChanKey.filter(chat.activeChanKeys.contains))
|
||||
|
||||
def addChans(chans: List[NamedChan]) = copy(namedChans = (namedChans ::: chans).distinct)
|
||||
}
|
||||
|
||||
object Chat {
|
||||
|
|
|
@ -13,6 +13,7 @@ import lila.user.UserRepo
|
|||
|
||||
private[chat] final class ChatActor(
|
||||
api: Api,
|
||||
namer: Namer,
|
||||
bus: Bus,
|
||||
prefApi: PrefApi) extends Actor {
|
||||
|
||||
|
@ -36,7 +37,7 @@ private[chat] final class ChatActor(
|
|||
_.withChan(chan, value)
|
||||
))) andThen {
|
||||
case _ ⇒ {
|
||||
member setActiveChan(chan, value)
|
||||
member setActiveChan (chan, value)
|
||||
reloadChat(member)
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +58,7 @@ private[chat] final class ChatActor(
|
|||
}
|
||||
}
|
||||
|
||||
case SocketEnter(uid, member, socket) ⇒ member.userId foreach { userId ⇒
|
||||
case SocketEnter(uid, member) ⇒ member.userId foreach { userId ⇒
|
||||
members += (uid -> ChatMember(uid, userId, member.troll, member.channel))
|
||||
}
|
||||
|
||||
|
@ -72,8 +73,8 @@ private[chat] final class ChatActor(
|
|||
}
|
||||
|
||||
private def reloadChat(member: ChatMember) {
|
||||
api.get(member.userId, member.extraChans) foreach {
|
||||
case chat: Chat ⇒ member.channel push Socket.makeMessage("chat.reload", chat.toJson)
|
||||
api.getNamed(member.userId, member.extraChans) foreach { chat ⇒
|
||||
member.channel push Socket.makeMessage("chat.reload", chat.toJson)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ case class ChatMember(
|
|||
private var privateActiveChans = Set[String]()
|
||||
|
||||
def wants(line: Line) =
|
||||
(troll || !line.troll) && (privateActiveChans.pp contains line.chan.key.pp pp)
|
||||
(troll || !line.troll) && (privateActiveChans contains line.chan.key)
|
||||
|
||||
def extraChans = privateExtraChans
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import lila.common.PimpedConfig._
|
|||
final class Env(
|
||||
config: Config,
|
||||
db: lila.db.Env,
|
||||
getUsername: String ⇒ Fu[String],
|
||||
flood: lila.security.Flood,
|
||||
prefApi: lila.pref.PrefApi,
|
||||
system: ActorSystem) {
|
||||
|
@ -21,12 +22,16 @@ final class Env(
|
|||
private[chat] lazy val lineColl = db(CollectionLine)
|
||||
|
||||
lazy val api = new Api(
|
||||
namer = namer,
|
||||
flood = flood,
|
||||
prefApi = prefApi,
|
||||
netDomain = NetDomain)
|
||||
|
||||
lazy val namer = new Namer(getUsername)
|
||||
|
||||
system.actorOf(Props(new ChatActor(
|
||||
api = api,
|
||||
api = api,
|
||||
namer = namer,
|
||||
bus = system.lilaBus,
|
||||
prefApi = prefApi)))
|
||||
}
|
||||
|
@ -36,6 +41,7 @@ object Env {
|
|||
lazy val current: Env = "[boot] chat" describes new Env(
|
||||
config = lila.common.PlayApp loadConfig "chat",
|
||||
db = lila.db.Env.current,
|
||||
getUsername = lila.user.Env.current.usernameOrAnonymous,
|
||||
flood = lila.security.Env.current.flood,
|
||||
prefApi = lila.pref.Env.current.api,
|
||||
system = lila.common.PlayApp.system)
|
||||
|
|
50
modules/chat/src/main/Namer.scala
Normal file
50
modules/chat/src/main/Namer.scala
Normal file
|
@ -0,0 +1,50 @@
|
|||
package lila.chat
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Future
|
||||
|
||||
import lila.game.{ GameRepo, Game }
|
||||
import lila.memo.AsyncCache
|
||||
import lila.tournament.TournamentRepo
|
||||
import lila.user.User
|
||||
|
||||
private[chat] final class Namer(getUsername: String ⇒ Fu[String]) {
|
||||
|
||||
def chat(c: Chat): Fu[NamedChat] =
|
||||
chans(c.chans, c.user) map { NamedChat(c, _) }
|
||||
|
||||
def chan(c: Chan, as: User): Fu[NamedChan] =
|
||||
chanCache(c -> as) map { NamedChan(c, _) }
|
||||
|
||||
def chans(cs: List[Chan], as: User): Fu[List[NamedChan]] =
|
||||
Future.traverse(cs) { c ⇒ chan(c, as) }
|
||||
|
||||
private val chanCache = AsyncCache(nameChan, timeToLive = 30 minutes)
|
||||
|
||||
private def nameChan(data: (Chan, User)): Fu[String] = data match {
|
||||
|
||||
case (static: StaticChan, _) ⇒ fuccess(static.name)
|
||||
|
||||
case (c@GameWatcherChan(id), _) ⇒
|
||||
GameRepo game id flatten s"No game for chan $c" flatMap nameWatcherChan
|
||||
|
||||
case (c@GamePlayerChan(id), user) ⇒
|
||||
GameRepo game id flatten s"No game for chan $c" flatMap { game ⇒
|
||||
(game player user).fold(nameWatcherChan(game)) { player ⇒
|
||||
lila.game.Namer.player(game opponent player, false)(getUsername) map { opponent ⇒
|
||||
s"Game: $opponent"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case (c@TournamentChan(id), _) ⇒
|
||||
TournamentRepo nameById id flatten s"No tournament for chan $c" map {
|
||||
_ + " tournament"
|
||||
}
|
||||
|
||||
case (c, _) ⇒ fuccess(c.toString)
|
||||
}
|
||||
|
||||
private def nameWatcherChan(game: Game) =
|
||||
lila.game.Namer.players(game, false)(getUsername) map { case (p1, p2) ⇒ s"$p1 vs $p2" }
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
package lila.game
|
||||
|
||||
import chess.Clock
|
||||
|
||||
import lila.user.User
|
||||
|
||||
object Namer {
|
||||
|
||||
def player(player: Player, withRating: Boolean = true)(getUsername: String ⇒ Fu[String]): Fu[String] =
|
||||
def players(game: Game, withRatings: Boolean = true)(implicit getUsername: String ⇒ Fu[String]): Fu[(String, String)] =
|
||||
player(game.firstPlayer, withRatings) zip player(game.secondPlayer, withRatings)
|
||||
|
||||
def player(player: Player, withRating: Boolean = true)(implicit getUsername: String ⇒ Fu[String]): Fu[String] =
|
||||
player.aiLevel.fold(
|
||||
player.userId.fold(fuccess(User.anonymous)) { id ⇒
|
||||
getUsername(id) map { username ⇒
|
||||
withRating.fold(
|
||||
"%s (%s)".format(username, player.rating getOrElse "?"),
|
||||
s"$username (${player.rating getOrElse "?"})",
|
||||
username)
|
||||
}
|
||||
}) { level ⇒ fuccess("A.I. level " + level) }
|
||||
|
|
|
@ -130,7 +130,7 @@ abstract class SocketActor[M <: SocketMember](uidTtl: Duration) extends Socket w
|
|||
eject(uid)
|
||||
members = members + (uid -> member)
|
||||
setAlive(uid)
|
||||
lilaBus.publish(SocketEnter(uid, member, self), 'socketDoor)
|
||||
lilaBus.publish(SocketEnter(uid, member), 'socketDoor)
|
||||
}
|
||||
|
||||
def setAlive(uid: String) { aliveUids put uid }
|
||||
|
|
|
@ -13,7 +13,7 @@ case class PingVersion(uid: String, version: Int)
|
|||
case object Broom
|
||||
case class Quit(uid: String)
|
||||
|
||||
case class SocketEnter[M <: SocketMember](uid: String, member: M, socket: ActorRef)
|
||||
case class SocketEnter[M <: SocketMember](uid: String, member: M)
|
||||
case class SocketLeave(uid: String)
|
||||
|
||||
case class LiveGames(uid: String, gameIds: List[String])
|
||||
|
|
|
@ -21,6 +21,9 @@ object TournamentRepo {
|
|||
|
||||
def byId(id: String): Fu[Option[Tournament]] = $find byId id
|
||||
|
||||
def nameById(id: String): Fu[Option[String]] =
|
||||
$primitive.one($select(id), "name")(_.asOpt[String])
|
||||
|
||||
def createdById(id: String): Fu[Option[Created]] = byIdAs(id, asCreated)
|
||||
|
||||
def startedById(id: String): Fu[Option[Started]] = byIdAs(id, asStarted)
|
||||
|
|
|
@ -64,7 +64,8 @@ object ApplicationBuild extends Build {
|
|||
libraryDependencies ++= provided(play.api, scalastic)
|
||||
)
|
||||
|
||||
lazy val chat = project("chat", Seq(common, db, hub, socket, user, security, pref)).settings(
|
||||
lazy val chat = project("chat", Seq(
|
||||
common, db, hub, socket, user, security, pref, game, tournament)).settings(
|
||||
libraryDependencies ++= provided(
|
||||
play.api, RM, PRM)
|
||||
)
|
||||
|
|
|
@ -841,6 +841,7 @@ var storage = {
|
|||
self.$lines = self.element.find('.lines');
|
||||
self.$chans = self.element.find('.chans');
|
||||
self.$form = self.element.find('.controls form');
|
||||
self.$invite = self.$form.find('.invite');
|
||||
self.$input = self.$form.find('input');
|
||||
|
||||
self.reload(_lc_);
|
||||
|
@ -858,6 +859,9 @@ var storage = {
|
|||
self.$chans.on('click', 'input', function(e) {
|
||||
self._setActive($(this).attr('name'), $(this).prop('checked'));
|
||||
});
|
||||
$(window).resize(function() {
|
||||
self._renderForm();
|
||||
});
|
||||
|
||||
$('body').on('socket.open', function() {
|
||||
lichess.socket.send('chat.register', {
|
||||
|
@ -885,6 +889,9 @@ var storage = {
|
|||
self.lines.push(l);
|
||||
self.$lines.append(self._renderLine(l)).scrollTop(999999);
|
||||
},
|
||||
_disablePlaceholder: _.once(function() {
|
||||
this.$input.attr('placeholder', '');
|
||||
}),
|
||||
_tell: function() {
|
||||
var self = this;
|
||||
if (!self.mainChan) return false;
|
||||
|
@ -903,6 +910,7 @@ var storage = {
|
|||
_setActive: function(chan, value, setMain) {
|
||||
var self = this;
|
||||
if (!self._exists(chan)) return;
|
||||
self._disablePlaceholder();
|
||||
setMain = setMain | false;
|
||||
if (value) self.activeChans.push(chan);
|
||||
else self.activeChans = _.without(self.activeChans, chan);
|
||||
|
@ -919,6 +927,7 @@ var storage = {
|
|||
// chan can be null, then there is no main chan
|
||||
_setMain: function(chan) {
|
||||
var self = this;
|
||||
self._disablePlaceholder();
|
||||
if (chan !== null && !self._isActive(chan)) {
|
||||
self._setActive(chan, true, true);
|
||||
} else {
|
||||
|
@ -982,9 +991,14 @@ var storage = {
|
|||
if (self.mainChan) {
|
||||
var index = self._chanIndex(self.mainChan);
|
||||
var mainChan = self.chans[self.mainChan];
|
||||
self.$form.show().find('.mainChan')
|
||||
self.$form.show();
|
||||
self.$invite
|
||||
.text(self.user + ' ● ' + mainChan.name)
|
||||
.attr('class', 'mainChan ' + self._colorClass(index));
|
||||
.attr('class', 'invite ' + self._colorClass(index));
|
||||
self.$input.css({
|
||||
width: (self.$form.width() - self.$invite.width() - 50) + 'px',
|
||||
paddingLeft: (self.$invite.width() + 40) + 'px'
|
||||
});
|
||||
} else {
|
||||
self.$form.hide();
|
||||
}
|
||||
|
|
|
@ -455,12 +455,13 @@ div.footer div.right {
|
|||
border-bottom: 1px solid #2a2a2a;
|
||||
cursor: pointer;
|
||||
}
|
||||
#chat .chan:hover, #chat .chan.main {
|
||||
#chat .chan.main {
|
||||
background: #030303;
|
||||
}
|
||||
#chat .chan > .name {
|
||||
display: block;
|
||||
margin-right: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#chat div.check {
|
||||
float: right;
|
||||
|
@ -509,12 +510,11 @@ div.footer div.right {
|
|||
}
|
||||
#chat > .room {
|
||||
margin-left: 201px;
|
||||
width: 100%;
|
||||
}
|
||||
#chat > .room > .lines {
|
||||
height: 171px;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
/* overflow-y: auto; */
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
#chat .line {
|
||||
|
@ -522,6 +522,7 @@ div.footer div.right {
|
|||
padding: 2px 10px;
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
color: #b0b0b0;
|
||||
}
|
||||
#chat .line.main {
|
||||
opacity: 1;
|
||||
|
@ -539,21 +540,19 @@ div.footer div.right {
|
|||
display: inline-block;
|
||||
}
|
||||
#chat .controls > form {
|
||||
width: 100%;
|
||||
background: #1a1a1a;
|
||||
display: none;
|
||||
position: relative;
|
||||
}
|
||||
#chat .controls > form .mainChan {
|
||||
#chat .controls > form .invite {
|
||||
padding: 0 10px;
|
||||
display: block;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
position: relative;
|
||||
margin-right: 12px;
|
||||
background: #0a0a0a;
|
||||
}
|
||||
#chat .controls > form .mainChan:after {
|
||||
#chat .controls > form .invite:after {
|
||||
content:"";
|
||||
border-top: 14px solid transparent;
|
||||
border-bottom: 14px solid transparent;
|
||||
|
@ -563,11 +562,16 @@ div.footer div.right {
|
|||
top: 0;
|
||||
}
|
||||
#chat .controls > form .input {
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 20px;
|
||||
padding: 4px 10px;
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
#chat .controls > form .input:focus {
|
||||
background: #2a2a2a;
|
||||
color: #d0d0d0;
|
||||
}
|
||||
#chat .color0 {
|
||||
/* green */
|
||||
|
|
Loading…
Reference in a new issue