From 1fbd805d6b7faa96bc9e3a6df55067deaff83db2 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 8 Sep 2021 13:44:39 +0200 Subject: [PATCH] liked ublog list --- app/controllers/Ublog.scala | 14 +++- app/views/blog/bits.scala | 3 +- app/views/ublog/blog.scala | 3 +- app/views/ublog/index.scala | 67 +++++++++-------- app/views/ublog/post.scala | 3 +- bin/mongodb/ublog-blog.js | 1 + conf/routes | 1 + modules/ublog/src/main/UblogPaginator.scala | 13 ++++ modules/ublog/src/main/UblogRank.scala | 80 ++++++++++----------- 9 files changed, 109 insertions(+), 76 deletions(-) diff --git a/app/controllers/Ublog.scala b/app/controllers/Ublog.scala index 50ba254216..2cd19d60e4 100644 --- a/app/controllers/Ublog.scala +++ b/app/controllers/Ublog.scala @@ -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 diff --git a/app/views/blog/bits.scala b/app/views/blog/bits.scala index 20c8a4c67b..79a0abfeaa 100644 --- a/app/views/blog/bits.scala +++ b/app/views/blog/bits.scala @@ -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) diff --git a/app/views/ublog/blog.scala b/app/views/ublog/blog.scala index 1b122bf28a..b04ee65c97 100644 --- a/app/views/ublog/blog.scala +++ b/app/views/ublog/blog.scala @@ -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")( diff --git a/app/views/ublog/index.scala b/app/views/ublog/index.scala index 0dee5a5732..4a6b0c7df4 100644 --- a/app/views/ublog/index.scala +++ b/app/views/ublog/index.scala @@ -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) ) ) } diff --git a/app/views/ublog/post.scala b/app/views/ublog/post.scala index f939ee691f..54fc5cb05e 100644 --- a/app/views/ublog/post.scala +++ b/app/views/ublog/post.scala @@ -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"), diff --git a/bin/mongodb/ublog-blog.js b/bin/mongodb/ublog-blog.js index b82a1de7f4..34a94602cb 100644 --- a/bin/mongodb/ublog-blog.js +++ b/bin/mongodb/ublog-blog.js @@ -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}`; diff --git a/conf/routes b/conf/routes index 6ccf0d0f99..ee462164a6 100644 --- a/conf/routes +++ b/conf/routes @@ -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 diff --git a/modules/ublog/src/main/UblogPaginator.scala b/modules/ublog/src/main/UblogPaginator.scala index f29e8460e2..64fc7f37d0 100644 --- a/modules/ublog/src/main/UblogPaginator.scala +++ b/modules/ublog/src/main/UblogPaginator.scala @@ -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]] = diff --git a/modules/ublog/src/main/UblogRank.scala b/modules/ublog/src/main/UblogRank.scala index 0851ca8b00..077850330e 100644 --- a/modules/ublog/src/main/UblogRank.scala +++ b/modules/ublog/src/main/UblogRank.scala @@ -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) - } }