diff --git a/Gemfile.lock b/Gemfile.lock index 4cd0586f2..f1fec246c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,7 +78,7 @@ GEM multi_json (1.7.3) nokogiri (1.5.9) pg (0.15.1) - pry (0.9.12.1) + pry (0.9.12.2) coderay (~> 1.0.5) method_source (~> 0.8) slop (~> 3.4) @@ -128,7 +128,7 @@ GEM tilt (>= 1.3.0) sinatra-flash (0.3.0) sinatra (>= 1.0.0) - slop (3.4.4) + slop (3.4.5) sqlite3 (1.3.7) thor (0.18.1) thread (0.0.8) diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb index e7705c047..6cd49c6bc 100644 --- a/app/controllers/stories_controller.rb +++ b/app/controllers/stories_controller.rb @@ -14,13 +14,20 @@ class Stringer < Sinatra::Base erb :archive end + get "/starred" do + @starred_stories = StoryRepository.starred(params[:page]) + + erb :starred + end + put "/stories/:id" do json_params = JSON.parse(request.body.read, symbolize_names: true) story = StoryRepository.fetch(params[:id]) story.is_read = !!json_params[:is_read] story.keep_unread = !!json_params[:keep_unread] - + story.is_starred = !!json_params[:is_starred] + StoryRepository.save(story) end @@ -29,4 +36,4 @@ class Stringer < Sinatra::Base redirect to("/news") end -end \ No newline at end of file +end diff --git a/app/public/css/styles.css b/app/public/css/styles.css index fe9d4a125..40735f551 100644 --- a/app/public/css/styles.css +++ b/app/public/css/styles.css @@ -179,6 +179,25 @@ li.story.open .story-preview { margin-right: 25px; } +.story-starred-box { + display: inline-block; + float: left; + padding-left: 4px; + width: 20%; +} + +.story-starred { + display: inline-block; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + margin-right: 25px; +} + .story-body { margin-left: 20px; margin-right: 20px; diff --git a/app/public/js/app.js b/app/public/js/app.js index 7fa5949ed..a33a5788d 100644 --- a/app/public/js/app.js +++ b/app/public/js/app.js @@ -47,6 +47,16 @@ var Story = Backbone.Model.extend({ if (this.shouldSave()) this.save(); }, + toggleStarred: function() { + if (this.get("is_starred")) { + this.set("is_starred", false); + } else { + this.set("is_starred", true); + } + + if (this.shouldSave()) this.save(); + }, + close: function() { this.set("open", false); }, @@ -73,7 +83,8 @@ var StoryView = Backbone.View.extend({ events: { "click .story-preview" : "storyClicked", - "click .story-keep-unread" : "toggleKeepUnread" + "click .story-keep-unread" : "toggleKeepUnread", + "click .story-starred" : "toggleStarred" }, initialize: function() { @@ -83,6 +94,7 @@ var StoryView = Backbone.View.extend({ this.listenTo(this.model, 'change:open', this.itemOpened); this.listenTo(this.model, 'change:is_read', this.itemRead); this.listenTo(this.model, 'change:keep_unread', this.itemKeepUnread); + this.listenTo(this.model, 'change:is_starred', this.itemStarred); }, render: function() { @@ -110,11 +122,16 @@ var StoryView = Backbone.View.extend({ if (!this.$el.visible()) window.scrollTo(0, this.$el.offset().top); }, - itemKeepUnread: function(){ + itemKeepUnread: function() { var icon = this.model.get("keep_unread") ? "icon-check" : "icon-check-empty"; this.$(".story-keep-unread > i").attr("class", icon); }, + itemStarred: function() { + var icon = this.model.get("is_starred") ? "icon-star" : "icon-star-empty"; + this.$(".story-starred > i").attr("class", icon); + }, + storyClicked: function() { this.model.toggle(); window.scrollTo(0, this.$el.offset().top); @@ -122,6 +139,11 @@ var StoryView = Backbone.View.extend({ toggleKeepUnread: function() { this.model.toggleKeepUnread(); + }, + + toggleStarred: function(e) { + e.stopPropagation(); + this.model.toggleStarred(); } }); @@ -203,6 +225,11 @@ var StoryList = Backbone.Collection.extend({ toggleCurrentKeepUnread: function() { if (this.cursorPosition < 0) this.cursorPosition = 0; this.at(this.cursorPosition).toggleKeepUnread(); + }, + + toggleCurrentStarred: function() { + if (this.cursorPosition < 0) this.cursorPosition = 0; + this.at(this.cursorPosition).toggleStarred(); } }); @@ -263,6 +290,10 @@ var AppView = Backbone.View.extend({ toggleCurrentKeepUnread: function() { this.stories.toggleCurrentKeepUnread(); + }, + + toggleCurrentStarred: function() { + this.stories.toggleCurrentStarred(); } }); @@ -287,4 +318,4 @@ $(document).ready(function() { Mousetrap.bind("?", function() { $("#shortcuts").modal('toggle'); }); -}); \ No newline at end of file +}); diff --git a/app/repositories/story_repository.rb b/app/repositories/story_repository.rb index 552ca2ae9..b8e20767a 100644 --- a/app/repositories/story_repository.rb +++ b/app/repositories/story_repository.rb @@ -8,6 +8,7 @@ def self.add(entry, feed) permalink: entry.url, body: extract_content(entry), is_read: false, + is_starred: false, published: entry.published || Time.now) end @@ -31,6 +32,11 @@ def self.read(page = 1) Story.where(is_read: true).order("published desc").page(page).per_page(15) end + def self.starred(page = 1) + Story.where(is_starred: true) + .order("published desc").page(page).per_page(15) + end + def self.read_count Story.where(is_read: true).count end diff --git a/app/views/js/stories.js.erb b/app/views/js/stories.js.erb index 866d4c547..f937cce8a 100644 --- a/app/views/js/stories.js.erb +++ b/app/views/js/stories.js.erb @@ -35,5 +35,9 @@ Mousetrap.bind(["m"], function() { StoryApp.toggleCurrentKeepUnread(); }); + + Mousetrap.bind(["s"], function() { + StoryApp.toggleCurrentStarred(); + }); }); - \ No newline at end of file + diff --git a/app/views/js/templates/_story.js.erb b/app/views/js/templates/_story.js.erb index 56460f982..60d6c482d 100644 --- a/app/views/js/templates/_story.js.erb +++ b/app/views/js/templates/_story.js.erb @@ -1,11 +1,16 @@ \ No newline at end of file + diff --git a/app/views/partials/_action_bar.erb b/app/views/partials/_action_bar.erb index 326fdf89d..800cf2ef5 100644 --- a/app/views/partials/_action_bar.erb +++ b/app/views/partials/_action_bar.erb @@ -11,6 +11,9 @@
<%= t('starred.sorry') %>
+