diff --git a/Gemfile b/Gemfile index 895a45b..6885200 100644 --- a/Gemfile +++ b/Gemfile @@ -41,6 +41,7 @@ group 'test' do gem 'colorific', '~> 1.0.0' gem 'test_notifier', '~> 1.0.0' gem 'mocha' + gem 'database_cleaner' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index da23754..015f7fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -71,6 +71,7 @@ GEM chunky_png (~> 1.2) fssm (>= 0.2.7) sass (~> 3.1) + database_cleaner (0.7.1) erubis (2.7.0) exception_notification (2.5.2) actionmailer (>= 3.0.4) @@ -225,6 +226,7 @@ DEPENDENCIES coffee-rails (~> 3.1.1) colorific (~> 1.0.0) compass (~> 0.12.alpha) + database_cleaner draper! exception_notification factory_girl_rails diff --git a/app/assets/images/icons/unread.png b/app/assets/images/icons/unread.png new file mode 100644 index 0000000..dd50efd Binary files /dev/null and b/app/assets/images/icons/unread.png differ diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index b0bc693..ae07f88 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1,9 +1,8 @@ div.activity +container - &.unread div.activity_title - font-weight: bold div.activity_title +column(20, true) + +mark-as-read font-size: 1.5em font-family: $display-font margin-bottom: 1em diff --git a/app/assets/stylesheets/partials/_articles.sass b/app/assets/stylesheets/partials/_articles.sass index e90768b..543b7f1 100644 --- a/app/assets/stylesheets/partials/_articles.sass +++ b/app/assets/stylesheets/partials/_articles.sass @@ -2,6 +2,7 @@ article +container div.article_title +column(20) + +mark-as-read font-size: 1.5em font-family: $display-font margin-bottom: 1em @@ -44,12 +45,10 @@ article &.private .article_title padding-left: 30px background: asset-url('icons/butterfly.png', image) 0 0 no-repeat - &.unread .article_title - font-weight: bold #articles article.highlight border: 1px dashed #AE9331 padding: 10px margin: 0 -5px +border-radius(4px) - background-color: #FAFAE2 \ No newline at end of file + background-color: #FAFAE2 diff --git a/app/assets/stylesheets/partials/_mixins.sass b/app/assets/stylesheets/partials/_mixins.sass index a0ecb03..34b0529 100644 --- a/app/assets/stylesheets/partials/_mixins.sass +++ b/app/assets/stylesheets/partials/_mixins.sass @@ -15,4 +15,14 @@ +single-box-shadow(#000, 0, 0, 0) display: inline margin: 0 - background-color: inherit \ No newline at end of file + background-color: inherit + +=mark-as-read + a.mark-as-read + margin-right: 15px + width: 17px + height: 17px + background: image_url('icons/unread.png') 0 0 no-repeat + text-indent: -9999px + display: inline-block + diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index fe55c14..229d5d2 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -2,8 +2,8 @@ class ActivitiesController < ApplicationController before_filter :user_required before_filter :find_activity, :only => [ :show, :edit, :update, :destroy, :register, :archive, :restore, - :create_discussion_list ] - before_filter :mark_as_read, :only => [ :show ] + :create_discussion_list, :read ] + before_filter :mark_as_read, :only => [ :show, :read ] before_filter :authorized_users_only, :only => [ :edit, :update, :destroy, :archive, :restore, :create_discussion_list ] @@ -27,7 +27,6 @@ def show @user = UserDecorator.decorate(current_user) end - def new @activity = Activity.new(:registration_open => true) end @@ -96,6 +95,11 @@ def create_discussion_list end end + def read + @resource = @activity + render "shared/mark_as_read" + end + private def find_activity diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index 9aaaa4a..69c1783 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -1,9 +1,9 @@ class ArticlesController < ApplicationController respond_to :html - before_filter :user_required, :only => [:new, :create, :edit, :update, :destroy] - before_filter :find_article, :only => [:show, :edit, :update, :destroy] - before_filter :mark_as_read, :only => [:show] + before_filter :user_required, :only => [:new, :create, :edit, :update, :destroy, :read] + before_filter :find_article, :only => [:show, :edit, :update, :destroy, :read] + before_filter :mark_as_read, :only => [:show, :read] before_filter :authorized_users_only, :only => [:edit, :update, :destroy] before_filter :profile_required, :only => [:new, :create] @@ -57,6 +57,11 @@ def destroy redirect_to(articles_path, :notice => 'Article was successfully destroyed.') end + def read + @resource = @article + render "shared/mark_as_read" + end + private def authorized_users_only diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index f9a3a11..ab9b0d2 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -69,6 +69,15 @@ def participants_link h.content_tag(:div, link || "", :class => "participants").html_safe end + def mark_as_read_link + h.link_to "Mark as read", h.read_activity_path(activity), + :method => :post, + :class => 'mark-as-read', + :rel => 'twipsy', + :title => "Mark as read", + :remote => true unless activity.read_by?(h.current_user) + end + def css_class css_class = [] css_class << 'unread' unless activity.read_by?(h.current_user) diff --git a/app/decorators/article_decorator.rb b/app/decorators/article_decorator.rb index e0b7522..f843f1f 100644 --- a/app/decorators/article_decorator.rb +++ b/app/decorators/article_decorator.rb @@ -29,6 +29,15 @@ def short_url RestClient.post("http://is.gd/create.php", :format => "simple", :url => url) end + def mark_as_read_link + h.link_to "Mark as read", h.read_article_path(article), + :method => :post, + :class => 'mark-as-read', + :rel => 'twipsy', + :title => "Mark as read", + :remote => true unless article.read_by?(h.current_user) + end + def css css_classes = [] diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index 004402f..a258812 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -1,6 +1,7 @@ = content_tag_for(:div, activity, :class => activity.css_class) do .activity_title - if @activities + = activity.mark_as_read_link = link_to activity.title, activity_path(activity) - else = activity.title diff --git a/app/views/articles/_article.html.haml b/app/views/articles/_article.html.haml index 8d89209..7125a5d 100644 --- a/app/views/articles/_article.html.haml +++ b/app/views/articles/_article.html.haml @@ -1,6 +1,7 @@ = content_tag_for :article, article, class: article.css do .article_title - if @articles + = article.mark_as_read_link = link_to article.title, article_path(article) - else = article.title diff --git a/app/views/shared/_unread_count.html.haml b/app/views/shared/_unread_count.html.haml index 78d073a..18d8a72 100644 --- a/app/views/shared/_unread_count.html.haml +++ b/app/views/shared/_unread_count.html.haml @@ -1,3 +1,3 @@ - if count > 0 %span.unread_count{rel: 'twipsy', title: "Unread #{type}", - data: { placement: 'below' }}= count \ No newline at end of file + data: { type: type, placement: 'below' }}= count diff --git a/app/views/shared/mark_as_read.js.erb b/app/views/shared/mark_as_read.js.erb new file mode 100644 index 0000000..6155ad9 --- /dev/null +++ b/app/views/shared/mark_as_read.js.erb @@ -0,0 +1,7 @@ +var unreadCount = $('.unread_count[data-type=<%= params[:controller] %>]'); +unreadCount.text(parseInt(unreadCount.text()) - 1); +if( unreadCount.text() === '0' ) { + unreadCount.remove(); +} + +$('#<%= dom_id(@resource) %> .mark-as-read').remove(); diff --git a/config/routes.rb b/config/routes.rb index fbaf31f..dcdf98a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,13 +8,17 @@ match '/read_all/:type' => 'people#read_all', as: 'read_all' resources :people - resources :articles + resources :articles do + post :read, :on => :member + end + resources :activities do member do post :register post :archive post :restore post :create_discussion_list + post :read end collection do get :archived diff --git a/test/integration/articles_test.rb b/test/integration/articles_test.rb index c7dfca2..39b6cab 100644 --- a/test/integration/articles_test.rb +++ b/test/integration/articles_test.rb @@ -46,15 +46,4 @@ def setup visit article_path(@article) assert_content("Restricted Unicorns!") end - - test "unicorns see an unread article" do - visit articles_path - assert_no_content("1 Updates") - - sign_user_in(@user) - assert_content("1 Updates") - - visit article_path(@article) - assert_no_content("1 Updates") - end end diff --git a/test/integration/unread_items_test.rb b/test/integration/unread_items_test.rb new file mode 100644 index 0000000..e909d15 --- /dev/null +++ b/test/integration/unread_items_test.rb @@ -0,0 +1,48 @@ +require 'test_helper' + +class UnreadItemsTest < ActionDispatch::IntegrationTest + def setup + @user = FactoryGirl.create(:user) + @article = FactoryGirl.create(:article, title: "Unicorns!", author: @user) + end + + test "unicorns see an unread article" do + visit articles_path + assert_no_content("1 Updates") + + sign_user_in(@user) + assert_content("1 Updates") + + visit article_path(@article) + assert_no_content("1 Updates") + end + + test "unicorns can mark all articles as read" do + 2.times { FactoryGirl.create(:article, title: "Title of Unicorns!", author: @user) } + + visit articles_path + assert_no_content("3 Updates") + + sign_user_in(@user) + assert_content("3 Updates") + + click_on "Mark all as read" + assert_no_content("3 Updates") + end + + test "unicorns can mark as read on an article via clicking on unread indicator" do + Capybara.current_driver = Capybara.javascript_driver + + sign_user_in(@user) + + assert_content("1 Updates") + + within("#article_#{@article.id}") do + click_link "Mark as read" + + assert_no_link("Mark as read") + end + + assert_no_content("1 Updates") + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 342091c..421ae1e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -10,12 +10,25 @@ TestNotifier.silence_no_notifier_warning = true OmniAuth.config.test_mode = true +DatabaseCleaner.clean_with :truncation +DatabaseCleaner.strategy = :truncation + class ActionDispatch::IntegrationTest include Capybara::DSL include Support::Integration include Support::Auth - setup { mock_uniweb_user({}) } - teardown { Capybara.reset_sessions! } + self.use_transactional_fixtures = false + + setup do + DatabaseCleaner.start + mock_uniweb_user({}) + end + + teardown do + DatabaseCleaner.clean + Capybara.reset_sessions! + Capybara.use_default_driver + end end