replace game timeline with leaderboards on homepage

This commit is contained in:
Thibault Duplessis 2013-12-25 13:03:26 +01:00
parent 595b4eb838
commit 92841878dd
29 changed files with 82 additions and 228 deletions

View file

@ -25,7 +25,7 @@ final class Env(
featured = Env.game.featured,
relations = Env.relation.api,
leaderboard = Env.user.cached.topRatingDay.apply,
recentGames = () Env.timeline.getter.recentGames,
progress = Env.user.cached.topProgressDay.apply,
timelineEntries = Env.timeline.getter.userEntries _)
lazy val userInfo = mashup.UserInfo(

View file

@ -27,8 +27,5 @@ private[app] final class Renderer extends Actor {
case lila.tournament.actorApi.TournamentTable(tours)
sender ! V.tournament.createdTable(tours)
case entry: lila.timeline.GameEntry
sender ! V.timeline.gameEntry(entry)
}
}

View file

@ -32,8 +32,8 @@ object Lobby extends LilaController {
tours = TournamentRepo.createdUnprotected,
filter = Env.setup.filter
).map(_.fold(Redirect(_), {
case (preload, entries, gameEntries, posts, tours, featured, leaderboard) status(html.lobby.home(
Json stringify preload, entries, gameEntries, posts, tours, featured, leaderboard)) |> { response
case (preload, entries, posts, tours, featured, leaderboard, progress) status(html.lobby.home(
Json stringify preload, entries, posts, tours, featured, leaderboard, progress)) |> { response
ctx.req.session.data.contains(LilaCookie.sessionId).fold(
response,
response withCookies LilaCookie.makeSessionId(ctx.req)

View file

@ -25,10 +25,10 @@ final class Preload(
featured: Featured,
relations: RelationApi,
leaderboard: Int => Fu[List[User]],
recentGames: () Fu[List[GameEntry]],
progress: Int => Fu[List[User]],
timelineEntries: String Fu[List[Entry]]) {
private type RightResponse = (JsObject, List[Entry], List[GameEntry], List[PostLiteView], List[Created], Option[Game], List[User])
private type RightResponse = (JsObject, List[Entry], List[PostLiteView], List[Created], Option[Game], List[User], List[User])
private type Response = Either[Call, RightResponse]
def apply(
@ -36,20 +36,20 @@ final class Preload(
tours: Fu[List[Created]],
filter: Fu[FilterConfig])(implicit ctx: Context): Fu[Response] =
(lobby ? GetOpen).mapTo[List[Hook]] zip
recentGames() zip
posts zip
tours zip
featured.one zip
(ctx.userId ?? relations.blocks) zip
(ctx.userId ?? timelineEntries) zip
leaderboard(10) zip
progress(10) zip
filter map {
case ((((((((hooks, gameEntries), posts), tours), feat), blocks), entries), leaderboard), filter)
case ((((((((hooks, posts), tours), feat), blocks), entries), leaderboard), progress), filter)
(Right((Json.obj(
"version" -> history.version,
"pool" -> JsArray(hooks map (_.render)),
"filter" -> filter.render,
"blocks" -> blocks
), entries, gameEntries, posts, tours, feat, leaderboard)))
), entries, posts, tours, feat, leaderboard, progress)))
}
}

View file

@ -1,30 +0,0 @@
@(leaderboard: List[User])(implicit ctx: Context)
<div class="lichess_ground">
<div class="lichess_table lichess_table_not_started" id="start_buttons">
@lobbyMenu.all.map { b =>
<a class="lichess_button button config_@b.code" href="@b.route" onclick="return false" title="@b.title()">@b.name()</a>
}
</div>
<div class="leaderboard undertable">
<div class="user_top">
<div class="undertable_top">
<a class="more" title="@trans.people()" href="@routes.User.list()">@trans.more() »</a>
<span class="title">@trans.todaysLeaders()</span>
</div>
<div class="undertable_inner">
<table>
<tbody>
@leaderboard.map { u =>
<tr>
<td>@userLink(u, withRating = false, cssClass="revert-underline".some)</td>
<td>@u.rating</td>
<td>@showProgress(u.progress)</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>

View file

@ -1,4 +1,4 @@
@(preload: String, userTimeline: List[lila.timeline.Entry], gameTimeline: List[lila.timeline.GameEntry], forumRecent: List[lila.forum.PostLiteView], tours: List[lila.tournament.Created], featured: Option[Game], leaderboard: List[User])(implicit ctx: Context)
@(preload: String, userTimeline: List[lila.timeline.Entry], forumRecent: List[lila.forum.PostLiteView], tours: List[lila.tournament.Created], featured: Option[Game], leaderboard: List[User], progress: List[User])(implicit ctx: Context)
@underchat = {
<a class="watchtv revert-underline" href="@routes.Tv.index">@trans.watchLichessTV()</a>
@ -69,7 +69,13 @@ underchat = underchat.some) {
<div id="hook_filter"></div>
</div>
</div>
@lobby.buttons(leaderboard)
@lobby.undertable(gameTimeline, forumRecent, tours)
<div class="lichess_ground">
<div class="lichess_table lichess_table_not_started" id="start_buttons">
@lobbyMenu.all.map { b =>
<a class="lichess_button button config_@b.code" href="@b.route" onclick="return false" title="@b.title()">@b.name()</a>
}
</div>
</div>
@lobby.undertable(forumRecent, tours, leaderboard, progress)
</div>
}

View file

@ -1,4 +1,4 @@
@(gameTimeline: List[lila.timeline.GameEntry], forumRecent: List[lila.forum.PostLiteView], tours: List[lila.tournament.Created])(implicit ctx: Context)
@(forumRecent: List[lila.forum.PostLiteView], tours: List[lila.tournament.Created], leaderboard: List[User], progress: List[User])(implicit ctx: Context)
<div class="open_tournaments undertable">
<div class="undertable_top">
@ -11,18 +11,51 @@
</table>
</div>
</div>
<div class="lichess_bot undertable">
<div class="undertable_top">
<a class="more" title="@trans.seeTheGamesBeingPlayedInRealTime()" href="@routes.Game.realtime()">@trans.games() »</a>
<span class="title">@trans.gamesBeingPlayedRightNow()</span>
<div class="clearfix">
<div class="leaderboard undertable half">
<div class="user_top">
<div class="undertable_top">
<a class="more" title="@trans.people()" href="@routes.User.list()">@trans.more() »</a>
<span class="title">@trans.todaysLeaders()</span>
</div>
<div class="undertable_inner">
<table>
<tbody>
@leaderboard.map { u =>
<tr>
<td>@userLink(u, withRating = false, cssClass="revert-underline".some)</td>
<td>@u.rating</td>
<td>@showProgress(u.progress)</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<div class="undertable_inner">
<div class="content">
<table>@timeline.gameEntries(gameTimeline)</table>
<div class="leaderboard undertable half">
<div class="user_top">
<div class="undertable_top">
<a class="more" title="@trans.people()" href="@routes.User.list()">@trans.more() »</a>
<span class="title">@trans.progressToday()</span>
</div>
<div class="undertable_inner">
<table>
<tbody>
@progress.map { u =>
<tr>
<td>@userLink(u, withRating = false, cssClass="revert-underline".some)</td>
<td>@u.rating</td>
<td>@showProgress(u.progress)</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="new_posts undertable" data-url="@routes.ForumPost.recent">
<div class="new_posts undertable nomargin" data-url="@routes.ForumPost.recent">
<div class="undertable_top">
<a class="more" title="@trans.talkAboutChessAndDiscussLichessFeaturesInTheForum()" href="@routes.ForumCateg.index">@trans.forum() »</a>
<span class="title">@trans.forum()</span>

View file

@ -1,5 +0,0 @@
@(entries: List[lila.timeline.GameEntry])
@entries.map { e =>
<tr>@timeline.gameEntry(e)</tr>
}

View file

@ -1,12 +0,0 @@
@(entry: lila.timeline.GameEntry)
<td><a class="watch s24 eye" href="@routes.Round.watcher(entry.gameId, "white")"></a></td>
<td>
@{ Html((entry.players.map {
case (name, None) => name
case (_, id) => userIdLink(id, withOnline = false, truncate = 12.some)
}).mkString(" vs ")) }
</td>
<td class="trans_me">@entry.variant.capitalize</td>
<td class="trans_me">@entry.rated.fold("Rated", "Casual")</td>
<td class="trans_me">@entry.clock.getOrElse("Unlimited")</td>

View file

@ -20,7 +20,6 @@ final class Env(config: Config, system: ActorSystem) {
val ai = select("actor.ai")
val monitor = select("actor.monitor")
val tournamentOrganizer = select("actor.tournament.organizer")
val gameTimeline = select("actor.timeline.game")
val timeline = select("actor.timeline.user")
val bookmark = select("actor.bookmark")
val roundMap = select("actor.round.map")
@ -39,7 +38,7 @@ final class Env(config: Config, system: ActorSystem) {
val hub = select("socket.hub")
}
private def select(name: String) =
private def select(name: String) =
system actorSelection ("/user/" + config.getString(name))
}

View file

@ -52,7 +52,6 @@ package lobby {
package timeline {
case class ReloadTimeline(user: String)
case class GameEntryView(rendered: String)
sealed trait Atom
case class Follow(u1: String, u2: String) extends Atom

View file

@ -8,7 +8,6 @@ import lila.game.{ GameRepo, Game, Player, Pov, Progress }
import lila.user.{ User, UserRepo }
private[lobby] final class Biter(
timeline: akka.actor.ActorSelection,
blocks: (String, String) Fu[Boolean],
roundMessenger: lila.round.Messenger) {
@ -27,7 +26,6 @@ private[lobby] final class Biter(
blame(creatorColor, ownerOption, makeGame(hook))
).start
_ (GameRepo insertDenormalized game) >>-
(timeline ! game) >>
// messenges are not sent to the game socket
// as nobody is there to see them yet
(roundMessenger init game)

View file

@ -58,7 +58,6 @@ final class Env(
}
private lazy val biter = new Biter(
timeline = hub.actor.gameTimeline,
blocks = blocks,
roundMessenger = roundMessenger)
}

View file

@ -42,8 +42,6 @@ private[lobby] final class Socket(
case NewForumPost notifyAll("reload_forum")
case GameEntryView(rendered) notifyVersion("game_entry", rendered)
case ReloadTimeline(user) sendTo(user, makeMessage("reload_timeline", JsNull))
case AddHook(hook) notifyVersion("hook_add", hook.render)

View file

@ -90,7 +90,6 @@ final class Env(
private lazy val rematcher = new Rematcher(
messenger = messenger,
router = hub.actor.router,
timeline = hub.actor.gameTimeline,
rematch960Cache = rematch960Cache)
private lazy val player: Player = new Player(

View file

@ -16,7 +16,6 @@ import lila.user.UserRepo
private[round] final class Rematcher(
messenger: Messenger,
router: ActorSelection,
timeline: ActorSelection,
rematch960Cache: ExpireSetMemo) {
def yes(pov: Pov): Fu[Events] = pov match {
@ -56,7 +55,6 @@ private[round] final class Rematcher(
nextId = nextGame.id
_ (GameRepo insertDenormalized nextGame) >>
GameRepo.saveNext(pov.game, nextGame.id) >>-
(timeline ! nextGame) >>
// messenges are not sent to the next game socket
// as nobody is there to see them yet
messenger.rematch(pov.game, nextGame) >>- {

View file

@ -28,15 +28,13 @@ final class Env(
lazy val processor = new Processor(
lobby = hub.actor.lobby,
friendConfigMemo = friendConfigMemo,
timeline = hub.actor.gameTimeline,
router = hub.actor.router,
aiPlay = aiPlay)
lazy val friendJoiner = new FriendJoiner(
messenger = messenger,
friendConfigMemo = friendConfigMemo,
router = hub.actor.router,
timeline = hub.actor.gameTimeline)
router = hub.actor.router)
lazy val friendConfigMemo = new FriendConfigMemo(ttl = FriendMemoTtl)

View file

@ -13,7 +13,6 @@ import makeTimeout.short
private[setup] final class FriendJoiner(
messenger: Messenger,
friendConfigMemo: FriendConfigMemo,
timeline: ActorSelection,
router: ActorSelection) {
def apply(game: Game, user: Option[User]): Valid[Fu[(Pov, List[Event])]] =
@ -31,7 +30,7 @@ private[setup] final class FriendJoiner(
p3 messenger init p2.game map { evts
p2 + Event.RedirectOwner(!color, url) ++ evts
}
_ (GameRepo save p3) >>- (timeline ! p3.game)
_ GameRepo save p3
} yield Pov(p3.game, color) -> p3.events
} toValid "Can't join started game " + game.id

View file

@ -19,7 +19,6 @@ import tube.{ userConfigTube, anonConfigTube }
private[setup] final class Processor(
lobby: ActorSelection,
friendConfigMemo: FriendConfigMemo,
timeline: ActorSelection,
router: ActorSelection,
aiPlay: Game Fu[Progress]) {
@ -30,8 +29,7 @@ private[setup] final class Processor(
val pov = config.pov
val game = ctx.me.fold(pov.game)(user pov.game.updatePlayer(pov.color, _ withUser user))
saveConfig(_ withAi config) >>
(GameRepo insertDenormalized game) >>-
(timeline ! game) >>
(GameRepo insertDenormalized game) >>
game.player.isHuman.fold(
fuccess(pov),
aiPlay(game) map { progress pov withGame progress.game }

View file

@ -13,31 +13,19 @@ final class Env(
renderer: ActorSelection,
system: ActorSystem) {
private val GameCollectionEntry = config getString "game.collection.entry"
private val GameDisplayMax = config getInt "game.display_max"
private val GameActorName = config getString "game.actor.name"
private val UserCollectionEntry = config getString "user.collection.entry"
private val UserDisplayMax = config getInt "user.display_max"
private val UserActorName = config getString "user.actor.name"
lazy val getter = new Getter(
gameMax = GameDisplayMax,
userMax = UserDisplayMax)
system.actorOf(Props(new GamePush(
lobbySocket = lobbySocket,
renderer = renderer,
getUsername = getUsername
)), name = GameActorName)
system.actorOf(Props(new Push(
lobbySocket = lobbySocket,
renderer = renderer,
getFriendIds = getFriendIds
)), name = UserActorName)
private[timeline] lazy val gameEntryColl = db(GameCollectionEntry)
private[timeline] lazy val entryColl = db(UserCollectionEntry)
}

View file

@ -1,27 +0,0 @@
package lila.timeline
case class GameEntry(
gameId: String,
whiteName: String,
blackName: String,
whiteId: Option[String],
blackId: Option[String],
variant: String,
rated: Boolean,
clock: Option[String]) {
def players: List[(String, Option[String])] = List(
whiteName -> whiteId,
blackName -> blackId)
}
object GameEntry {
import lila.db.JsTube
import play.api.libs.json._
private[timeline] lazy val tube = JsTube(
Json.reads[GameEntry],
Json.writes[GameEntry],
Seq(_.NoId))
}

View file

@ -1,47 +0,0 @@
package lila.timeline
import akka.actor._
import akka.pattern.{ ask, pipe }
import play.api.templates.Html
import chess.Color, Color._
import lila.db.api.$insert
import lila.game.{ Game, Namer }
import lila.hub.actorApi.timeline._
import makeTimeout.short
import tube.gameEntryTube
private[timeline] final class GamePush(
lobbySocket: ActorSelection,
renderer: ActorSelection,
getUsername: String Fu[String]) extends Actor {
def receive = {
case game: Game makeEntry(game) flatMap { entry
$insert(entry) >>- {
renderer ? entry map {
case view: Html GameEntryView(view.body)
} pipeToSelection lobbySocket
}
} logFailure ("[timeline] push " + game.id)
}
private def makeEntry(game: Game): Fu[GameEntry] =
usernameRating(game, White) zip usernameRating(game, Black) map {
case (whiteName, blackName) GameEntry(
gameId = game.id,
whiteName = whiteName,
blackName = blackName,
whiteId = userId(game, White),
blackId = userId(game, Black),
variant = game.variant.name,
rated = game.rated,
clock = game.clock map (_.show))
}
private def userId(game: Game, color: Color): Option[String] =
(game player color).userId
private def usernameRating(game: Game, color: Color): Fu[String] =
Namer.player(game player color)(getUsername)
}

View file

@ -4,21 +4,14 @@ import play.api.libs.json.Json
import lila.db.api._
import lila.db.Implicits._
import tube.{ entryTube, gameEntryTube }
import tube.entryTube
private[timeline] final class Getter(
gameMax: Int,
userMax: Int) {
private[timeline] final class Getter(userMax: Int) {
def recentGames: Fu[List[GameEntry]] =
$find[GameEntry](
$query[GameEntry]($select.all) sort $sort.naturalOrder,
gameMax)
def userEntries(userId: String): Fu[List[Entry]] =
def userEntries(userId: String): Fu[List[Entry]] =
_userEntries(userId, userMax)
def moreUserEntries(userId: String): Fu[List[Entry]] =
def moreUserEntries(userId: String): Fu[List[Entry]] =
_userEntries(userId, 100)
private def _userEntries(userId: String, max: Int): Fu[List[Entry]] =

View file

@ -4,9 +4,6 @@ package object timeline extends PackageObject with WithPlay {
object tube {
private[timeline] implicit lazy val gameEntryTube =
GameEntry.tube inColl Env.current.gameEntryColl
private[timeline] implicit lazy val entryTube =
Entry.tube inColl Env.current.entryColl
}

View file

@ -84,7 +84,6 @@ final class Env(
private lazy val joiner = new GameJoiner(
roundMap = roundMap,
timelinePush = hub.actor.gameTimeline,
system = system)
{

View file

@ -12,7 +12,6 @@ import lila.user.{ User, UserRepo }
final class GameJoiner(
roundMap: ActorRef,
timelinePush: ActorSelection,
system: ActorSystem) {
private val secondsToMove = 20
@ -36,7 +35,6 @@ final class GameJoiner(
.start
.startClock(2)
_ (GameRepo insertDenormalized game) >>-
(timelinePush ! game) >>-
scheduleIdleCheck(PovRef(game.id, Color.White), secondsToMove)
} yield game

View file

@ -2112,7 +2112,6 @@ var storage = {
if (!strongSocket.available) return;
var $timeline = $("#timeline");
var $bot = $("div.lichess_bot");
var $newposts = $("div.new_posts");
var $hooks = $wrap.find('#hooks');
var $canvas = $wrap.find('.canvas');
@ -2209,10 +2208,6 @@ var storage = {
return false;
});
$bot.on("click", "tr", function() {
location.href = $(this).find('a.watch').attr("href");
});
function resizeTimeline() {
var max = $('#lichess').offset().top + 516;
if ($timeline.length) {
@ -2223,7 +2218,6 @@ var storage = {
}
}
resizeTimeline();
renderTimeline(lichess_preload.timeline);
_.each(lichess_preload.pool, function(h) {
addHook(h, true);
@ -2232,9 +2226,6 @@ var storage = {
lichess.socket = new strongSocket("/lobby/socket", lichess_preload.version, $.extend(true, lichess.socketDefaults, {
events: {
game_entry: function(e) {
renderTimeline([e]);
},
reload_timeline: function() {
$.ajax({
url: $timeline.data('href'),
@ -2281,20 +2272,6 @@ var storage = {
$('body').trigger('lichess.content_loaded');
}
function renderTimeline(data) {
var html = "";
for (var i in data) {
html += '<tr>' + data[i] + '</tr>';
}
$bot.find('.undertable_inner').find('tbody').each(function() {
$(this).prepend(html);
if ($(this).children().length > 50) {
$(this).find('tr:gt(40)').remove();
}
}).end().scrollTop(0);
$('body').trigger('lichess.content_loaded');
}
function removeHook(id) {
pool = _.reject(pool, function(h) {
return h.id == id;

View file

@ -248,18 +248,6 @@ div.lichess_table_not_started {
border: none;
padding: 0;
}
div.lichess_homepage div.leaderboard {
position: absolute;
top: 514px;
width: 242px;
}
div.lichess_homepage div.leaderboard td:first-child {
max-width: 150px;
overflow: hidden;
}
div.lichess_homepage div.leaderboard div.undertable_inner {
height: auto;
}
div.lichess_table div.username {
padding-left: 16px;
}

View file

@ -1736,6 +1736,9 @@ div.undertable {
font-size: 11.5px;
margin-top: 15px;
}
div.undertable.nomargin {
margin-top: 0;
}
div.undertable_top {
border: 1px solid #ccc;
padding: 3px 5px;
@ -1753,7 +1756,7 @@ div.undertable_top span.title {
div.undertable_inner {
border: 1px solid #ccc;
border-top: 0;
height: 204px;
height: 202px;
width: auto;
overflow: hidden;
}
@ -1783,6 +1786,17 @@ div.undertable tr:first-child td {
div.undertable tr:nth-child(even) td {
background: #f0f0f0;
}
div.undertable.half {
width: 250px;
float: left;
}
div.undertable.half:first-child {
margin-right: 14px;
}
div.undertable.leaderboard td:first-child {
max-width: 150px;
overflow: hidden;
}
div.open_tournaments div.undertable_inner {
height: auto;
}