scala-news-reader

rss/atom news reader in scala

git clone https://9o.is/git/scala-news-reader.git

commit 47ab6ae8a56ec61efa4d8a6c0dab7d62115f9e8d
parent 8887cf1a58d9c76d37181e4b583c79d9f7fe72be
Author: Jul <jul@9o.is>
Date:   Sat,  3 Aug 2013 12:59:02 -0400

can now view saved articles

Diffstat:
Msrc/main/scala/com/joereader/config/Site.scala | 2++
Msrc/main/scala/com/joereader/snippet/ArticleSnip.scala | 20+++++++++++++++++---
Msrc/main/scala/com/joereader/snippet/ReaderSnip.scala | 46++++++++++++++++++++++++++++++++++++++--------
Msrc/main/scala/com/joereader/snippet/UserTopbar.scala | 5++++-
Msrc/main/scala/com/joereader/snippet/UserWriterSnipView.scala | 3+--
Msrc/main/webapp/blogwriter.html | 4++--
Msrc/main/webapp/reader.html | 4++--
Asrc/main/webapp/saved.html | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main/webapp/templates-hidden/parts/blog-profile.html | 4++--
Msrc/main/webapp/templates-hidden/parts/user-profile.html | 4++--
10 files changed, 148 insertions(+), 22 deletions(-)

diff --git a/src/main/scala/com/joereader/config/Site.scala b/src/main/scala/com/joereader/config/Site.scala @@ -29,6 +29,7 @@ object Site extends Locs { // others val reader = MenuLoc(Menu.i("Reader") / "reader" >> RequireLoggedIn) + val savedArticles = MenuLoc(Menu.i("Saved Articles") / "reader" / "saved" >> RequireLoggedIn >> TemplateBox(() => Templates("saved" :: Nil))) val searchCategories = MenuLoc(Menu.i("Search Categories") / "search" / "categories" >> RequireLoggedIn) // tmp: should be a menu param that gets a category @@ -151,6 +152,7 @@ object Site extends Locs { signUp6.menu, signUp7.menu, reader.menu, + savedArticles.menu, searchCategories.menu, searchWriters.menu, editAccount.menu, diff --git a/src/main/scala/com/joereader/snippet/ArticleSnip.scala b/src/main/scala/com/joereader/snippet/ArticleSnip.scala @@ -18,9 +18,18 @@ import xml._ * Mix this in to list articles. */ trait ArticleSnip { - + + def sort(entries: List[(FeedEntry, BlogWriterUser)]) = + entries.sortWith((x,y) => x._1.date.getTime > y._1.date.getTime) + def articles(entries: List[(FeedEntry, BlogWriterUser)]): CssSel = - ".article *" #> entries.map { + if(entries.isEmpty) + ".reader-nav [class+]" #> "hide" & + ".article *" #> + <p style="padding: 200px 0; text-align: center"> + No articles to display here + </p> + else ".article *" #> sort(entries).map { f => val (entry, bwu) = f @@ -96,10 +105,15 @@ trait ArticleSnip { "a [href]" #> bwu.link } } + +} + +object Article { + def separator = "~" } case class Article(blog: Blog, entry: FeedEntry) { - def id: String = blog.id.get + "~" + entry.id + def id: String = blog.id.get + Article.separator + entry.id def idHash = id.hashCode } diff --git a/src/main/scala/com/joereader/snippet/ReaderSnip.scala b/src/main/scala/com/joereader/snippet/ReaderSnip.scala @@ -20,28 +20,58 @@ class ReaderSnip extends UserSnip with ArticleSnip { user => val entries: List[(FeedEntry, BlogWriterUser)] = - user.following.allUsers.map(bwu => + user.following.allUsers.flatMap(bwu => for { blog <- bwu.blog blogWriter <- bwu.blogWriter - } yield - blog.urlRss.get.head.entries. - filter(_.author.name == blogWriter.name.get.toString). - map((_, bwu)) - ).flatten.flatten + } yield blog.urlRss.get.head.entries. + filter(_.author.name == blogWriter.name.get.toString). + map((_, bwu))).flatten + + articles(entries) + + }(TestUser.isLoggedIn, NodeSeq.Empty) + + def savedArticles(html: NodeSeq): NodeSeq = serve(html) { + user => + + def entries: List[(FeedEntry, BlogWriterUser)] = { + val blogIds: List[String] = + user.saved.get.flatMap(str => + str.split(Article.separator).headOption) + + val blogs: List[Blog] = + blogIds.flatMap(Blog.findByStringId) + + val allEntries: List[(Blog, List[FeedEntry])] = + blogs.map(b => (b, b.urlRss.get.head.entries)).toList + + val savedEntries: List[(Blog, List[FeedEntry])] = + allEntries.map(f => + (f._1, f._2.filter(e => user.saved.get. + exists(_ == Article(f._1, e).id)))).filterNot(_._2.isEmpty) + + savedEntries.flatMap(f => + f._2.map { entry => + val blog = Some(f._1) + val blogWriter = f._1.writer(entry.author.name) + val user = blogWriter.flatMap(_.user.obj) + (entry, BlogWriterUser(user, blog, blogWriter)) + }) + } articles(entries) }(TestUser.isLoggedIn, NodeSeq.Empty) def comingSoon: NodeSeq = - if(TestUser.isLoggedIn) NodeSeq.Empty + if (TestUser.isLoggedIn) NodeSeq.Empty else { <div style="margin: 100px 0" class="text-center"> <h3>Have another blog?</h3> <a data-lift="UserBlogsSnip.addBlog" class="btn btn-primary" style="margin-bottom: 50px">Add Blog</a> <br/><br/> - {Templates("templates-hidden" :: "parts" :: "social" :: Nil).openOr(NodeSeq.Empty)} + { Templates("templates-hidden" :: "parts" :: "social" :: Nil).openOr(NodeSeq.Empty) } <h2>Coming Soon!</h2> <p>We'll send an email as soon as we're ready.</p> </div> diff --git a/src/main/scala/com/joereader/snippet/UserTopbar.scala b/src/main/scala/com/joereader/snippet/UserTopbar.scala @@ -36,7 +36,7 @@ object UserTopbar { <a href={Site.searchCategories.url}>Search Writers</a> </li> <li> - <a href={Site.editAccount.url}>Settings</a> + <a href={Site.savedArticles.url}>Saved Articles</a> </li> { if(user.isWriter) { @@ -55,6 +55,9 @@ object UserTopbar { } <li class="divider"></li> <li> + <a href={Site.editAccount.url}>Settings</a> + </li> + <li> <a href={Site.logout.url}>Log Out</a> </li> </ul> diff --git a/src/main/scala/com/joereader/snippet/UserWriterSnipView.scala b/src/main/scala/com/joereader/snippet/UserWriterSnipView.scala @@ -62,8 +62,7 @@ trait UserWriterSnipView extends UserSnip with ArticleSnip with BackgroundSnip { } yield blog.urlRss.get.head.entries. filter(_.author.name == blogWriter.name.get). map((_, BlogWriterUser(Some(user), Some(blog), Some(blogWriter))))). - flatten. - sortWith((x,y) => x._1.date.getTime > y._1.date.getTime ) + flatten articles(entries) diff --git a/src/main/webapp/blogwriter.html b/src/main/webapp/blogwriter.html @@ -85,8 +85,8 @@ <div class="row-fluid sub-header"> <div class="span12"> <time class="timeago" datetime=""></time> - <a class="article-share" href="">Share</a> - <a class="article-save" href="">Save</a> + <span class="article-share"></span> + <span class="article-save"></span> </div> </div> <span id="article-content"></span> diff --git a/src/main/webapp/reader.html b/src/main/webapp/reader.html @@ -62,8 +62,8 @@ <div class="row-fluid sub-header"> <div class="span12"> <time class="timeago" datetime=""></time> - <a class="article-share" href="">Share</a> - <a class="article-save" href="">Save</a> + <span class="article-share"></span> + <span class="article-save"></span> </div> </div> <span id="article-content"></span> diff --git a/src/main/webapp/saved.html b/src/main/webapp/saved.html @@ -0,0 +1,78 @@ +<div data-lift="surround?with=base-wrap;at=content"> + + <div class="container"> + <div data-lift="ReaderSnip.comingSoon"></div> + <div data-lift="ReaderSnip.savedArticles" class="row-fluid"> + + <div id="reader-nav-wrapper" class="span2 offset0"> + <div id="reader-nav" class="reader-nav reader-span-spacing nolink-decoration"> + <ul id="reader-writers" class="nav text-center"> + <li id="writer"> + <a href=""> + <img id="writer-image" src="" /> + </a> + <a id="writer-name" href=""></a> + </li> + </ul> + + </div> + </div> + + <div id="boxed-articles-wrapper" class="span8 offset0"> + <div class="boxed-articles boxed-articles-span"> + <div class="article keynav-article"> + <div class="article-inner"> + <div class="article-key"></div> + <div class="row-fluid header text-center"> + <div class="span12"> + <a class="article-user-link" href=""> + <img id="article-user-image" src=""/> + </a> + <div class="writer-name nolink-decoration"> + <a id="article-user-name" class="article-user-link" href=""></a> + </div> + </div> + </div> + <div class="title"> + <a href="" target="_blank"></a> + </div> + <div class="row-fluid sub-header"> + <div class="span12"> + <time class="timeago" datetime=""></time> + <span class="article-share"></span> + <span class="article-save"></span> + </div> + </div> + <span id="article-content"></span> + </div> + </div> + </div> + </div> + + <div data-lift="ignore" class="span2 offset0"> + <div id="reader-ad-nav" class="reader-span-spacing text-center"> + <h5>Functional Programming</h5> + <ul class="nav nolink-decoration"> + <li> + <img src="https://secure.gravatar.com/avatar/7d6356b8b56a9a71583787904a970daa?s=80&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" /> + <a href="/julio">Julio Enrique Cabrera</a> + </li> + <li> + <img src="https://secure.gravatar.com/avatar/51e9266cad7460b23daac1179a72da2b?s=80&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" /> + <a href="/">Nathan Hamblen</a> + </li> + <li> + <img src="https://secure.gravatar.com/avatar/375ca615cb17ebcb05c417f3433fea4a?s=80&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" /> + <a href="/">Tim Nelson</a> + </li> + </ul> + </div> + </div> + + + </div> + </div> + + + +</div> diff --git a/src/main/webapp/templates-hidden/parts/blog-profile.html b/src/main/webapp/templates-hidden/parts/blog-profile.html @@ -112,8 +112,8 @@ <div class="row-fluid sub-header"> <div class="span12"> <time class="timeago" datetime=""></time> - <a class="article-share" href="">Share</a> - <a class="article-save" href="">Save</a> + <span class="article-share"></span> + <span class="article-save"></span> </div> </div> <span id="article-content"></span> diff --git a/src/main/webapp/templates-hidden/parts/user-profile.html b/src/main/webapp/templates-hidden/parts/user-profile.html @@ -98,8 +98,8 @@ <div class="row-fluid sub-header"> <div class="span12"> <time class="timeago" datetime=""></time> - <a class="article-share" href="">Share</a> - <a class="article-save" href="">Save</a> + <span class="article-share"></span> + <span class="article-save"></span> </div> </div> <span id="article-content"></span>