mostrar notificaciones a los usuarios cuando alguien comenta en su
debate o responde a su comentario
This commit is contained in:
@@ -116,4 +116,14 @@ class ApplicationController < ActionController::Base
|
|||||||
add_notifications_for activity
|
add_notifications_for activity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_notifications_for(activity)
|
||||||
|
case activity.action
|
||||||
|
when "debate_comment"
|
||||||
|
author = activity.trackable.debate.author
|
||||||
|
when "comment_reply"
|
||||||
|
author = activity.trackable.parent.author
|
||||||
|
end
|
||||||
|
author.notifications.create!(activity: activity) unless activity.made_by? author
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
9
app/controllers/notifications_controller.rb
Normal file
9
app/controllers/notifications_controller.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class NotificationsController < ApplicationController
|
||||||
|
before_action :authenticate_user!
|
||||||
|
load_and_authorize_resource class: "User"
|
||||||
|
|
||||||
|
def index
|
||||||
|
@notifications = current_user.notifications.unread.recent.for_render
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
16
app/helpers/notifications_helper.rb
Normal file
16
app/helpers/notifications_helper.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module NotificationsHelper
|
||||||
|
|
||||||
|
def notification_text_for(notification)
|
||||||
|
case notification.activity.action
|
||||||
|
when "debate_comment"
|
||||||
|
t("comments.notifications.commented_on_your_debate")
|
||||||
|
when "comment_reply"
|
||||||
|
t("comments.notifications.replied_to_your_comment")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notifications_class_for(user)
|
||||||
|
user.notifications.unread.count > 0 ? "with_notifications" : "without_notifications"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
12
app/models/notification.rb
Normal file
12
app/models/notification.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class Notification < ActiveRecord::Base
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :activity
|
||||||
|
|
||||||
|
scope :unread, -> { where(read: false) }
|
||||||
|
scope :recent, -> { order(id: :desc) }
|
||||||
|
scope :for_render, -> { includes(activity: [:user, :trackable]) }
|
||||||
|
|
||||||
|
def timestamp
|
||||||
|
activity.trackable.created_at
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -22,6 +22,7 @@ class User < ActiveRecord::Base
|
|||||||
has_many :proposals, -> { with_hidden }, foreign_key: :author_id
|
has_many :proposals, -> { with_hidden }, foreign_key: :author_id
|
||||||
has_many :comments, -> { with_hidden }
|
has_many :comments, -> { with_hidden }
|
||||||
has_many :failed_census_calls
|
has_many :failed_census_calls
|
||||||
|
has_many :notifications
|
||||||
|
|
||||||
validates :username, presence: true, if: :username_required?
|
validates :username, presence: true, if: :username_required?
|
||||||
validates :username, uniqueness: true, if: :username_required?
|
validates :username, uniqueness: true, if: :username_required?
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<%= link_to(t("devise_views.menu.login_items.logout"), destroy_user_session_path, method: :delete) %>
|
<%= link_to(t("devise_views.menu.login_items.logout"), destroy_user_session_path, method: :delete) %>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to 'Notificaciones', notifications_path, class: notifications_class_for(current_user) %>
|
||||||
|
</li>
|
||||||
<% else %>
|
<% else %>
|
||||||
<li>
|
<li>
|
||||||
<%= link_to(t("devise_views.menu.login_items.login"), new_user_session_path) %>
|
<%= link_to(t("devise_views.menu.login_items.login"), new_user_session_path) %>
|
||||||
|
|||||||
11
app/views/notifications/index.html.erb
Normal file
11
app/views/notifications/index.html.erb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div class="row">
|
||||||
|
<ul>
|
||||||
|
<% @notifications.each do |notification| %>
|
||||||
|
<li>
|
||||||
|
<time><%= l notification.timestamp.to_datetime, format: :datetime %></time> •
|
||||||
|
<%= notification.activity.username %> <%= notification_text_for(notification) %>
|
||||||
|
<%= link_to notification.activity.trackable.debate.title, notification.activity.trackable.debate %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -267,6 +267,9 @@ en:
|
|||||||
zero: "No votes"
|
zero: "No votes"
|
||||||
one: "1 vote"
|
one: "1 vote"
|
||||||
other: "%{count} votes"
|
other: "%{count} votes"
|
||||||
|
notifications:
|
||||||
|
commented_on_your_debate: "commented on yout debate"
|
||||||
|
replied_to_your_comment: "replied to your comment on"
|
||||||
comments_helper:
|
comments_helper:
|
||||||
comment_link: "Comment"
|
comment_link: "Comment"
|
||||||
comment_button: "Publish comment"
|
comment_button: "Publish comment"
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ es:
|
|||||||
zero: "Sin votos"
|
zero: "Sin votos"
|
||||||
one: "1 voto"
|
one: "1 voto"
|
||||||
other: "%{count} votos"
|
other: "%{count} votos"
|
||||||
|
notifications:
|
||||||
|
commented_on_your_debate: "ha comentado en tu debate"
|
||||||
|
replied_to_your_comment: "ha respondido a tu comentario en"
|
||||||
comments_helper:
|
comments_helper:
|
||||||
comment_link: "Comentar"
|
comment_link: "Comentar"
|
||||||
comment_button: "Publicar comentario"
|
comment_button: "Publicar comentario"
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ Rails.application.routes.draw do
|
|||||||
resource :account, controller: "account", only: [:show, :update, :delete] do
|
resource :account, controller: "account", only: [:show, :update, :delete] do
|
||||||
collection { get :erase }
|
collection { get :erase }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :notifications, only: :index
|
||||||
|
|
||||||
resource :verification, controller: "verification", only: [:show]
|
resource :verification, controller: "verification", only: [:show]
|
||||||
|
|
||||||
scope module: :verification do
|
scope module: :verification do
|
||||||
|
|||||||
9
db/migrate/20150928115005_create_notifications.rb
Normal file
9
db/migrate/20150928115005_create_notifications.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class CreateNotifications < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :notifications do |t|
|
||||||
|
t.belongs_to :user, index: true, foreign_key: true
|
||||||
|
t.belongs_to :activity, index: true, foreign_key: true
|
||||||
|
t.boolean :read, default: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
13
db/schema.rb
13
db/schema.rb
@@ -204,6 +204,17 @@ ActiveRecord::Schema.define(version: 20151215165824) do
|
|||||||
|
|
||||||
add_index "moderators", ["user_id"], name: "index_moderators_on_user_id", using: :btree
|
add_index "moderators", ["user_id"], name: "index_moderators_on_user_id", using: :btree
|
||||||
|
|
||||||
|
create_table "notifications", force: :cascade do |t|
|
||||||
|
t.integer "user_id"
|
||||||
|
t.integer "activity_id"
|
||||||
|
t.boolean "read", default: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "notifications", ["activity_id"], name: "index_notifications_on_activity_id", using: :btree
|
||||||
|
add_index "notifications", ["user_id"], name: "index_notifications_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "organizations", force: :cascade do |t|
|
create_table "organizations", force: :cascade do |t|
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.string "name", limit: 60
|
t.string "name", limit: 60
|
||||||
@@ -405,5 +416,7 @@ ActiveRecord::Schema.define(version: 20151215165824) do
|
|||||||
add_foreign_key "identities", "users"
|
add_foreign_key "identities", "users"
|
||||||
add_foreign_key "locks", "users"
|
add_foreign_key "locks", "users"
|
||||||
add_foreign_key "moderators", "users"
|
add_foreign_key "moderators", "users"
|
||||||
|
add_foreign_key "notifications", "activities"
|
||||||
|
add_foreign_key "notifications", "users"
|
||||||
add_foreign_key "organizations", "users"
|
add_foreign_key "organizations", "users"
|
||||||
end
|
end
|
||||||
|
|||||||
16
spec/controllers/notifications_controller_spec.rb
Normal file
16
spec/controllers/notifications_controller_spec.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe NotificationsController do
|
||||||
|
|
||||||
|
describe "#index" do
|
||||||
|
let(:user) { create :user }
|
||||||
|
let(:notification) { create :notification, user: user }
|
||||||
|
|
||||||
|
it "assigns @notifications" do
|
||||||
|
sign_in user
|
||||||
|
|
||||||
|
get :index, debate: { title: 'A sample debate', description: 'this is a sample debate', terms_of_service: 1 }
|
||||||
|
expect(assigns(:notifications)).to eq user.notifications.unread.recent.for_render
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -290,4 +290,10 @@ FactoryGirl.define do
|
|||||||
sequence(:track_id) { |n| "#{n}" }
|
sequence(:track_id) { |n| "#{n}" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :notification do
|
||||||
|
association :user, factory: :user
|
||||||
|
association :activity, factory: :activity
|
||||||
|
read false
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
97
spec/features/notifications_spec.rb
Normal file
97
spec/features/notifications_spec.rb
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
feature "Notifications" do
|
||||||
|
let(:author) { create :user }
|
||||||
|
let(:user) { create :user }
|
||||||
|
let(:debate) { create :debate, author: author }
|
||||||
|
|
||||||
|
scenario "User commented on my debate", :js do
|
||||||
|
login_as user
|
||||||
|
visit debate_path debate
|
||||||
|
|
||||||
|
fill_in "comment-body-debate_#{debate.id}", with: "I commented on your debate"
|
||||||
|
click_button "Publish comment"
|
||||||
|
within "#comments" do
|
||||||
|
expect(page).to have_content "I commented on your debate"
|
||||||
|
end
|
||||||
|
|
||||||
|
logout
|
||||||
|
login_as author
|
||||||
|
visit root_path
|
||||||
|
expect(page).to have_xpath "//a[@class='with_notifications' and @href='#{notifications_path}' and text()='Notificaciones']"
|
||||||
|
|
||||||
|
click_link "Notificaciones"
|
||||||
|
expect(page).to have_content user.username
|
||||||
|
expect(page).to have_content I18n.t("comments.notifications.commented_on_your_debate")
|
||||||
|
expect(page).to_not have_content I18n.t("comments.notifications.replied_to_your_comment")
|
||||||
|
expect(page).to have_link debate.title, href: debate_path(debate)
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "User replied to my comment", :js do
|
||||||
|
comment = create :comment, commentable: debate, user: author
|
||||||
|
login_as user
|
||||||
|
visit debate_path debate
|
||||||
|
|
||||||
|
click_link "Reply"
|
||||||
|
within "#js-comment-form-comment_#{comment.id}" do
|
||||||
|
fill_in "comment-body-comment_#{comment.id}", with: "I replied to your comment"
|
||||||
|
click_button "Publish reply"
|
||||||
|
end
|
||||||
|
|
||||||
|
within "#comment_#{comment.id}" do
|
||||||
|
expect(page).to have_content "I replied to your comment"
|
||||||
|
end
|
||||||
|
|
||||||
|
logout
|
||||||
|
login_as author
|
||||||
|
visit root_path
|
||||||
|
expect(page).to have_xpath "//a[@class='with_notifications' and @href='#{notifications_path}' and text()='Notificaciones']"
|
||||||
|
|
||||||
|
visit notifications_path
|
||||||
|
expect(page).to have_content user.username
|
||||||
|
expect(page).to have_content I18n.t("comments.notifications.replied_to_your_comment")
|
||||||
|
expect(page).to_not have_content I18n.t("comments.notifications.commented_on_your_debate")
|
||||||
|
expect(page).to have_link debate.title, href: debate_path(debate)
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Author commented on his own debate", :js do
|
||||||
|
login_as author
|
||||||
|
visit debate_path debate
|
||||||
|
|
||||||
|
fill_in "comment-body-debate_#{debate.id}", with: "I commented on my own debate"
|
||||||
|
click_button "Publish comment"
|
||||||
|
within "#comments" do
|
||||||
|
expect(page).to have_content "I commented on my own debate"
|
||||||
|
end
|
||||||
|
expect(page).to have_xpath "//a[@class='without_notifications' and @href='#{notifications_path}' and text()='Notificaciones']"
|
||||||
|
|
||||||
|
click_link "Notificaciones"
|
||||||
|
expect(page).to_not have_content user.username
|
||||||
|
expect(page).to_not have_content I18n.t("comments.notifications.commented_on_your_debate")
|
||||||
|
expect(page).to_not have_content I18n.t("comments.notifications.replied_to_your_comment")
|
||||||
|
expect(page).to_not have_link debate.title, href: debate_path(debate)
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario "Author replied to his own comment", :js do
|
||||||
|
comment = create :comment, commentable: debate, user: author
|
||||||
|
login_as author
|
||||||
|
visit debate_path debate
|
||||||
|
|
||||||
|
click_link "Reply"
|
||||||
|
within "#js-comment-form-comment_#{comment.id}" do
|
||||||
|
fill_in "comment-body-comment_#{comment.id}", with: "I replied to my own comment"
|
||||||
|
click_button "Publish reply"
|
||||||
|
end
|
||||||
|
|
||||||
|
within "#comment_#{comment.id}" do
|
||||||
|
expect(page).to have_content "I replied to my own comment"
|
||||||
|
end
|
||||||
|
expect(page).to have_xpath "//a[@class='without_notifications' and @href='#{notifications_path}' and text()='Notificaciones']"
|
||||||
|
|
||||||
|
visit notifications_path
|
||||||
|
expect(page).to_not have_content user.username
|
||||||
|
expect(page).to_not have_content I18n.t("comments.notifications.replied_to_your_comment")
|
||||||
|
expect(page).to_not have_content I18n.t("comments.notifications.commented_on_your_debate")
|
||||||
|
expect(page).to_not have_link debate.title, href: debate_path(debate)
|
||||||
|
end
|
||||||
|
end
|
||||||
48
spec/helpers/notifications_helper_spec.rb
Normal file
48
spec/helpers/notifications_helper_spec.rb
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe NotificationsHelper do
|
||||||
|
|
||||||
|
describe "#notification_text_for" do
|
||||||
|
let(:comment_activity) { create :activity, action: "debate_comment" }
|
||||||
|
let(:reply_activity) { create :activity, action: "comment_reply" }
|
||||||
|
|
||||||
|
context "when action was comment on a debate" do
|
||||||
|
it "returns 'commented_on_your_debate' locale text" do
|
||||||
|
notification = create :notification, activity: comment_activity
|
||||||
|
expect(notification_text_for(notification)).to eq t("comments.notifications.commented_on_your_debate")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when action was comment on a debate" do
|
||||||
|
it "returns 'replied_to_your_comment' locale text" do
|
||||||
|
notification = create :notification, activity: reply_activity
|
||||||
|
expect(notification_text_for(notification)).to eq t("comments.notifications.replied_to_your_comment")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#notifications_class_for" do
|
||||||
|
let(:user) { create :user }
|
||||||
|
|
||||||
|
context "when user doesn't have any notification" do
|
||||||
|
it "returns class 'without_notifications'" do
|
||||||
|
expect(notifications_class_for(user)).to eq "without_notifications"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when user doesn't have unread notifications" do
|
||||||
|
it "returns class 'without_notifications'" do
|
||||||
|
notification = create :notification, user: user, read: true
|
||||||
|
expect(notifications_class_for(user)).to eq "without_notifications"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when user has unread notifications" do
|
||||||
|
it "returns class 'with_notifications'" do
|
||||||
|
notification = create :notification, user: user
|
||||||
|
expect(notifications_class_for(user)).to eq "with_notifications"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
44
spec/models/notification_spec.rb
Normal file
44
spec/models/notification_spec.rb
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Notification do
|
||||||
|
|
||||||
|
describe "#unread (scope)" do
|
||||||
|
it "returns only unread notifications" do
|
||||||
|
unread_notification = create :notification
|
||||||
|
read_notification = create :notification, read: true
|
||||||
|
|
||||||
|
unread_notifications = Notification.unread
|
||||||
|
expect(unread_notifications.size).to be 1
|
||||||
|
expect(unread_notifications.first).to eq unread_notification
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#recent (scope)" do
|
||||||
|
it "returns notifications sorted by id descendant" do
|
||||||
|
old_notification = create :notification
|
||||||
|
new_notification = create :notification
|
||||||
|
|
||||||
|
sorted_notifications = Notification.recent
|
||||||
|
expect(sorted_notifications.size).to be 2
|
||||||
|
expect(sorted_notifications.first).to eq new_notification
|
||||||
|
expect(sorted_notifications.last).to eq old_notification
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#for_render (scope)" do
|
||||||
|
it "returns notifications with including activity, user and trackable info" do
|
||||||
|
expect(Notification).to receive(:includes).with(activity: [:user, :trackable]).exactly(:once)
|
||||||
|
Notification.for_render
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#timestamp" do
|
||||||
|
it "returns the timestamp of the trackable object" do
|
||||||
|
comment = create :comment
|
||||||
|
activity = create :activity, trackable: comment
|
||||||
|
notification = create :notification, activity: activity
|
||||||
|
|
||||||
|
expect(notification.timestamp).to eq comment.created_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user