diff --git a/app/templating/UserHelper.scala b/app/templating/UserHelper.scala index 09138a3614..aeaa05852e 100644 --- a/app/templating/UserHelper.scala +++ b/app/templating/UserHelper.scala @@ -56,6 +56,24 @@ trait UserHelper { self: I18nHelper with StringHelper => } } + def lightUserLink( + user: LightUser, + cssClass: Option[String] = None, + withOnline: Boolean = true, + withTitle: Boolean = true, + truncate: Option[Int] = None, + params: String = ""): Html = Html { + userIdNameLink( + userId = user.id, + username = user.name, + title = user.title, + cssClass = cssClass, + withOnline = withOnline, + withTitle = withTitle, + truncate = truncate, + params = params) + } + def userIdLink( userId: String, cssClass: Option[String]): Html = userIdLink(userId.some, cssClass) diff --git a/app/views/pool/homeList.scala.html b/app/views/pool/homeList.scala.html index 6fe9518425..5b3ca0c07e 100644 --- a/app/views/pool/homeList.scala.html +++ b/app/views/pool/homeList.scala.html @@ -5,18 +5,18 @@ case (p, it) => {

@p.setup.name

- @defining(4) { displayedUsers => - + @defining(4) { displayedPlayers => +
- @p.sortedUsers.take(displayedUsers).map { user => + @p.playersAround(ctx.me, displayedPlayers).map { player => - - + + } - @defining(p.nbUsers - displayedUsers) { moreUsers => - @if(moreUsers > 0) { - + @defining(p.nbPlayers - displayedPlayers) { morePlayers => + @if(morePlayers > 0) { + } } diff --git a/app/views/pool/standing.scala.html b/app/views/pool/standing.scala.html index c4b6186df1..9cb5f91908 100644 --- a/app/views/pool/standing.scala.html +++ b/app/views/pool/standing.scala.html @@ -4,20 +4,20 @@
@userLink(user, withRating = false)@p.setup.glickoLens(user).intRating@lightUserLink(player.user)@player.rating
And 15 more
And @morePlayers more
- + - @p.rankedUsers.map { - case (user, rank) => { - + @p.rankedPlayers.map { + case (player, rank) => { + } diff --git a/modules/pool/src/main/Env.scala b/modules/pool/src/main/Env.scala index 93ed54d435..d86713673d 100644 --- a/modules/pool/src/main/Env.scala +++ b/modules/pool/src/main/Env.scala @@ -48,6 +48,7 @@ final class Env( history = new History(ttl = HistoryMessageTtl), uidTimeout = UidTimeout, lightUser = lightUser, + isOnline = isOnline, renderer = hub.actor.renderer) }), name = SocketName) diff --git a/modules/pool/src/main/Player.scala b/modules/pool/src/main/Player.scala new file mode 100644 index 0000000000..d3b3eff1b5 --- /dev/null +++ b/modules/pool/src/main/Player.scala @@ -0,0 +1,6 @@ +package lila.pool + +case class Player(user: lila.common.LightUser, rating: Int) { + + def is(p: Player) = user.id == p.user.id +} diff --git a/modules/pool/src/main/Pool.scala b/modules/pool/src/main/Pool.scala index 4e5fac570f..d05494a9c4 100644 --- a/modules/pool/src/main/Pool.scala +++ b/modules/pool/src/main/Pool.scala @@ -1,28 +1,43 @@ package lila.pool +import lila.common.LightUser import lila.user.User case class Pool( setup: PoolSetup, - users: List[User]) { + players: List[Player]) { - lazy val sortedUsers = users.sortBy(u => -setup.glickoLens(u).intRating) + lazy val sortedPlayers = players.sortBy(-_.rating) - lazy val rankedUsers = sortedUsers.zipWithIndex map { - case (user, rank) => user -> (rank + 1) + lazy val rankedPlayers = sortedPlayers.zipWithIndex map { + case (player, rank) => player -> (rank + 1) } - lazy val nbUsers = users.size + lazy val nbPlayers = players.size - def contains(u: User) = users contains u + def contains(userId: String): Boolean = players exists (_.user.id == userId) + def contains(u: User): Boolean = contains(u.id) + def contains(p: Player): Boolean = contains(p.user.id) - def withUser(u: User) = copy(users = u :: users).distinctUsers + def withPlayer(p: Player) = copy(players = p :: players).distinctPlayers + def withUser(u: User) = withPlayer(Player( + LightUser(u.id, u.username, u.title), + setup.glickoLens(u).intRating + )) - private def distinctUsers = copy( - users = users.map { u => - u.id -> u + def filterPlayers(cond: Player => Boolean) = copy(players = players filter cond) + + private def distinctPlayers = copy( + players = players.map { p => + p.user.id -> p }.toMap.values.toList ) - def withoutUser(u: User) = copy(users = users filter (_.id != u.id)) + def withoutPlayer(p: Player) = copy(players = players filterNot (_ is p)) + def withoutUser(u: User) = copy(players = players filterNot (_.user.id == u.id)) + + def playersAround(uo: Option[User], nb: Int) = uo.fold(sortedPlayers take nb) { u => + val rating = setup.glickoLens(u).intRating + players.sortBy(p => math.abs(rating - p.rating)) take nb sortBy (-_.rating) + } } diff --git a/modules/pool/src/main/PoolActor.scala b/modules/pool/src/main/PoolActor.scala index e9c2487dc8..1ddd9049a3 100644 --- a/modules/pool/src/main/PoolActor.scala +++ b/modules/pool/src/main/PoolActor.scala @@ -8,18 +8,26 @@ import play.api.libs.json._ import actorApi._ import lila.common.LightUser +import lila.hub.actorApi.SendTos import lila.socket.actorApi.{ Connected => _, _ } import lila.socket.{ SocketActor, History, Historical } -import lila.hub.actorApi.SendTos +import lila.user.User private[pool] final class PoolActor( setup: PoolSetup, val history: History, lightUser: String => Option[LightUser], + isOnline: String => Boolean, renderer: ActorSelection, uidTimeout: Duration) extends SocketActor[Member](uidTimeout) with Historical[Member] { private var pool = Pool(setup, Nil) + lila.user.UserRepo randomDudes scala.util.Random.nextInt(500) foreach { users => + pool = users.foldLeft(pool)(_ withUser _) + } + + // last time each user waved to the pool + private val wavers = new lila.memo.ExpireSetMemo(10 seconds) def receiveSpecific = { @@ -27,12 +35,23 @@ private[pool] final class PoolActor( case Enter(user) => pool = pool withUser user + wavers put user.id sender ! true + case Wave(user) => + wavers put user.id + case Leave(user) => pool = pool withoutUser user sender ! true + case Broom => + broom + pool = pool filterPlayers { p => + wavers get p.user.id + } + pool.players map (_.user.id) filter isOnline foreach wavers.put + case GetVersion => sender ! history.version // case StartGame(game) => game.players foreach { player => @@ -49,7 +68,7 @@ private[pool] final class PoolActor( import makeTimeout.short renderer ? RemindPool(pool) foreach { case html: play.twirl.api.Html => - val event = SendTos(pool.users.map(_.id).toSet, Json.obj( + val event = SendTos(pool.players.map(_.user.id).toSet, Json.obj( "t" -> "poolReminder", "d" -> Json.obj( "id" -> pool.setup.id, @@ -58,7 +77,7 @@ private[pool] final class PoolActor( context.system.lilaBus.publish(event, 'users) } - case Reload => notifyReload + case Reload => notifyReload case PingVersion(uid, v) => ping(uid) @@ -66,8 +85,6 @@ private[pool] final class PoolActor( history.since(v).fold(resync(m))(_ foreach sendMessage(m)) } - case Broom => broom - case lila.chat.actorApi.ChatLine(_, line) => line match { case line: lila.chat.UserLine => notifyVersionTrollable("message", lila.chat.Line toJson line, troll = line.troll) diff --git a/modules/pool/src/main/actorApi.scala b/modules/pool/src/main/actorApi.scala index b9e042e9ce..42634fdc9f 100644 --- a/modules/pool/src/main/actorApi.scala +++ b/modules/pool/src/main/actorApi.scala @@ -30,6 +30,7 @@ private[pool] case object GetPool private[pool] case object Reload private[pool] case class Enter(user: User) private[pool] case class Leave(user: User) +private[pool] case class Wave(user: User) private[pool] case object Pairing private[pool] case object CheckLeaders case class RemindPool(pool: Pool) diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala index dccede8336..6e69a28dce 100644 --- a/modules/user/src/main/UserRepo.scala +++ b/modules/user/src/main/UserRepo.scala @@ -72,6 +72,8 @@ trait UserRepo { def usernameById(id: ID) = $primitive.one($select(id), F.username)(_.asOpt[String]) + def randomDudes(nb: Int) = $find($query(stableGoodLadSelect) sort BSONDocument("count.games" -> -1) skip scala.util.Random.nextInt(5000), nb) + def rank(user: User) = $count(enabledSelect ++ Json.obj(F.rating -> $gt(Glicko.default.rating))) map (1+) def orderByGameCount(u1: String, u2: String): Fu[Option[(String, String)]] = diff --git a/project/Build.scala b/project/Build.scala index c90347950a..5a36942025 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -14,7 +14,7 @@ object ApplicationBuild extends Build { resolvers ++= Dependencies.Resolvers.commons, scalacOptions := compilerOptions, incOptions := incOptions.value.withNameHashing(true), - offline := false, + offline := true, libraryDependencies ++= Seq( scalaz, scalalib, hasher, config, apache, scalaTime, csv, jgit, actuarius, elastic4s, findbugs, RM, diff --git a/public/stylesheets/dark.css b/public/stylesheets/dark.css index 1a06484bc8..f2b1c7b8bb 100644 --- a/public/stylesheets/dark.css +++ b/public/stylesheets/dark.css @@ -219,6 +219,7 @@ body.dark div.content_box .loader:before, body.dark div.content_box .loader:after { background-color: #2b2b2b; } +body.dark #pool_list h2, body.dark #timeline, body.dark #timeline > .entry { border-color: #2b2b2b; @@ -416,6 +417,10 @@ body.dark div.user_show div.user-infos.scroll-shadow-hard { background-size: 100% 20px, 100% 20px, 100% 10px, 100% 10px; background-attachment: local, local, scroll, scroll; } +body.dark #pool_list > div { + background: rgba(0, 0, 0, 0.4); + box-shadow: 0 0 2px rgba(255,255,255,0.2); +} body.dark::-webkit-input-placeholder { color: #666; } diff --git a/public/stylesheets/pool.css b/public/stylesheets/pool.css index 9d1068e41d..1ebf4bdde4 100644 --- a/public/stylesheets/pool.css +++ b/public/stylesheets/pool.css @@ -14,6 +14,4 @@ width: 246px; max-height: 510px; overflow-y: hidden; - border: 1px solid #ccc; - padding: 3px; }
@trans.standing() (@p.nbUsers)@trans.standing() (@p.nbPlayers)
@rank - @userLink(user, withRating = false) + @lightUserLink(player.user) - @p.setup.glickoLens(user).intRating + @player.rating