diff --git a/app/views/base/layout.scala.html b/app/views/base/layout.scala.html
index 9b9725653c..016c292fb1 100644
--- a/app/views/base/layout.scala.html
+++ b/app/views/base/layout.scala.html
@@ -106,7 +106,9 @@ withLangAnnotations: Boolean = true)(body: Html)(implicit ctx: Context)
-
+
}.getOrElse {
@trans.signIn()
diff --git a/modules/challenge/src/main/BSONHandlers.scala b/modules/challenge/src/main/BSONHandlers.scala
index e0c2ed8ebf..501152e547 100644
--- a/modules/challenge/src/main/BSONHandlers.scala
+++ b/modules/challenge/src/main/BSONHandlers.scala
@@ -46,8 +46,14 @@ private object BSONHandlers {
def read(b: BSONBoolean) = Mode(b.value)
def write(m: Mode) = BSONBoolean(m.rated)
}
+ implicit val RatingBSONHandler = new BSON[Rating] {
+ def reads(r: Reader) = Rating(r.int("i"), r.boolD("p"))
+ def writes(w: Writer, r: Rating) = BSONDocument(
+ "i" -> r.int,
+ "b" -> w.boolO(r.provisional))
+ }
implicit val EitherChallengerBSONHandler = new BSON[EitherChallenger] {
- def reads(r: Reader) = (r.strO("id") |@| r.intO("rating")) {
+ def reads(r: Reader) = (r.strO("id") |@| r.getO[Rating]("rating")) {
case (id, rating) => Right(Registered(id, rating))
} orElse r.strO("secret").map { secret =>
Left(Anonymous(secret))
diff --git a/modules/challenge/src/main/Challenge.scala b/modules/challenge/src/main/Challenge.scala
index 1131b5fe9f..cb1fd8f418 100644
--- a/modules/challenge/src/main/Challenge.scala
+++ b/modules/challenge/src/main/Challenge.scala
@@ -25,7 +25,12 @@ case class Challenge(
object Challenge {
- case class Registered(id: String, rating: Int)
+ case class Rating(int: Int, provisional: Boolean)
+ object Rating {
+ def apply(p: lila.rating.Perf): Rating = Rating(p.intRating, p.provisional)
+ }
+
+ case class Registered(id: String, rating: Rating)
case class Anonymous(secret: String)
sealed trait TimeControl
@@ -78,7 +83,7 @@ object Challenge {
case _ => ColorChoice.Random
},
challenger = challenger.fold[EitherChallenger](Left(Anonymous(randomId))) { u =>
- Right(Registered(u.id, u.perfs(perfType(variant, timeControl)).intRating))
+ Right(Registered(u.id, Rating(u.perfs(perfType(variant, timeControl)))))
},
destUserId = destUserId,
createdAt = DateTime.now,
diff --git a/modules/challenge/src/main/JsonView.scala b/modules/challenge/src/main/JsonView.scala
index 932c9dc54f..75f197dae4 100644
--- a/modules/challenge/src/main/JsonView.scala
+++ b/modules/challenge/src/main/JsonView.scala
@@ -2,6 +2,8 @@ package lila.challenge
import play.api.libs.json._
+import lila.common.PimpedJson._
+
final class JsonView(getLightUser: String => Option[lila.common.LightUser]) {
import Challenge._
@@ -18,8 +20,9 @@ final class JsonView(getLightUser: String => Option[lila.common.LightUser]) {
"id" -> u.id,
"name" -> light.fold(u.id)(_.name),
"title" -> light.map(_.title),
- "rating" -> u.rating
- )
+ "rating" -> u.rating.int,
+ "provisional" -> u.rating.provisional
+ ).noNull
},
"destUserId" -> c.destUserId,
"variant" -> Json.obj(
@@ -28,12 +31,13 @@ final class JsonView(getLightUser: String => Option[lila.common.LightUser]) {
"name" -> c.variant.name),
"rated" -> c.mode.rated,
"timeControl" -> (c.timeControl match {
- case TimeControl.Clock(l, i) => Json.obj(
+ case c@TimeControl.Clock(l, i) => Json.obj(
"type" -> "clock",
"limit" -> l,
- "increment" -> i)
+ "increment" -> i,
+ "show" -> c.show)
case TimeControl.Correspondence(d) => Json.obj(
- "type" -> "clock",
+ "type" -> "correspondence",
"daysPerTurn" -> d)
case TimeControl.Unlimited => Json.obj("type" -> "unlimited")
}),
diff --git a/public/javascripts/big.js b/public/javascripts/big.js
index 46db0d3648..576e64a1a7 100644
--- a/public/javascripts/big.js
+++ b/public/javascripts/big.js
@@ -346,10 +346,9 @@ lichess.challengeBox = (function() {
var baseUrl = $('body').data('asset-url');
var isDev = $('body').data('dev');
$('head').append($('')
- .attr('href', baseUrl + '/assets/stylesheet/challenge.css'));
- $.getScript(baseUrl + "/assets/compiled/challenge" + (isDev ? '.min' : '') + '.js').done(function() {
- instance = LichessChallenge({
- element: document.getElementById('#challenge_notifications'),
+ .attr('href', baseUrl + '/assets/stylesheet/challengeBox.css'));
+ $.getScript(baseUrl + "/assets/compiled/lichess.challenge" + (isDev ? '' : '.min') + '.js').done(function() {
+ instance = LichessChallenge(document.getElementById('challenge_notifications'), {
setCount: function(nb) {
$('#challenge_notifications_tag').attr('data-count', nb).toggleClass('none', !nb);
}
@@ -1114,9 +1113,9 @@ lichess.unique = function(xs) {
}
});
});
- $('#challenge_notifications').one('mouseover', function() {
+ $('#challenge_notifications_tag').one('mouseover click', function() {
lichess.challengeBox.load();
- });
+ }).trigger('click');
$('#translation_call .close').click(function() {
$.post($(this).data("href"));
diff --git a/public/stylesheets/challengeBox.css b/public/stylesheets/challengeBox.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/ui/challenge/src/ctrl.js b/ui/challenge/src/ctrl.js
index 919f467ed5..c2aa23eacb 100644
--- a/ui/challenge/src/ctrl.js
+++ b/ui/challenge/src/ctrl.js
@@ -6,12 +6,20 @@ module.exports = function(env) {
this.data = env.data;
- this.userId = env.userId;
-
this.socket = new socket(env.socketSend, this);
this.vm = {
+ initiating: true,
+ reloading: false
};
+ this.update = function(data) {
+ this.data = data;
+ this.vm.initiating = false;
+ this.vm.reloading = false;
+ }.bind(this);
+
+ xhr.load();
+
this.trans = lichess.trans(env.i18n);
};
diff --git a/ui/challenge/src/main.js b/ui/challenge/src/main.js
index 00083ee25e..d3e6a2ee47 100644
--- a/ui/challenge/src/main.js
+++ b/ui/challenge/src/main.js
@@ -1,12 +1,13 @@
var m = require('mithril');
+var ctrl = require('./ctrl');
module.exports = function(element, opts) {
+ var controller = new ctrl(opts);
+
m.module(element, {
controller: function() {
- return {
- data: opts.data
- };
+ return controller;
},
view: require('./view')
});
diff --git a/ui/challenge/src/view.js b/ui/challenge/src/view.js
index 69d75b762a..52d0d0310f 100644
--- a/ui/challenge/src/view.js
+++ b/ui/challenge/src/view.js
@@ -1,6 +1,55 @@
var m = require('mithril');
+function user(u) {
+ var rating = u.rating + (u.provisional ? '?' : '');
+ var fullName = (u.title ? u.title + ' ' : '') + u.name;
+ return {
+ tag: 'a',
+ attrs: {
+ class: 'ulpt user_link',
+ 'data-href': '/@/' + u.name
+ },
+ children: [
+ fullName,
+ m('span.progress', [rating, ratingDiff])
+ ]
+ };
+}
+
+function timeControl(c) {
+ switch (c.type) {
+ case 'unlimited':
+ return 'Unlimited';
+ case 'correspondence':
+ return c.daysPerTurn + ' days';
+ case 'clock':
+ return c.show;
+ }
+}
+
+function challenge(c) {
+ return m('div.challenge', [
+ m('i', {
+ 'data-icon': c.perf.icon
+ }),
+ m('div.content', [
+ m('span.title', user(c.challenger)),
+ m('span.desc', [
+ c.rated ? 'Rated' : 'Casual',
+ timeControl(c.timeControl),
+ c.variant.name
+ ].join(' '))
+ ])
+ ]);
+}
+
module.exports = function(ctrl) {
+ if (ctrl.vm.initiating) return m('div.square-wrap', m('div.square-spin'));
var d = ctrl.data;
- return m('div.challenges', 'hehe');
+ return m('div', {
+ class: 'challenges ' + (ctrl.vm.reloading ? ' reloading' : '')
+ }, [
+ d.in.map(challenge),
+ d.out.map(challenge),
+ ])
};
diff --git a/ui/challenge/src/xhr.js b/ui/challenge/src/xhr.js
index cfec4c69ad..36f2193f9c 100644
--- a/ui/challenge/src/xhr.js
+++ b/ui/challenge/src/xhr.js
@@ -10,4 +10,11 @@ function uncache(url) {
}
module.exports = {
+ load: function() {
+ return m.request({
+ method: 'GET',
+ url: uncache('/challenge'),
+ config: xhrConfig,
+ }).then(ctrl.update);
+ }
};