puzzle of the day on homepage
This commit is contained in:
parent
015a98c238
commit
88dbc9f003
|
@ -26,7 +26,8 @@ final class Env(
|
|||
relations = Env.relation.api,
|
||||
leaderboard = Env.user.cached.topRatingDay.apply,
|
||||
progress = Env.user.cached.topProgressDay.apply,
|
||||
timelineEntries = Env.timeline.getter.userEntries _)
|
||||
timelineEntries = Env.timeline.getter.userEntries _,
|
||||
dailyPuzzle = Env.puzzle.daily)
|
||||
|
||||
lazy val userInfo = mashup.UserInfo(
|
||||
countUsers = () => Env.user.countEnabled,
|
||||
|
|
|
@ -33,5 +33,8 @@ private[app] final class Renderer extends Actor {
|
|||
|
||||
case lila.tournament.actorApi.TournamentTable(tours) =>
|
||||
sender ! V.tournament.createdTable(tours)
|
||||
|
||||
case lila.puzzle.RenderDaily(puzzle, fen, lastMove) =>
|
||||
sender ! V.puzzle.daily(puzzle, fen, lastMove)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ object Lobby extends LilaController {
|
|||
tours = TournamentRepo.createdUnprotected,
|
||||
filter = Env.setup.filter
|
||||
).map(_.fold(Redirect(_), {
|
||||
case (preload, entries, posts, tours, featured, leaderboard, progress) =>
|
||||
case (preload, entries, posts, tours, featured, leaderboard, progress, puzzle) =>
|
||||
val response = status(html.lobby.home(
|
||||
Json stringify preload, entries, posts, tours, featured, leaderboard, progress
|
||||
Json stringify preload, entries, posts, tours, featured, leaderboard, progress, puzzle
|
||||
))
|
||||
// the session cookie is required for anon lobby filter storage
|
||||
ctx.req.session.data.contains(LilaCookie.sessionId).fold(
|
||||
|
|
|
@ -7,17 +7,17 @@ import play.api.libs.json.{ Json, JsObject, JsArray }
|
|||
import play.api.mvc.Call
|
||||
|
||||
import controllers.routes
|
||||
import lila.api.Context
|
||||
import lila.forum.PostLiteView
|
||||
import lila.game.{ Game, GameRepo, Featured }
|
||||
import lila.lobby.actorApi.GetOpen
|
||||
import lila.lobby.{ Hook, HookRepo }
|
||||
import lila.relation.RelationApi
|
||||
import lila.setup.FilterConfig
|
||||
import lila.socket.History
|
||||
import lila.timeline.Entry
|
||||
import lila.tournament.Created
|
||||
import lila.user.User
|
||||
import lila.api.Context
|
||||
import lila.relation.RelationApi
|
||||
import makeTimeout.large
|
||||
|
||||
final class Preload(
|
||||
|
@ -27,9 +27,10 @@ final class Preload(
|
|||
relations: RelationApi,
|
||||
leaderboard: Int => Fu[List[User]],
|
||||
progress: Int => Fu[List[User]],
|
||||
timelineEntries: String => Fu[List[Entry]]) {
|
||||
timelineEntries: String => Fu[List[Entry]],
|
||||
dailyPuzzle: () => Fu[Option[lila.puzzle.DailyPuzzle]]) {
|
||||
|
||||
private type RightResponse = (JsObject, List[Entry], List[PostLiteView], List[Created], Option[Game], List[User], List[User])
|
||||
private type RightResponse = (JsObject, List[Entry], List[PostLiteView], List[Created], Option[Game], List[User], List[User], Option[lila.puzzle.DailyPuzzle])
|
||||
private type Response = Either[Call, RightResponse]
|
||||
|
||||
def apply(
|
||||
|
@ -44,13 +45,14 @@ final class Preload(
|
|||
(ctx.userId ?? timelineEntries) zip
|
||||
leaderboard(10) zip
|
||||
progress(10) zip
|
||||
dailyPuzzle() zip
|
||||
filter map {
|
||||
case ((((((((hooks, posts), tours), feat), blocks), entries), leaderboard), progress), filter) =>
|
||||
case (((((((((hooks, posts), tours), feat), blocks), entries), leaderboard), progress), puzzle), filter) =>
|
||||
(Right((Json.obj(
|
||||
"version" -> history.version,
|
||||
"pool" -> JsArray(hooks map (_.render)),
|
||||
"filter" -> filter.render,
|
||||
"blocks" -> blocks
|
||||
), entries, posts, tours, feat, leaderboard, progress)))
|
||||
), entries, posts, tours, feat, leaderboard, progress, puzzle)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(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)
|
||||
@(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], puzzle: Option[lila.puzzle.DailyPuzzle])(implicit ctx: Context)
|
||||
|
||||
@underchat = {
|
||||
<div id="featured_game" title="@trans.watchLichessTV()">
|
||||
|
@ -46,7 +46,7 @@ themepicker = true) {
|
|||
@translationCall.map(i18n.callBox(_))
|
||||
</div>
|
||||
<div class="clearfix lichess_homepage">
|
||||
<div class="lichess_board_wrap lichess_player_white">
|
||||
<div class="lichess_board_wrap">
|
||||
<div id="hooks_wrap">
|
||||
<div class="tabs">
|
||||
<a data-tab="list" class="list">@trans.list()</a>
|
||||
|
@ -78,6 +78,15 @@ themepicker = true) {
|
|||
<a class="lichess_button button config_@b.code" href="@b.route" onclick="return false" title="@b.title()">@b.name()</a>
|
||||
}
|
||||
</div>
|
||||
@puzzle.map { p =>
|
||||
<div id="daily_puzzle">
|
||||
@p.html
|
||||
<div class="vstext">
|
||||
@trans.puzzleOfTheDay()<br />
|
||||
@trans.findTheBestMoveForColor(p.color.name)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@lobby.undertable(forumRecent, tours, leaderboard, progress)
|
||||
</div>
|
||||
|
|
7
app/views/puzzle/daily.scala.html
Normal file
7
app/views/puzzle/daily.scala.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
@(p: lila.puzzle.Puzzle, fen: String, lastMove: String)
|
||||
|
||||
<a href="@routes.Puzzle.show(p.id)"
|
||||
class="mini_board parse_fen"
|
||||
data-color="@p.color"
|
||||
data-fen="@fen"
|
||||
data-lastmove="@lastMove"></a>
|
|
@ -272,6 +272,7 @@ toTrackYourProgress=To track your progress:
|
|||
trainingSignupExplanation=Lichess will provide puzzles that match your ability, making for better training sessions.
|
||||
recentlyPlayedPuzzles=Recently played puzzles
|
||||
puzzleId=Puzzle %s
|
||||
puzzleOfTheDay=Puzzle of the day
|
||||
goodMove=Good move
|
||||
butYouCanDoBetter=But you can do better.
|
||||
bestMove=Best move!
|
||||
|
|
File diff suppressed because one or more lines are too long
65
modules/puzzle/src/main/Daily.scala
Normal file
65
modules/puzzle/src/main/Daily.scala
Normal file
|
@ -0,0 +1,65 @@
|
|||
package lila.puzzle
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.{ ActorSelection, Scheduler }
|
||||
import akka.pattern.ask
|
||||
import org.joda.time.DateTime
|
||||
import reactivemongo.bson.BSONDocument
|
||||
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import lila.db.Types.Coll
|
||||
|
||||
private[puzzle] final class Daily(
|
||||
coll: Coll,
|
||||
renderer: ActorSelection,
|
||||
scheduler: Scheduler) {
|
||||
|
||||
private val cache =
|
||||
lila.memo.AsyncCache.single[Option[DailyPuzzle]](f = find, timeToLive = 30 minutes)
|
||||
|
||||
def apply(): Fu[Option[DailyPuzzle]] = cache apply true
|
||||
|
||||
private def find: Fu[Option[DailyPuzzle]] = findCurrent orElse findNew flatMap {
|
||||
case Some(puzzle) => makeDaily(puzzle)
|
||||
case None =>
|
||||
scheduler.scheduleOnce(10.seconds)(cache.clear)
|
||||
fuccess(none)
|
||||
}
|
||||
|
||||
private def makeDaily(puzzle: Puzzle): Fu[Option[DailyPuzzle]] = {
|
||||
import chess.format.{ UciMove, Forsyth }
|
||||
import makeTimeout.short
|
||||
~(for {
|
||||
sit1 <- Forsyth << puzzle.fen
|
||||
move <- puzzle.history.lastOption
|
||||
uci <- UciMove(move)
|
||||
sit2 <- sit1.move(uci.orig, uci.dest, uci.promotion).toOption map (_.situationAfter)
|
||||
fen = Forsyth >> sit2
|
||||
} yield renderer ? RenderDaily(puzzle, fen, move) map {
|
||||
case html: play.api.templates.Html => DailyPuzzle(html, puzzle.color).some
|
||||
})
|
||||
} recover {
|
||||
case e: Exception =>
|
||||
play.api.Logger("daily puzzle").warn(e.getMessage)
|
||||
none
|
||||
}
|
||||
|
||||
private def findCurrent = coll.find(
|
||||
BSONDocument("day" -> BSONDocument("$gt" -> DateTime.now.minusDays(1)))
|
||||
).one[Puzzle]
|
||||
|
||||
private def findNew = coll.find(
|
||||
BSONDocument("day" -> BSONDocument("$exists" -> false))
|
||||
).sort(BSONDocument("vote.sum" -> -1)).one[Puzzle] flatMap {
|
||||
case Some(puzzle) => coll.update(
|
||||
BSONDocument("_id" -> puzzle.id),
|
||||
BSONDocument("$set" -> BSONDocument("day" -> DateTime.now))
|
||||
) inject puzzle.some
|
||||
case None => fuccess(none)
|
||||
}
|
||||
}
|
||||
|
||||
case class DailyPuzzle(html: play.api.templates.Html, color: chess.Color)
|
||||
|
||||
case class RenderDaily(puzzle: Puzzle, fen: String, lastMove: String)
|
|
@ -1,6 +1,6 @@
|
|||
package lila.puzzle
|
||||
|
||||
import akka.actor.{ ActorSystem, Props }
|
||||
import akka.actor.{ ActorSelection, ActorSystem, Props }
|
||||
import com.typesafe.config.Config
|
||||
|
||||
import lila.common.PimpedConfig._
|
||||
|
@ -8,6 +8,7 @@ import lila.common.PimpedConfig._
|
|||
final class Env(
|
||||
config: Config,
|
||||
db: lila.db.Env,
|
||||
renderer: ActorSelection,
|
||||
system: ActorSystem) {
|
||||
|
||||
private val settings = new {
|
||||
|
@ -32,6 +33,12 @@ final class Env(
|
|||
|
||||
lazy val forms = DataForm
|
||||
|
||||
lazy val daily = new Daily(
|
||||
puzzleColl,
|
||||
renderer,
|
||||
system.scheduler
|
||||
).apply _
|
||||
|
||||
private[puzzle] lazy val puzzleColl = db(CollectionPuzzle)
|
||||
private[puzzle] lazy val attemptColl = db(CollectionAttempt)
|
||||
}
|
||||
|
@ -41,5 +48,6 @@ object Env {
|
|||
lazy val current: Env = "[boot] puzzle" describes new Env(
|
||||
config = lila.common.PlayApp loadConfig "puzzle",
|
||||
db = lila.db.Env.current,
|
||||
renderer = lila.hub.Env.current.actor.renderer,
|
||||
system = lila.common.PlayApp.system)
|
||||
}
|
||||
|
|
|
@ -513,7 +513,7 @@ div.under_chat {
|
|||
width: 224px;
|
||||
margin-left: -28px;
|
||||
position: absolute;
|
||||
top: 530px;
|
||||
top: 529px;
|
||||
left: 0;
|
||||
}
|
||||
div.under_chat .watchtv {
|
||||
|
|
|
@ -1250,6 +1250,15 @@ div.vstext .right {
|
|||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
#daily_puzzle {
|
||||
width: 224px;
|
||||
position: absolute;
|
||||
top: 529px;
|
||||
left: 0;
|
||||
}
|
||||
#daily_puzzle > div.vstext {
|
||||
text-align: center;
|
||||
}
|
||||
div.extra_top {
|
||||
float: right;
|
||||
margin: 20px 20px 0 0;
|
||||
|
|
Loading…
Reference in a new issue