From e91060252ef20d90737468fd3417ef248d3c3151 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 15 Apr 2015 11:51:53 +0200 Subject: [PATCH] display viewed videos --- app/controllers/Video.scala | 8 ++--- app/views/video/author.scala.html | 2 +- app/views/video/card.scala.html | 19 ++++++----- app/views/video/index.scala.html | 2 +- app/views/video/search.scala.html | 2 +- app/views/video/show.scala.html | 6 ++-- modules/video/src/main/VideoApi.scala | 45 ++++++++++++++++++++------- modules/video/src/main/View.scala | 2 ++ public/stylesheets/video.css | 10 ++++++ 9 files changed, 67 insertions(+), 29 deletions(-) diff --git a/app/controllers/Video.scala b/app/controllers/Video.scala index a77345fac5..bfd2e98aab 100644 --- a/app/controllers/Video.scala +++ b/app/controllers/Video.scala @@ -24,10 +24,10 @@ object Video extends LilaController { def index = Open { implicit ctx => WithUserControl { control => control.query match { - case Some(query) => env.api.video.search(query, getInt("page") | 1) map { videos => + case Some(query) => env.api.video.search(ctx.me, query, getInt("page") | 1) map { videos => Ok(html.video.search(videos, control)) } - case None => env.api.video.byTags(control.filter.tags, getInt("page") | 1) zip + case None => env.api.video.byTags(ctx.me, control.filter.tags, getInt("page") | 1) zip env.api.video.count.apply map { case (videos, count) => Ok(html.video.index(videos, count, control)) @@ -40,7 +40,7 @@ object Video extends LilaController { WithUserControl { control => env.api.video.find(id) flatMap { case None => fuccess(NotFound(html.video.notFound(control))) - case Some(video) => env.api.video.similar(video, 9) zip + case Some(video) => env.api.video.similar(ctx.me, video, 9) zip ctx.userId.?? { userId => env.api.view.add(View.make(videoId = video.id, userId = userId)) } map { @@ -53,7 +53,7 @@ object Video extends LilaController { def author(author: String) = Open { implicit ctx => WithUserControl { control => - env.api.video.byAuthor(author, getInt("page") | 1) map { videos => + env.api.video.byAuthor(ctx.me, author, getInt("page") | 1) map { videos => Ok(html.video.author(author, videos, control)) } } diff --git a/app/views/video/author.scala.html b/app/views/video/author.scala.html index a616feb073..accccf132a 100644 --- a/app/views/video/author.scala.html +++ b/app/views/video/author.scala.html @@ -1,4 +1,4 @@ -@(author: String, videos: Paginator[lila.video.Video], control: lila.video.UserControl)(implicit ctx: Context) +@(author: String, videos: Paginator[lila.video.VideoView], control: lila.video.UserControl)(implicit ctx: Context) @layout( title = s"$author • Free Chess Videos", diff --git a/app/views/video/card.scala.html b/app/views/video/card.scala.html index cac9315a92..e455e07f5d 100644 --- a/app/views/video/card.scala.html +++ b/app/views/video/card.scala.html @@ -1,16 +1,19 @@ -@(video: lila.video.Video, control: lila.video.UserControl)(implicit ctx: Context) +@(vv: lila.video.VideoView, control: lila.video.UserControl)(implicit ctx: Context) - - + + @if(ctx.isAuth) { + + } + - @video.title + @vv.video.title - @video.title - @video.author - @video.targets.map(lila.video.Target.name).mkString(", ") + @vv.video.title + @vv.video.author + @vv.video.targets.map(lila.video.Target.name).mkString(", ") - @video.tags.map { tag => + @vv.video.tags.map { tag => @tag.capitalize } diff --git a/app/views/video/index.scala.html b/app/views/video/index.scala.html index 39aad7a4fb..f957b1ddf7 100644 --- a/app/views/video/index.scala.html +++ b/app/views/video/index.scala.html @@ -1,4 +1,4 @@ -@(videos: Paginator[lila.video.Video], count: Int, control: lila.video.UserControl)(implicit ctx: Context) +@(videos: Paginator[lila.video.VideoView], count: Int, control: lila.video.UserControl)(implicit ctx: Context) @layout( title = "Free Chess Videos", diff --git a/app/views/video/search.scala.html b/app/views/video/search.scala.html index e92613f8bd..0bbc2b78ca 100644 --- a/app/views/video/search.scala.html +++ b/app/views/video/search.scala.html @@ -1,4 +1,4 @@ -@(videos: Paginator[lila.video.Video], control: lila.video.UserControl)(implicit ctx: Context) +@(videos: Paginator[lila.video.VideoView], control: lila.video.UserControl)(implicit ctx: Context) @layout( title = s"${control.query.getOrElse("Search")} • Free Chess Videos", diff --git a/app/views/video/show.scala.html b/app/views/video/show.scala.html index 55cf395b79..b4290163c9 100644 --- a/app/views/video/show.scala.html +++ b/app/views/video/show.scala.html @@ -1,4 +1,4 @@ -@(video: lila.video.Video, similar: List[lila.video.Video], control: lila.video.UserControl)(implicit ctx: Context) +@(video: lila.video.Video, similar: Seq[lila.video.VideoView], control: lila.video.UserControl)(implicit ctx: Context) @layout( title = s"${video.title} • Free Chess Videos", @@ -24,8 +24,8 @@ control = control) { }
- @similar.map { video => - @card(video, control) + @similar.map { vv => + @card(vv, control) }
diff --git a/modules/video/src/main/VideoApi.scala b/modules/video/src/main/VideoApi.scala index 512a5e59ce..6cf1f68c09 100644 --- a/modules/video/src/main/VideoApi.scala +++ b/modules/video/src/main/VideoApi.scala @@ -26,6 +26,17 @@ private[video] final class VideoApi( private implicit val TagNbBSONHandler = Macros.handler[TagNb] import View.viewBSONHandler + private def videoViews(userOption: Option[User])(videos: Seq[Video]): Fu[Seq[VideoView]] = userOption match { + case None => fuccess { + videos map { VideoView(_, false) } + } + case Some(user) => view.seenVideoIds(user, videos) map { ids => + videos.map { v => + VideoView(v, ids contains v.id) + } + } + } + object video { private val maxPerPage = 18 @@ -33,7 +44,7 @@ private[video] final class VideoApi( def find(id: Video.ID): Fu[Option[Video]] = videoColl.find(BSONDocument("_id" -> id)).one[Video] - def search(query: String, page: Int): Fu[Paginator[Video]] = { + def search(user: Option[User], query: String, page: Int): Fu[Paginator[VideoView]] = { val q = query.split(' ').map { word => s""""$word"""" } mkString " " val textScore = BSONDocument("score" -> BSONDocument("$meta" -> "textScore")) Paginator( @@ -44,7 +55,7 @@ private[video] final class VideoApi( ), projection = textScore, sort = textScore - ), + ) mapFutureList videoViews(user), currentPage = page, maxPerPage = maxPerPage) } @@ -75,18 +86,18 @@ private[video] final class VideoApi( doc flatMap (_.getAs[String]("_id")) } - def popular(page: Int): Fu[Paginator[Video]] = Paginator( + def popular(user: Option[User], page: Int): Fu[Paginator[VideoView]] = Paginator( adapter = new BSONAdapter[Video]( collection = videoColl, selector = BSONDocument(), projection = BSONDocument(), sort = BSONDocument("metadata.likes" -> -1) - ), + ) mapFutureList videoViews(user), currentPage = page, maxPerPage = maxPerPage) - def byTags(tags: List[Tag], page: Int): Fu[Paginator[Video]] = - if (tags.isEmpty) popular(page) + def byTags(user: Option[User], tags: List[Tag], page: Int): Fu[Paginator[VideoView]] = + if (tags.isEmpty) popular(user, page) else Paginator( adapter = new BSONAdapter[Video]( collection = videoColl, @@ -95,11 +106,11 @@ private[video] final class VideoApi( ), projection = BSONDocument(), sort = BSONDocument("metadata.likes" -> -1) - ), + ) mapFutureList videoViews(user), currentPage = page, maxPerPage = maxPerPage) - def byAuthor(author: String, page: Int): Fu[Paginator[Video]] = + def byAuthor(user: Option[User], author: String, page: Int): Fu[Paginator[VideoView]] = Paginator( adapter = new BSONAdapter[Video]( collection = videoColl, @@ -108,11 +119,11 @@ private[video] final class VideoApi( ), projection = BSONDocument(), sort = BSONDocument("metadata.likes" -> -1) - ), + ) mapFutureList videoViews(user), currentPage = page, maxPerPage = maxPerPage) - def similar(video: Video, max: Int): Fu[List[Video]] = + def similar(user: Option[User], video: Video, max: Int): Fu[Seq[VideoView]] = videoColl.find(BSONDocument( "tags" -> BSONDocument("$in" -> video.tags), "_id" -> BSONDocument("$ne" -> video.id) @@ -120,7 +131,7 @@ private[video] final class VideoApi( .cursor[Video] .collect[List]().map { videos => videos.sortBy { v => -v.similarity(video) } take max - } + } flatMap videoViews(user) object count { @@ -149,6 +160,18 @@ private[video] final class VideoApi( viewColl.db command Count(viewColl.name, BSONDocument( View.BSONFields.id -> View.makeId(video.id, user.id) ).some) map (0!=) + + def seenVideoIds(user: User, videos: Seq[Video]): Fu[Set[Video.ID]] = + viewColl.find( + BSONDocument( + "_id" -> BSONDocument("$in" -> videos.map { v => + View.makeId(v.id, user.id) + }) + ), + BSONDocument(View.BSONFields.videoId -> true, "_id" -> false) + ).cursor[BSONDocument].collect[List]() map { docs => + docs.flatMap(_.getAs[String](View.BSONFields.videoId)).toSet + } } object tag { diff --git a/modules/video/src/main/View.scala b/modules/video/src/main/View.scala index 59efe8aef4..0c2bd6f305 100644 --- a/modules/video/src/main/View.scala +++ b/modules/video/src/main/View.scala @@ -8,6 +8,8 @@ case class View( userId: String, date: DateTime) +case class VideoView(video: Video, view: Boolean) + object View { def makeId(videoId: Video.ID, userId: String) = s"$videoId/$userId" diff --git a/public/stylesheets/video.css b/public/stylesheets/video.css index 39f05a2bf1..da0aa5c7b7 100644 --- a/public/stylesheets/video.css +++ b/public/stylesheets/video.css @@ -90,6 +90,16 @@ transform: translateY(-100%); opacity: 1; } +#video .card .view { + position: absolute; + bottom: 5px; + right: 8px; + z-index: 2; + opacity: 0.3; +} +#video .card .view.yes { + opacity: 1; +} #video .card .full-title { font-weight: bold; display: block;