more ublog tier/rank system
parent
2a8e423311
commit
1257ef10fc
|
@ -11,7 +11,8 @@ import lila.user.{ User => UserModel }
|
|||
|
||||
final class Ublog(env: Env) extends LilaController(env) {
|
||||
|
||||
import views.html.ublog.post.{ editUrlOf, urlOf }
|
||||
import views.html.ublog.post.{ editUrlOfPost, urlOfPost }
|
||||
import views.html.ublog.blog.{ urlOfBlog }
|
||||
import lila.common.paginator.Paginator.zero
|
||||
|
||||
def index(username: String, page: Int) = Open { implicit ctx =>
|
||||
|
@ -19,7 +20,7 @@ final class Ublog(env: Env) extends LilaController(env) {
|
|||
OptionFuResult(env.user.repo named username) { user =>
|
||||
env.ublog.api.getUserBlog(user) flatMap { blog =>
|
||||
(canViewBlogOf(user, blog) ?? env.ublog.paginator.byUser(user, true, page)) map { posts =>
|
||||
Ok(html.ublog.index(user, posts))
|
||||
Ok(html.ublog.blog(user, blog, posts))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +43,7 @@ final class Ublog(env: Env) extends LilaController(env) {
|
|||
env.ublog.api.getUserBlog(user) flatMap { blog =>
|
||||
env.ublog.api.findByIdAndBlog(UblogPost.Id(id), blog.id) flatMap {
|
||||
_.filter(canViewPost(user, blog)) ?? { post =>
|
||||
if (slug != post.slug) Redirect(urlOf(post)).fuccess
|
||||
if (slug != post.slug) Redirect(urlOfPost(post)).fuccess
|
||||
else {
|
||||
env.ublog.api.otherPosts(UblogBlog.Id.User(user.id), post) zip
|
||||
ctx.me.??(env.ublog.like.liked(post)) map { case (others, liked) =>
|
||||
|
@ -94,7 +95,7 @@ final class Ublog(env: Env) extends LilaController(env) {
|
|||
CreateLimitPerUser(me.id, cost = if (me.isVerified) 1 else 3) {
|
||||
env.ublog.api.create(data, me) map { post =>
|
||||
lila.mon.ublog.create(me.id).increment()
|
||||
Redirect(editUrlOf(post)).flashSuccess
|
||||
Redirect(editUrlOfPost(post)).flashSuccess
|
||||
}
|
||||
}(rateLimitedFu)
|
||||
)
|
||||
|
@ -120,7 +121,7 @@ final class Ublog(env: Env) extends LilaController(env) {
|
|||
err => BadRequest(html.ublog.form.edit(me, prev, err)).fuccess,
|
||||
data =>
|
||||
env.ublog.api.update(data, prev, me) map { post =>
|
||||
Redirect(urlOf(post)).flashSuccess
|
||||
Redirect(urlOfPost(post)).flashSuccess
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -144,6 +145,24 @@ final class Ublog(env: Env) extends LilaController(env) {
|
|||
}
|
||||
}
|
||||
|
||||
def setTier(blogId: String) = SecureBody(_.ModerateBlog) { implicit ctx => me =>
|
||||
UblogBlog.Id(blogId).??(env.ublog.api.getBlog) flatMap {
|
||||
_ ?? { blog =>
|
||||
implicit val body = ctx.body
|
||||
lila.ublog.UblogForm.tier
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
err => Redirect(urlOfBlog(blog)).flashFailure.fuccess,
|
||||
tier =>
|
||||
env.ublog.api.setTier(blog.id, tier) >>
|
||||
env.ublog.like.recomputeRankOfAllPosts(blog.id) >> env.mod.logApi
|
||||
.blogTier(lila.report.Mod(me.user), blog.id.full, tier)
|
||||
inject Redirect(urlOfBlog(blog)).flashSuccess
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ImageRateLimitPerIp = lila.memo.RateLimit.composite[lila.common.IpAddress](
|
||||
key = "ublog.image.ip"
|
||||
)(
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package views.html.ublog
|
||||
|
||||
import controllers.routes
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.paginator.Paginator
|
||||
import lila.ublog.{ UblogBlog, UblogPost }
|
||||
import lila.user.User
|
||||
|
||||
object blog {
|
||||
|
||||
import views.html.ublog.{ post => postView }
|
||||
|
||||
def apply(user: User, blog: UblogBlog, posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
moreCss = cssTag("ublog"),
|
||||
moreJs = frag(
|
||||
posts.hasNextPage option infiniteScrollTag,
|
||||
ctx.isAuth option jsModule("ublog")
|
||||
),
|
||||
title = trans.ublog.xBlog.txt(user.username)
|
||||
) {
|
||||
main(cls := "box box-pad page page-small ublog-index")(
|
||||
div(cls := "box__top")(
|
||||
h1(trans.ublog.xBlog(userLink(user))),
|
||||
if (ctx is user)
|
||||
div(cls := "box__top__actions")(
|
||||
a(href := routes.Ublog.drafts(user.username))(trans.ublog.drafts()),
|
||||
postView.newPostLink
|
||||
)
|
||||
else isGranted(_.ModerateBlog) option tierForm(blog)
|
||||
),
|
||||
standardFlash(),
|
||||
if (posts.nbResults > 0)
|
||||
div(cls := "ublog-index__posts ublog-post-cards infinite-scroll")(
|
||||
posts.currentPageResults map { postView.card(_) },
|
||||
pagerNext(posts, np => s"${routes.Ublog.index(user.username, np).url}")
|
||||
)
|
||||
else
|
||||
div(cls := "ublog-index__posts--empty")(
|
||||
trans.ublog.noPostsInThisBlogYet()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def urlOfBlog(blog: UblogBlog) = blog.id match {
|
||||
case UblogBlog.Id.User(userId) => routes.Ublog.index(usernameOrId(userId))
|
||||
}
|
||||
|
||||
private def tierForm(blog: UblogBlog) = postForm(action := routes.Ublog.setTier(blog.id.full)) {
|
||||
val form = lila.ublog.UblogForm.tier.fill(blog.tier)
|
||||
form3.select(form("tier"), lila.ublog.UblogBlog.Tier.options)
|
||||
}
|
||||
}
|
|
@ -112,7 +112,9 @@ object form {
|
|||
views.html.base.captcha(form, c)
|
||||
},
|
||||
form3.actions(
|
||||
a(href := post.fold(routes.Ublog.index(user.username))(views.html.ublog.post.urlOf))(trans.cancel()),
|
||||
a(href := post.fold(routes.Ublog.index(user.username))(views.html.ublog.post.urlOfPost))(
|
||||
trans.cancel()
|
||||
),
|
||||
form3.submit(trans.apply())
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,40 +13,6 @@ object index {
|
|||
|
||||
import views.html.ublog.{ post => postView }
|
||||
|
||||
def apply(user: User, posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
moreCss = cssTag("ublog"),
|
||||
moreJs = frag(
|
||||
posts.hasNextPage option infiniteScrollTag,
|
||||
ctx.isAuth option jsModule("ublog")
|
||||
),
|
||||
title = trans.ublog.xBlog.txt(user.username)
|
||||
) {
|
||||
main(cls := "box box-pad page page-small ublog-index")(
|
||||
div(cls := "box__top")(
|
||||
h1(trans.ublog.xBlog(userLink(user))),
|
||||
if (ctx is user)
|
||||
div(cls := "box__top__actions")(
|
||||
a(href := routes.Ublog.drafts(user.username))(trans.ublog.drafts()),
|
||||
newPostLink
|
||||
)
|
||||
else if (isGranted(_.ModerateBlog) && user.marks.troll)
|
||||
badTag("Not visible to the public")
|
||||
else emptyFrag
|
||||
),
|
||||
standardFlash(),
|
||||
if (posts.nbResults > 0)
|
||||
div(cls := "ublog-index__posts ublog-post-cards infinite-scroll")(
|
||||
posts.currentPageResults map { postView.card(_) },
|
||||
pagerNext(posts, np => s"${routes.Ublog.index(user.username, np).url}")
|
||||
)
|
||||
else
|
||||
div(cls := "ublog-index__posts--empty")(
|
||||
trans.ublog.noPostsInThisBlogYet()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def drafts(user: User, posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
moreCss = frag(cssTag("ublog")),
|
||||
|
@ -58,12 +24,12 @@ object index {
|
|||
h1(trans.ublog.drafts()),
|
||||
div(cls := "box__top__actions")(
|
||||
a(href := routes.Ublog.index(user.username))(trans.ublog.published()),
|
||||
newPostLink
|
||||
postView.newPostLink
|
||||
)
|
||||
),
|
||||
if (posts.nbResults > 0)
|
||||
div(cls := "ublog-index__posts ublog-index__posts--drafts ublog-post-cards infinite-scroll")(
|
||||
posts.currentPageResults map { postView.card(_, postView.editUrlOf) },
|
||||
posts.currentPageResults map { postView.card(_, postView.editUrlOfPost) },
|
||||
pagerNext(posts, np => routes.Ublog.drafts(user.username, np).url)
|
||||
)
|
||||
else
|
||||
|
@ -117,13 +83,4 @@ object index {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def newPostLink(implicit ctx: Context) = ctx.me map { u =>
|
||||
a(
|
||||
href := routes.Ublog.form(u.username),
|
||||
cls := "button button-green",
|
||||
dataIcon := "",
|
||||
title := trans.ublog.newPost.txt()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ object post {
|
|||
if (post.live) trans.ublog.thisPostIsPublished() else trans.ublog.thisIsADraft()
|
||||
),
|
||||
a(
|
||||
href := editUrlOf(post),
|
||||
href := editUrlOfPost(post),
|
||||
cls := "button button-empty text",
|
||||
dataIcon := ""
|
||||
)(trans.edit())
|
||||
|
@ -71,7 +71,7 @@ object post {
|
|||
a(
|
||||
titleOrText(trans.reportXToModerators.txt(user.username)),
|
||||
cls := "button button-empty ublog-post__meta__report",
|
||||
href := s"${routes.Report.form}?username=${user.username}&postUrl=${urlencode(s"${netBaseUrl}${urlOf(post).url}")}&reason=comm",
|
||||
href := s"${routes.Report.form}?username=${user.username}&postUrl=${urlencode(s"${netBaseUrl}${urlOfPost(post).url}")}&reason=comm",
|
||||
dataIcon := ""
|
||||
)
|
||||
),
|
||||
|
@ -86,7 +86,7 @@ object post {
|
|||
|
||||
def card(
|
||||
post: UblogPost.BasePost,
|
||||
makeUrl: UblogPost.BasePost => Call = urlOf,
|
||||
makeUrl: UblogPost.BasePost => Call = urlOfPost,
|
||||
showAuthor: Boolean = false
|
||||
)(implicit ctx: Context) =
|
||||
a(cls := "ublog-post-card", href := makeUrl(post))(
|
||||
|
@ -99,12 +99,21 @@ object post {
|
|||
)
|
||||
)
|
||||
|
||||
def urlOf(post: UblogPost.BasePost) = post.blog match {
|
||||
def urlOfPost(post: UblogPost.BasePost) = post.blog match {
|
||||
case UblogBlog.Id.User(userId) =>
|
||||
routes.Ublog.post(usernameOrId(userId), post.slug, post.id.value)
|
||||
}
|
||||
|
||||
def editUrlOf(post: UblogPost.BasePost) = routes.Ublog.edit(post.id.value)
|
||||
def editUrlOfPost(post: UblogPost.BasePost) = routes.Ublog.edit(post.id.value)
|
||||
|
||||
private[ublog] def newPostLink(implicit ctx: Context) = ctx.me map { u =>
|
||||
a(
|
||||
href := routes.Ublog.form(u.username),
|
||||
cls := "button button-green",
|
||||
dataIcon := "",
|
||||
title := trans.ublog.newPost.txt()
|
||||
)
|
||||
}
|
||||
|
||||
object thumbnail {
|
||||
def apply(post: UblogPost.BasePost, size: UblogPost.thumbnail.SizeSelector) =
|
||||
|
|
|
@ -61,6 +61,8 @@ db.ublog_post.find({ blog: { $exists: false } }).forEach(p => {
|
|||
at: p.liveAt,
|
||||
}
|
||||
: undefined,
|
||||
likers: [],
|
||||
likes: NumberInt(0),
|
||||
},
|
||||
$unset: {
|
||||
user: 1,
|
||||
|
|
|
@ -67,6 +67,7 @@ GET /ublog/$id<\w{8}>/edit controllers.Ublog.edit(id: String)
|
|||
POST /ublog/$id<\w{8}>/edit controllers.Ublog.update(id: String)
|
||||
POST /ublog/$id<\w{8}>/del controllers.Ublog.delete(id: String)
|
||||
POST /ublog/$id<\w{8}>/like controllers.Ublog.like(id: String, v: Boolean)
|
||||
POST /ublog/:blogId/tier controllers.Ublog.setTier(blogId: String)
|
||||
POST /upload/image/ublog/$id<\w{8}> controllers.Ublog.image(id: String)
|
||||
|
||||
# User
|
||||
|
|
|
@ -170,6 +170,7 @@ object ModActivity {
|
|||
case object Appeal extends Action
|
||||
case object SetEmail extends Action
|
||||
case object Streamer extends Action
|
||||
case object Blog extends Action
|
||||
case object ForumAdmin extends Action
|
||||
val dbMap = Map(
|
||||
"modMessage" -> Message,
|
||||
|
@ -191,6 +192,7 @@ object ModActivity {
|
|||
"streamerDecline" -> Streamer,
|
||||
"streamerunlist" -> Streamer,
|
||||
"streamerTier" -> Streamer,
|
||||
"blogTier" -> Blog,
|
||||
"deletePost" -> ForumAdmin,
|
||||
"closeTopic" -> ForumAdmin
|
||||
)
|
||||
|
|
|
@ -68,6 +68,7 @@ case class Modlog(
|
|||
case Modlog.streamerFeature => "feature streamer" // BC
|
||||
case Modlog.streamerUnfeature => "unfeature streamer" // BC
|
||||
case Modlog.streamerTier => "set streamer tier"
|
||||
case Modlog.blogTier => "set blog tier"
|
||||
case Modlog.teamKick => "kick from team"
|
||||
case Modlog.teamEdit => "edited team"
|
||||
case Modlog.appealPost => "posted in appeal"
|
||||
|
@ -135,6 +136,7 @@ object Modlog {
|
|||
val streamerFeature = "streamerFeature" // BC
|
||||
val streamerUnfeature = "streamerUnfeature" // BC
|
||||
val streamerTier = "streamerTier"
|
||||
val blogTier = "blogTier"
|
||||
val teamKick = "teamKick"
|
||||
val teamEdit = "teamEdit"
|
||||
val appealPost = "appealPost"
|
||||
|
|
|
@ -29,6 +29,10 @@ final class ModlogApi(repo: ModlogRepo, userRepo: UserRepo, ircApi: IrcApi)(impl
|
|||
add {
|
||||
Modlog(mod.user.id, streamerId.some, Modlog.streamerTier, v.toString.some)
|
||||
}
|
||||
def blogTier(mod: Mod, blogId: String, v: Int) =
|
||||
add {
|
||||
Modlog(mod.user.id, blogId.some, Modlog.blogTier, v.toString.some)
|
||||
}
|
||||
|
||||
def practiceConfig(mod: User.ID) =
|
||||
add {
|
||||
|
|
|
@ -3,7 +3,6 @@ package lila.round
|
|||
import akka.actor._
|
||||
import akka.stream.scaladsl._
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.LilaStream
|
||||
|
|
|
@ -34,7 +34,9 @@ final class Env(
|
|||
val form = wire[UblogForm]
|
||||
|
||||
lila.common.Bus.subscribeFun("shadowban") { case lila.hub.actorApi.mod.Shadowban(userId, v) =>
|
||||
api.setShadowban(userId, v).unit
|
||||
api.setShadowban(userId, v) >>
|
||||
like.recomputeRankOfAllPosts(UblogBlog.Id.User(userId))
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ final class UblogApi(
|
|||
|
||||
def setTier(blog: UblogBlog.Id, tier: Int): Funit =
|
||||
colls.blog.update
|
||||
.one($id(blog), $set("modTier" -> tier, "tier" -> tier))
|
||||
.one($id(blog), $set("modTier" -> tier, "tier" -> tier), upsert = true)
|
||||
.void
|
||||
|
||||
private[ublog] def setShadowban(userId: User.ID, v: Boolean) = {
|
||||
|
|
|
@ -6,8 +6,8 @@ case class UblogBlog(
|
|||
_id: UblogBlog.Id,
|
||||
title: Option[String],
|
||||
intro: Option[String],
|
||||
tier: Int, // actual tier, auto or set by a mod
|
||||
modTier: Option[Int] // tier set by a mod
|
||||
tier: UblogBlog.Tier, // actual tier, auto or set by a mod
|
||||
modTier: Option[UblogBlog.Tier] // tier set by a mod
|
||||
) {
|
||||
def id = _id
|
||||
def visible = tier >= UblogBlog.Tier.VISIBLE
|
||||
|
@ -26,6 +26,7 @@ object UblogBlog {
|
|||
}
|
||||
}
|
||||
|
||||
type Tier = Int
|
||||
object Tier {
|
||||
val HIDDEN = 0 // not visible
|
||||
val VISIBLE = 1 // not listed in community page
|
||||
|
@ -38,6 +39,15 @@ object UblogBlog {
|
|||
if (user.marks.troll) Tier.HIDDEN
|
||||
else if (user.hasTitle || user.perfs.standard.glicko.establishedIntRating.exists(_ > 2200)) Tier.NORMAL
|
||||
else Tier.LOW
|
||||
|
||||
val options = List(
|
||||
HIDDEN -> "Hidden",
|
||||
VISIBLE -> "Unlisted",
|
||||
LOW -> "Low tier",
|
||||
NORMAL -> "Normal tier",
|
||||
HIGH -> "High tier",
|
||||
BEST -> "Best tier"
|
||||
)
|
||||
}
|
||||
|
||||
def make(user: User) = UblogBlog(
|
||||
|
|
|
@ -71,7 +71,7 @@ object UblogForm {
|
|||
created = UblogPost.Recorded(user.id, DateTime.now),
|
||||
updated = none,
|
||||
lived = none,
|
||||
likes = UblogPost.Likes(0)
|
||||
likes = UblogPost.Likes(1)
|
||||
)
|
||||
|
||||
def update(user: User, prev: UblogPost) =
|
||||
|
@ -85,4 +85,6 @@ object UblogForm {
|
|||
lived = prev.lived orElse live.option(UblogPost.Recorded(user.id, DateTime.now))
|
||||
)
|
||||
}
|
||||
|
||||
val tier = Form(single("tier" -> number(min = UblogBlog.Tier.HIDDEN, max = UblogBlog.Tier.BEST)))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package lila.ublog
|
||||
|
||||
import cats.implicits._
|
||||
import reactivemongo.api._
|
||||
import scala.concurrent.ExecutionContext
|
||||
import lila.db.dsl._
|
||||
|
@ -16,30 +17,50 @@ final class UblogLike(colls: UblogColls)(implicit ec: ExecutionContext) {
|
|||
colls.post.exists($id(post.id) ++ selectLiker(user.id))
|
||||
|
||||
def apply(postId: UblogPost.Id, user: User, v: Boolean): Fu[UblogPost.Likes] =
|
||||
countLikes(postId).flatMap {
|
||||
fetchRankData(postId).flatMap {
|
||||
case None => fuccess(UblogPost.Likes(v ?? 1))
|
||||
case Some((blog, prevLikes, liveAt)) =>
|
||||
case Some((prevLikes, liveAt, tier)) =>
|
||||
val likes = UblogPost.Likes(prevLikes.value + (if (v) 1 else -1))
|
||||
colls.post.update.one(
|
||||
$id(postId),
|
||||
$set(
|
||||
"likes" -> likes,
|
||||
"rank" -> computeRank(blog, likes, liveAt)
|
||||
"rank" -> computeRank(likes, liveAt, tier)
|
||||
) ++ {
|
||||
if (v) $addToSet("likers" -> user.id) else $pull("likers" -> user.id)
|
||||
}
|
||||
) inject likes
|
||||
}
|
||||
|
||||
private def computeRank(blog: UblogBlog, likes: UblogPost.Likes, liveAt: DateTime) =
|
||||
def recomputeRankOfAllPosts(blogId: UblogBlog.Id): Funit =
|
||||
colls.blog.byId[UblogBlog](blogId.full) flatMap {
|
||||
_ ?? { blog =>
|
||||
colls.post
|
||||
.find($doc("blog" -> blog.id), $doc("likes" -> true, "lived" -> true).some)
|
||||
.cursor[Bdoc]()
|
||||
.list() flatMap { docs =>
|
||||
lila.common.Future.applySequentially(docs) { doc =>
|
||||
(
|
||||
doc.string("_id"),
|
||||
doc.getAsOpt[UblogPost.Likes]("likes"),
|
||||
doc.getAsOpt[UblogPost.Recorded]("lived")
|
||||
).tupled ?? { case (id, likes, lived) =>
|
||||
colls.post.updateField($id(id), "rank", computeRank(likes, lived.at, blog.tier)).void
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def computeRank(likes: UblogPost.Likes, liveAt: DateTime, tier: UblogBlog.Tier) =
|
||||
UblogPost.Rank {
|
||||
liveAt plusHours {
|
||||
val baseHours = likesToHours(likes)
|
||||
blog.tier match {
|
||||
case UblogBlog.Tier.LOW => (baseHours * 0.2).toInt
|
||||
tier match {
|
||||
case UblogBlog.Tier.LOW => (baseHours * 0.3).toInt
|
||||
case UblogBlog.Tier.NORMAL => baseHours
|
||||
case UblogBlog.Tier.HIGH => baseHours * 5
|
||||
case UblogBlog.Tier.BEST => baseHours * 12
|
||||
case UblogBlog.Tier.HIGH => baseHours * 3
|
||||
case UblogBlog.Tier.BEST => baseHours * 10
|
||||
case _ => -99999
|
||||
}
|
||||
}
|
||||
|
@ -47,34 +68,33 @@ final class UblogLike(colls: UblogColls)(implicit ec: ExecutionContext) {
|
|||
|
||||
private def likesToHours(likes: UblogPost.Likes): Int =
|
||||
if (likes.value < 1) 0
|
||||
else (5 * math.log(likes.value) + 1).toInt.min(likes.value) * 24
|
||||
else (5 * math.log(likes.value) + 1).toInt.atMost(likes.value) * 12
|
||||
|
||||
private def countLikes(postId: UblogPost.Id): Fu[Option[(UblogBlog, UblogPost.Likes, DateTime)]] =
|
||||
private def fetchRankData(postId: UblogPost.Id): Fu[Option[(UblogPost.Likes, DateTime, UblogBlog.Tier)]] =
|
||||
colls.post
|
||||
.aggregateWith[Bdoc]() { framework =>
|
||||
.aggregateOne() { framework =>
|
||||
import framework._
|
||||
List(
|
||||
Match($id(postId)),
|
||||
Match($id(postId)) -> List(
|
||||
PipelineOperator($lookup.simple(colls.blog, "blog", "blog", "_id")),
|
||||
UnwindField("blog"),
|
||||
Project(
|
||||
$doc(
|
||||
"_id" -> false,
|
||||
"blog" -> true,
|
||||
"likes" -> $doc("$size" -> "$likers"),
|
||||
"lived.at" -> true
|
||||
"_id" -> false,
|
||||
"blog.tier" -> true,
|
||||
"likes" -> $doc("$size" -> "$likers"),
|
||||
"lived.at" -> true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
.headOption
|
||||
.map { docOption =>
|
||||
for {
|
||||
doc <- docOption
|
||||
likes <- doc.getAsOpt[UblogPost.Likes]("likes")
|
||||
lived <- doc.getAsOpt[Bdoc]("lived")
|
||||
liveAt <- doc.getAsOpt[DateTime]("at")
|
||||
blogs <- doc.getAsOpt[List[UblogBlog]]("blog")
|
||||
blog <- blogs.headOption
|
||||
} yield (blog, likes, liveAt)
|
||||
liveAt <- lived.getAsOpt[DateTime]("at")
|
||||
blog <- doc.getAsOpt[Bdoc]("blog")
|
||||
tier <- blog int "tier"
|
||||
} yield (likes, liveAt, tier)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,4 +16,7 @@ lichess.load.then(() => {
|
|||
.then(likes => button.text(likes).toggleClass(likeClass, liked));
|
||||
})
|
||||
);
|
||||
$('#form3-tier').on('change', function (this: HTMLSelectElement) {
|
||||
(this.parentNode as HTMLFormElement).submit();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue