liked ublog list

pull/9757/head
Thibault Duplessis 2021-09-08 13:44:39 +02:00
parent 3ed848b539
commit 1fbd805d6b
9 changed files with 109 additions and 76 deletions

View File

@ -206,7 +206,7 @@ final class Ublog(env: Env) extends LilaController(env) {
def community(page: Int) = Open { implicit ctx =>
NotForKids {
Reasonable(page, 10) {
Reasonable(page, 20) {
env.ublog.paginator.liveByCommunity(page) map { posts =>
Ok(html.ublog.index.community(posts))
}
@ -214,6 +214,18 @@ final class Ublog(env: Env) extends LilaController(env) {
}
}
def liked(page: Int) = Auth { implicit ctx => me =>
NotForKids {
Reasonable(page, 15) {
ctx.me ?? { me =>
env.ublog.paginator.liveByLiked(me, page) map { posts =>
Ok(html.ublog.index.liked(posts))
}
}
}
}
}
def userAtom(username: String) = Action.async {
env.user.repo.enabledNamed(username) flatMap {
case None => NotFound.fuccess

View File

@ -12,8 +12,9 @@ object bits {
def menu(year: Option[Int], active: Option[String]) =
st.nav(cls := "page-menu__menu subnav")(
a(cls := active.has("friends").option("active"), href := routes.Ublog.friends())("Friends blogs"),
a(cls := active.has("community").option("active"), href := routes.Ublog.community())("Community blogs"),
a(cls := active.has("friends").option("active"), href := routes.Ublog.friends())("Friends blogs"),
a(cls := active.has("liked").option("active"), href := routes.Ublog.liked())("Liked blog posts"),
a(cls := active.has("lichess").option("active"), href := routes.Blog.index())("Lichess blog"),
year.isDefined || active.has("lichess") option lila.blog.allYears.map { y =>
a(cls := (year has y).option("active"), href := routes.Blog.year(y))(y)

View File

@ -25,7 +25,8 @@ object blog {
atomLinkTag = link(
href := routes.Ublog.userAtom(user.username),
st.title := title
).some
).some,
robots = netConfig.crawlable && blog.listed
) {
main(cls := "box box-pad page page-small ublog-index")(
div(cls := "box__top")(

View File

@ -8,6 +8,7 @@ import lila.app.ui.ScalatagsTemplate._
import lila.common.paginator.Paginator
import lila.ublog.UblogPost
import lila.user.User
import play.api.mvc.Call
object index {
@ -39,47 +40,53 @@ object index {
)
}
def friends(posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) =
def friends(posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) = list(
title = "Friends blogs",
posts = posts,
menuItem = "friends",
route = routes.Ublog.friends _,
onEmpty = "Nothing to show. Follow some authors!"
)
def liked(posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) = list(
title = "Liked blog posts",
posts = posts,
menuItem = "liked",
route = routes.Ublog.liked _,
onEmpty = "Nothing to show. Like some posts!"
)
def community(posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) = list(
title = "Community blogs",
posts = posts,
menuItem = "community",
route = routes.Ublog.community _,
onEmpty = "Nothing to show."
)
private def list(
title: String,
posts: Paginator[UblogPost.PreviewPost],
menuItem: String,
route: Int => Call,
onEmpty: => Frag
)(implicit ctx: Context) =
views.html.base.layout(
moreCss = cssTag("ublog"),
moreJs = posts.hasNextPage option infiniteScrollTag,
title = "Friends blogs"
title = title
) {
main(cls := "page-menu")(
views.html.blog.bits.menu(none, "friends".some),
views.html.blog.bits.menu(none, menuItem.some),
main(cls := "page-menu__content box box-pad ublog-index")(
div(cls := "box__top")(
h1("Friends blogs")
),
div(cls := "box__top")(h1(title)),
if (posts.nbResults > 0)
div(cls := "ublog-index__posts ublog-post-cards infinite-scroll")(
posts.currentPageResults map { postView.card(_, showAuthor = true) },
pagerNext(posts, np => routes.Ublog.friends(np).url)
pagerNext(posts, np => route(np).url)
)
else
div(cls := "ublog-index__posts--empty")(
"Nothing to show. Follow some authors!"
)
)
)
}
def community(posts: Paginator[UblogPost.PreviewPost])(implicit ctx: Context) =
views.html.base.layout(
moreCss = cssTag("ublog"),
moreJs = posts.hasNextPage option infiniteScrollTag,
title = "Community blogs"
) {
main(cls := "page-menu")(
views.html.blog.bits.menu(none, "community".some),
main(cls := "page-menu__content box box-pad ublog-index")(
div(cls := "box__top")(
h1("Community blogs")
),
div(cls := "ublog-index__posts ublog-post-cards infinite-scroll")(
posts.currentPageResults map { postView.card(_, showAuthor = true) },
pagerNext(posts, np => routes.Ublog.community(np).url)
)
div(cls := "ublog-index__posts--empty")(onEmpty)
)
)
}

View File

@ -37,7 +37,8 @@ object post {
url = s"$netBaseUrl${routes.Ublog.post(user.username, post.slug, post.id.value)}",
description = post.intro
)
.some
.some,
robots = netConfig.crawlable && blog.listed
) {
main(cls := "box box-pad page page-small ublog-post")(
thumbnail(post, _.Large)(cls := "ublog-post__image"),

View File

@ -28,6 +28,7 @@ db.ublog_post.createIndex(
name: 'liveByRank',
}
);
db.ublog_post.createIndex({ likers: 1, rank: -1 }, { partialFilterExpression: { live: true }, name: 'liveByLiked' });
db.ublog_post.find({ blog: { $exists: false } }).forEach(p => {
blogId = `user:${p.user}`;

View File

@ -101,6 +101,7 @@ GET /blog/:id/:slug controllers.Blog.show(id: String, slug: S
GET /blog.atom controllers.Blog.atom
GET /blog.txt controllers.Blog.sitemapTxt
GET /blog/friends controllers.Ublog.friends(page: Int ?= 1)
GET /blog/liked controllers.Ublog.liked(page: Int ?= 1)
GET /blog/community controllers.Ublog.community(page: Int ?= 1)
# Training - Coordinate

View File

@ -52,6 +52,19 @@ final class UblogPaginator(
maxPerPage = maxPerPage
)
def liveByLiked(me: User, page: Int): Fu[Paginator[PreviewPost]] =
Paginator(
adapter = new Adapter[PreviewPost](
collection = colls.post,
selector = $doc("live" -> true, "likers" -> me.id),
projection = previewPostProjection.some,
sort = $sort desc "rank",
readPreference = ReadPreference.secondaryPreferred
),
currentPage = page,
maxPerPage = maxPerPage
)
object liveByFollowed {
def apply(user: User, page: Int): Fu[Paginator[PreviewPost]] =

View File

@ -17,20 +17,44 @@ final class UblogRank(colls: UblogColls)(implicit ec: ExecutionContext) {
colls.post.exists($id(post.id) ++ selectLiker(user.id))
def like(postId: UblogPost.Id, user: User, v: Boolean): Fu[UblogPost.Likes] =
fetchRankData(postId).flatMap {
case None => fuccess(UblogPost.Likes(v ?? 1))
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(likes, liveAt, tier)
) ++ {
if (v) $addToSet("likers" -> user.id) else $pull("likers" -> user.id)
}
) inject likes
}
colls.post
.aggregateOne() { framework =>
import framework._
Match($id(postId)) -> List(
PipelineOperator($lookup.simple(colls.blog, "blog", "blog", "_id")),
UnwindField("blog"),
Project(
$doc(
"_id" -> false,
"tier" -> "$blog.tier",
"likes" -> $doc("$size" -> "$likers"),
"at" -> "$lived.at"
)
)
)
}
.map { docOption =>
for {
doc <- docOption
likes <- doc.getAsOpt[UblogPost.Likes]("likes")
liveAt <- doc.getAsOpt[DateTime]("at")
tier <- doc int "tier"
} yield (likes, liveAt, tier)
}
.flatMap {
case None => fuccess(UblogPost.Likes(v ?? 1))
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(likes, liveAt, tier)
) ++ {
if (v) $addToSet("likers" -> user.id) else $pull("likers" -> user.id)
}
) inject likes
}
def recomputeRankOfAllPosts(blogId: UblogBlog.Id): Funit =
colls.blog.byId[UblogBlog](blogId.full) flatMap {
@ -71,32 +95,4 @@ final class UblogRank(colls: UblogColls)(implicit ec: ExecutionContext) {
}
}
}
private def fetchRankData(postId: UblogPost.Id): Fu[Option[(UblogPost.Likes, DateTime, UblogBlog.Tier)]] =
colls.post
.aggregateOne() { framework =>
import framework._
Match($id(postId)) -> List(
PipelineOperator($lookup.simple(colls.blog, "blog", "blog", "_id")),
UnwindField("blog"),
Project(
$doc(
"_id" -> false,
"blog.tier" -> true,
"likes" -> $doc("$size" -> "$likers"),
"lived.at" -> true
)
)
)
}
.map { docOption =>
for {
doc <- docOption
likes <- doc.getAsOpt[UblogPost.Likes]("likes")
lived <- doc.getAsOpt[Bdoc]("lived")
liveAt <- lived.getAsOpt[DateTime]("at")
blog <- doc.getAsOpt[Bdoc]("blog")
tier <- blog int "tier"
} yield (likes, liveAt, tier)
}
}