ublog post like timeline notification
parent
8a076b0867
commit
d6be3c2583
|
@ -163,6 +163,14 @@ final class Ublog(env: Env) extends LilaController(env) {
|
|||
}
|
||||
}
|
||||
|
||||
def redirect(id: String) = Open { implicit ctx =>
|
||||
env.ublog.api.postPreview(UblogPost.Id(id)) flatMap {
|
||||
_.fold(notFound) { post =>
|
||||
Redirect(urlOfPost(post)).fuccess
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setTier(blogId: String) = SecureBody(_.ModerateBlog) { implicit ctx => me =>
|
||||
UblogBlog.Id(blogId).??(env.ublog.api.getBlog) flatMap {
|
||||
_ ?? { blog =>
|
||||
|
|
|
@ -123,6 +123,11 @@ object timeline {
|
|||
)
|
||||
case BlogPost(id, slug, title) =>
|
||||
a(cls := "text", dataIcon := "", href := routes.Blog.show(id, slug))(title)
|
||||
case UblogPostLike(userId, postId, postTitle) =>
|
||||
trans.xLikesY(
|
||||
userLink(userId),
|
||||
a(href := routes.Ublog.redirect(postId))(postTitle)
|
||||
)
|
||||
case StreamStart(id, name) =>
|
||||
views.html.streamer.bits
|
||||
.redirectLink(id)(cls := "text", dataIcon := "")(trans.xStartedStreaming(name))
|
||||
|
|
|
@ -64,6 +64,7 @@ GET /@/:username/blog/drafts controllers.Ublog.drafts(username: String
|
|||
GET /@/:username/blog/new controllers.Ublog.form(username: String)
|
||||
GET /@/:username/blog.atom controllers.Ublog.userAtom(username: String)
|
||||
POST /ublog/new controllers.Ublog.create
|
||||
GET /ublog/$id<\w{8}>/redirect controllers.Ublog.redirect(id: String)
|
||||
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)
|
||||
|
|
|
@ -176,6 +176,9 @@ package timeline {
|
|||
case class BlogPost(id: String, slug: String, title: String) extends Atom("blogPost", true) {
|
||||
def userIds = Nil
|
||||
}
|
||||
case class UblogPostLike(userId: String, id: String, title: String) extends Atom("ublogPostLike", true) {
|
||||
def userIds = List(userId)
|
||||
}
|
||||
case class StreamStart(id: String, name: String) extends Atom("streamStart", true) {
|
||||
def userIds = List(id)
|
||||
}
|
||||
|
|
|
@ -18,37 +18,10 @@ case class Entry(
|
|||
) {
|
||||
|
||||
import Entry._
|
||||
import atomBsonHandlers._
|
||||
|
||||
def similarTo(other: Entry) = typ == other.typ && data == other.data
|
||||
|
||||
case object Deprecated extends lila.base.LilaException {
|
||||
val message = "Deprecated timeline entry"
|
||||
}
|
||||
|
||||
lazy val decode: Option[Atom] = Try(typ match {
|
||||
case "follow" => followHandler.readTry(data).get
|
||||
case "team-join" => teamJoinHandler.readTry(data).get
|
||||
case "team-create" => teamCreateHandler.readTry(data).get
|
||||
case "forum-post" => forumPostHandler.readTry(data).get
|
||||
case "ublog-post" => ublogPostHandler.readTry(data).get
|
||||
case "tour-join" => tourJoinHandler.readTry(data).get
|
||||
case "game-end" => gameEndHandler.readTry(data).get
|
||||
case "simul-create" => simulCreateHandler.readTry(data).get
|
||||
case "simul-join" => simulJoinHandler.readTry(data).get
|
||||
case "study-like" => studyLikeHandler.readTry(data).get
|
||||
case "plan-start" => planStartHandler.readTry(data).get
|
||||
case "plan-renew" => planRenewHandler.readTry(data).get
|
||||
case "blog-post" => blogPostHandler.readTry(data).get
|
||||
case "stream-start" => streamStartHandler.readTry(data).get
|
||||
case _ => sys error s"Unhandled atom type: $typ"
|
||||
}) match {
|
||||
case Success(atom) => Some(atom)
|
||||
case Failure(Deprecated) => none
|
||||
case Failure(err) =>
|
||||
lila.log("timeline").warn(err.getMessage)
|
||||
none
|
||||
}
|
||||
lazy val decode: Option[Atom] = atomBsonHandlers.handlers.get(typ).flatMap(_ readOpt data)
|
||||
|
||||
def userIds = decode.??(_.userIds)
|
||||
|
||||
|
@ -64,20 +37,21 @@ object Entry {
|
|||
private[timeline] def make(data: Atom): Entry = {
|
||||
import atomBsonHandlers._
|
||||
data match {
|
||||
case d: Follow => "follow" -> toBson(d)
|
||||
case d: TeamJoin => "team-join" -> toBson(d)
|
||||
case d: TeamCreate => "team-create" -> toBson(d)
|
||||
case d: ForumPost => "forum-post" -> toBson(d)
|
||||
case d: UblogPost => "ublog-post" -> toBson(d)
|
||||
case d: TourJoin => "tour-join" -> toBson(d)
|
||||
case d: GameEnd => "game-end" -> toBson(d)
|
||||
case d: SimulCreate => "simul-create" -> toBson(d)
|
||||
case d: SimulJoin => "simul-join" -> toBson(d)
|
||||
case d: StudyLike => "study-like" -> toBson(d)(studyLikeHandler)
|
||||
case d: PlanStart => "plan-start" -> toBson(d)(planStartHandler)
|
||||
case d: PlanRenew => "plan-renew" -> toBson(d)(planRenewHandler)
|
||||
case d: BlogPost => "blog-post" -> toBson(d)(blogPostHandler)
|
||||
case d: StreamStart => "stream-start" -> toBson(d)(streamStartHandler)
|
||||
case d: Follow => "follow" -> toBson(d)
|
||||
case d: TeamJoin => "team-join" -> toBson(d)
|
||||
case d: TeamCreate => "team-create" -> toBson(d)
|
||||
case d: ForumPost => "forum-post" -> toBson(d)
|
||||
case d: UblogPost => "ublog-post" -> toBson(d)
|
||||
case d: TourJoin => "tour-join" -> toBson(d)
|
||||
case d: GameEnd => "game-end" -> toBson(d)
|
||||
case d: SimulCreate => "simul-create" -> toBson(d)
|
||||
case d: SimulJoin => "simul-join" -> toBson(d)
|
||||
case d: StudyLike => "study-like" -> toBson(d)
|
||||
case d: PlanStart => "plan-start" -> toBson(d)
|
||||
case d: PlanRenew => "plan-renew" -> toBson(d)
|
||||
case d: BlogPost => "blog-post" -> toBson(d)
|
||||
case d: UblogPostLike => "ublog-post-like" -> toBson(d)
|
||||
case d: StreamStart => "stream-start" -> toBson(d)
|
||||
}
|
||||
} match {
|
||||
case (typ, bson) =>
|
||||
|
@ -85,52 +59,73 @@ object Entry {
|
|||
}
|
||||
|
||||
object atomBsonHandlers {
|
||||
implicit val followHandler = Macros.handler[Follow]
|
||||
implicit val teamJoinHandler = Macros.handler[TeamJoin]
|
||||
implicit val teamCreateHandler = Macros.handler[TeamCreate]
|
||||
implicit val forumPostHandler = Macros.handler[ForumPost]
|
||||
implicit val ublogPostHandler = Macros.handler[UblogPost]
|
||||
implicit val tourJoinHandler = Macros.handler[TourJoin]
|
||||
implicit val gameEndHandler = Macros.handler[GameEnd]
|
||||
implicit val simulCreateHandler = Macros.handler[SimulCreate]
|
||||
implicit val simulJoinHandler = Macros.handler[SimulJoin]
|
||||
implicit val studyLikeHandler = Macros.handler[StudyLike]
|
||||
implicit val planStartHandler = Macros.handler[PlanStart]
|
||||
implicit val planRenewHandler = Macros.handler[PlanRenew]
|
||||
implicit val blogPostHandler = Macros.handler[BlogPost]
|
||||
implicit val streamStartHandler = Macros.handler[StreamStart]
|
||||
implicit val followHandler = Macros.handler[Follow]
|
||||
implicit val teamJoinHandler = Macros.handler[TeamJoin]
|
||||
implicit val teamCreateHandler = Macros.handler[TeamCreate]
|
||||
implicit val forumPostHandler = Macros.handler[ForumPost]
|
||||
implicit val ublogPostHandler = Macros.handler[UblogPost]
|
||||
implicit val tourJoinHandler = Macros.handler[TourJoin]
|
||||
implicit val gameEndHandler = Macros.handler[GameEnd]
|
||||
implicit val simulCreateHandler = Macros.handler[SimulCreate]
|
||||
implicit val simulJoinHandler = Macros.handler[SimulJoin]
|
||||
implicit val studyLikeHandler = Macros.handler[StudyLike]
|
||||
implicit val planStartHandler = Macros.handler[PlanStart]
|
||||
implicit val planRenewHandler = Macros.handler[PlanRenew]
|
||||
implicit val blogPostHandler = Macros.handler[BlogPost]
|
||||
implicit val ublogPostLikeHandler = Macros.handler[UblogPostLike]
|
||||
implicit val streamStartHandler = Macros.handler[StreamStart]
|
||||
|
||||
val handlers = Map(
|
||||
"follow" -> followHandler,
|
||||
"team-join" -> teamJoinHandler,
|
||||
"team-create" -> teamCreateHandler,
|
||||
"forum-post" -> forumPostHandler,
|
||||
"ublog-post" -> ublogPostHandler,
|
||||
"tour-join" -> tourJoinHandler,
|
||||
"game-end" -> gameEndHandler,
|
||||
"simul-create" -> simulCreateHandler,
|
||||
"simul-join" -> simulJoinHandler,
|
||||
"study-like" -> studyLikeHandler,
|
||||
"plan-start" -> planStartHandler,
|
||||
"plan-renew" -> planRenewHandler,
|
||||
"blog-post" -> blogPostHandler,
|
||||
"ublog-post-like" -> ublogPostLikeHandler,
|
||||
"stream-start" -> streamStartHandler
|
||||
)
|
||||
}
|
||||
|
||||
object atomJsonWrite {
|
||||
implicit val followWrite = Json.writes[Follow]
|
||||
implicit val teamJoinWrite = Json.writes[TeamJoin]
|
||||
implicit val teamCreateWrite = Json.writes[TeamCreate]
|
||||
implicit val forumPostWrite = Json.writes[ForumPost]
|
||||
implicit val ublogPostWrite = Json.writes[UblogPost]
|
||||
implicit val tourJoinWrite = Json.writes[TourJoin]
|
||||
implicit val gameEndWrite = Json.writes[GameEnd]
|
||||
implicit val simulCreateWrite = Json.writes[SimulCreate]
|
||||
implicit val simulJoinWrite = Json.writes[SimulJoin]
|
||||
implicit val studyLikeWrite = Json.writes[StudyLike]
|
||||
implicit val planStartWrite = Json.writes[PlanStart]
|
||||
implicit val planRenewWrite = Json.writes[PlanRenew]
|
||||
implicit val blogPostWrite = Json.writes[BlogPost]
|
||||
implicit val streamStartWrite = Json.writes[StreamStart]
|
||||
val followWrite = Json.writes[Follow]
|
||||
val teamJoinWrite = Json.writes[TeamJoin]
|
||||
val teamCreateWrite = Json.writes[TeamCreate]
|
||||
val forumPostWrite = Json.writes[ForumPost]
|
||||
val ublogPostWrite = Json.writes[UblogPost]
|
||||
val tourJoinWrite = Json.writes[TourJoin]
|
||||
val gameEndWrite = Json.writes[GameEnd]
|
||||
val simulCreateWrite = Json.writes[SimulCreate]
|
||||
val simulJoinWrite = Json.writes[SimulJoin]
|
||||
val studyLikeWrite = Json.writes[StudyLike]
|
||||
val planStartWrite = Json.writes[PlanStart]
|
||||
val planRenewWrite = Json.writes[PlanRenew]
|
||||
val blogPostWrite = Json.writes[BlogPost]
|
||||
val ublogPostLikeWrite = Json.writes[UblogPostLike]
|
||||
val streamStartWrite = Json.writes[StreamStart]
|
||||
implicit val atomWrite = Writes[Atom] {
|
||||
case d: Follow => followWrite writes d
|
||||
case d: TeamJoin => teamJoinWrite writes d
|
||||
case d: TeamCreate => teamCreateWrite writes d
|
||||
case d: ForumPost => forumPostWrite writes d
|
||||
case d: UblogPost => ublogPostWrite writes d
|
||||
case d: TourJoin => tourJoinWrite writes d
|
||||
case d: GameEnd => gameEndWrite writes d
|
||||
case d: SimulCreate => simulCreateWrite writes d
|
||||
case d: SimulJoin => simulJoinWrite writes d
|
||||
case d: StudyLike => studyLikeWrite writes d
|
||||
case d: PlanStart => planStartWrite writes d
|
||||
case d: PlanRenew => planRenewWrite writes d
|
||||
case d: BlogPost => blogPostWrite writes d
|
||||
case d: StreamStart => streamStartWrite writes d
|
||||
case d: Follow => followWrite writes d
|
||||
case d: TeamJoin => teamJoinWrite writes d
|
||||
case d: TeamCreate => teamCreateWrite writes d
|
||||
case d: ForumPost => forumPostWrite writes d
|
||||
case d: UblogPost => ublogPostWrite writes d
|
||||
case d: TourJoin => tourJoinWrite writes d
|
||||
case d: GameEnd => gameEndWrite writes d
|
||||
case d: SimulCreate => simulCreateWrite writes d
|
||||
case d: SimulJoin => simulJoinWrite writes d
|
||||
case d: StudyLike => studyLikeWrite writes d
|
||||
case d: PlanStart => planStartWrite writes d
|
||||
case d: PlanRenew => planRenewWrite writes d
|
||||
case d: BlogPost => blogPostWrite writes d
|
||||
case d: UblogPostLike => ublogPostLikeWrite writes d
|
||||
case d: StreamStart => streamStartWrite writes d
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -103,6 +103,9 @@ final class UblogApi(
|
|||
.cursor[UblogPost.PreviewPost](ReadPreference.secondaryPreferred)
|
||||
.list(nb)
|
||||
|
||||
def postPreview(id: UblogPost.Id) =
|
||||
colls.post.byId[UblogPost.PreviewPost](id.value, previewPostProjection)
|
||||
|
||||
private def imageRel(post: UblogPost) = s"ublog:${post.id}"
|
||||
|
||||
def uploadImage(user: User, post: UblogPost, picture: PicfitApi.FilePart): Fu[UblogPost] =
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package lila.ublog
|
||||
|
||||
import cats.implicits._
|
||||
import reactivemongo.api._
|
||||
import scala.concurrent.ExecutionContext
|
||||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
import org.joda.time.DateTime
|
||||
import play.api.i18n.Lang
|
||||
import reactivemongo.api._
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
final class UblogRank(colls: UblogColls)(implicit ec: ExecutionContext) {
|
||||
import lila.db.dsl._
|
||||
import lila.hub.actorApi.timeline.{ Propagate, UblogPostLike }
|
||||
import lila.user.User
|
||||
|
||||
final class UblogRank(colls: UblogColls, timeline: lila.hub.actors.Timeline)(implicit ec: ExecutionContext) {
|
||||
|
||||
import UblogBsonHandlers._
|
||||
|
||||
|
@ -22,16 +24,16 @@ final class UblogRank(colls: UblogColls)(implicit ec: ExecutionContext) {
|
|||
.aggregateOne() { framework =>
|
||||
import framework._
|
||||
Match($id(postId)) -> List(
|
||||
PipelineOperator($lookup.simple(colls.blog, "blog", "blog", "_id")),
|
||||
PipelineOperator($lookup.simple(from = colls.blog, as = "blog", local = "blog", foreign = "_id")),
|
||||
UnwindField("blog"),
|
||||
Project(
|
||||
$doc(
|
||||
"_id" -> false,
|
||||
"tier" -> "$blog.tier",
|
||||
"likes" -> $doc("$size" -> "$likers"),
|
||||
"topics" -> "$topics",
|
||||
"at" -> "$lived.at",
|
||||
"language" -> true,
|
||||
"at" -> "$lived.at"
|
||||
"title" -> true
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -39,25 +41,31 @@ final class UblogRank(colls: UblogColls)(implicit ec: ExecutionContext) {
|
|||
.map { docOption =>
|
||||
for {
|
||||
doc <- docOption
|
||||
id <- doc.getAsOpt[UblogPost.Id]("_id")
|
||||
likes <- doc.getAsOpt[UblogPost.Likes]("likes")
|
||||
topics <- doc.getAsOpt[List[UblogTopic]]("topics")
|
||||
liveAt <- doc.getAsOpt[DateTime]("at")
|
||||
language <- doc.getAsOpt[Lang]("language")
|
||||
tier <- doc int "tier"
|
||||
} yield (topics, likes, liveAt, language, tier)
|
||||
language <- doc.getAsOpt[Lang]("language")
|
||||
title <- doc string "title"
|
||||
} yield (id, topics, likes, liveAt, tier, language, title)
|
||||
}
|
||||
.flatMap {
|
||||
_.fold(fuccess(UblogPost.Likes(v ?? 1))) { case (topics, prevLikes, liveAt, language, tier) =>
|
||||
val likes = UblogPost.Likes(prevLikes.value + (if (v) 1 else -1))
|
||||
colls.post.update.one(
|
||||
$id(postId),
|
||||
$set(
|
||||
"likes" -> likes,
|
||||
"rank" -> computeRank(topics, likes, liveAt, language, tier)
|
||||
) ++ {
|
||||
if (v) $addToSet("likers" -> user.id) else $pull("likers" -> user.id)
|
||||
}
|
||||
) inject likes
|
||||
_.fold(fuccess(UblogPost.Likes(v ?? 1))) {
|
||||
case (id, topics, prevLikes, liveAt, tier, language, title) =>
|
||||
val likes = UblogPost.Likes(prevLikes.value + (if (v) 1 else -1))
|
||||
colls.post.update.one(
|
||||
$id(postId),
|
||||
$set(
|
||||
"likes" -> likes,
|
||||
"rank" -> computeRank(topics, likes, liveAt, language, tier)
|
||||
) ++ {
|
||||
if (v) $addToSet("likers" -> user.id) else $pull("likers" -> user.id)
|
||||
}
|
||||
) >>- {
|
||||
if (v && tier >= UblogBlog.Tier.NORMAL)
|
||||
timeline ! (Propagate(UblogPostLike(user.id, id.value, title)) toFollowersOf user.id)
|
||||
} inject likes
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue