Merge pull request #1130 from Retrospring/feature/mark-all-notifications-as-read

Mark all notifications as read
This commit is contained in:
Karina Kwiatek 2023-05-07 10:07:11 +02:00 committed by GitHub
commit 7a9a7e0d47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 48 deletions

View File

@ -25,6 +25,16 @@ class NotificationsController < ApplicationController
end end
end end
def read
current_user.notifications.where(new: true).update_all(new: false) # rubocop:disable Rails/SkipsModelValidations
respond_to do |format|
format.turbo_stream do
render "navigation/notifications", locals: { notifications: [], notification_count: nil }
end
end
end
private private
def paginate_notifications def paginate_notifications

View File

@ -19,16 +19,19 @@
%a.nav-link{ href: '#', data: { bs_target: '#modal-list-memberships', bs_toggle: :modal } } %a.nav-link{ href: '#', data: { bs_target: '#modal-list-memberships', bs_toggle: :modal } }
%i.fa.fa-list.hidden-xs %i.fa.fa-list.hidden-xs
%span.d-none.d-sm-inline.d-md-none= t(".list") %span.d-none.d-sm-inline.d-md-none= t(".list")
= nav_entry t("navigation.notifications"), notifications_path, badge: notification_count, class: "d-block d-sm-none", hotkey: "g n" = nav_entry t("navigation.notifications"), notifications_path, class: "d-block d-sm-none", hotkey: "g n"
%li.nav-item.dropdown.d-none.d-sm-block %li.nav-item.dropdown.d-none.d-sm-block
%a.nav-link.dropdown-toggle{ href: '#', data: { bs_toggle: :dropdown } } %a.nav-link.dropdown-toggle{ href: '#', data: { bs_toggle: :dropdown } }
- if notification_count.nil? %turbo-frame#notification-desktop-icon
%i.fa.fa-bell-o - if notification_count.nil?
- else %i.fa.fa-bell-o
%i.fa.fa-bell - else
%span.visually-hidden= t("navigation.notifications") %i.fa.fa-bell
%span.badge= notification_count %span.visually-hidden= t("navigation.notifications")
= render 'navigation/dropdown/notifications', notifications: notifications, size: "desktop" %span.badge= notification_count
.dropdown-menu.dropdown-menu-end.notification-dropdown
%turbo-frame#notifications-dropdown-list
= render "navigation/dropdown/notifications", notifications:, notification_count: nil
%li.nav-item.d-none.d-sm-block{ data: { bs_toggle: 'tooltip', bs_placement: 'bottom' }, title: t('.ask_question') } %li.nav-item.d-none.d-sm-block{ data: { bs_toggle: 'tooltip', bs_placement: 'bottom' }, title: t('.ask_question') }
%a.nav-link{ href: "#", name: "toggle-all-ask", data: { bs_target: "#modal-ask-followers", bs_toggle: :modal, hotkey: "n" } } %a.nav-link{ href: "#", name: "toggle-all-ask", data: { bs_target: "#modal-ask-followers", bs_toggle: :modal, hotkey: "n" } }
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o

View File

@ -9,7 +9,7 @@
- if APP_CONFIG.dig(:features, :discover, :enabled) || current_user.mod? - if APP_CONFIG.dig(:features, :discover, :enabled) || current_user.mod?
= nav_entry t("navigation.discover"), discover_path, icon: 'compass', icon_only: true = nav_entry t("navigation.discover"), discover_path, icon: 'compass', icon_only: true
= nav_entry t("navigation.notifications"), notifications_path("all"), icon: notifications_icon, = nav_entry t("navigation.notifications"), notifications_path("all"), icon: notifications_icon,
badge: notification_count, badge_color: "primary", icon_only: true badge: notification_count, badge_color: "primary", badge_attr: { id: "notification-mobile-count" }, icon_only: true
%li.nav-item.profile--image-dropdown %li.nav-item.profile--image-dropdown
%a.nav-link{ href: '#', data: { bs_toggle: 'dropdown', bs_target: '#rs-mobile-nav-profile' }, aria: { controls: 'rs-mobile-nav-profile', expanded: 'false' } } %a.nav-link{ href: '#', data: { bs_toggle: 'dropdown', bs_target: '#rs-mobile-nav-profile' }, aria: { controls: 'rs-mobile-nav-profile', expanded: 'false' } }
%img.avatar-md.d-inline{ src: current_user.profile_picture.url(:small) } %img.avatar-md.d-inline{ src: current_user.profile_picture.url(:small) }

View File

@ -1,16 +1,18 @@
.dropdown-menu.dropdown-menu-end.notification-dropdown{ id: "rs-#{size}-nav-notifications" } - if notifications.count.zero?
- if notifications.count.zero? %a.dropdown-item.text-center{ href: notifications_path('all'), data: { turbo_frame: "_top" } }
%a.dropdown-item.text-center{ href: notifications_path('all') } %i.fa.fa-fw.fa-chevron-right
%i.fa.fa-fw.fa-chevron-right = t(".all")
= t(".all") .dropdown-item.text-center.p-2
.dropdown-item.text-center.p-2 %i.fa.fa-bell-o.notification__bell-icon
%i.fa.fa-bell-o.notification__bell-icon %p= t(".none")
%p= t(".none") - else
- else %a.dropdown-item.text-center{ href: notifications_path, data: { turbo_frame: "_top" } }
%a.dropdown-item.text-center{ href: notifications_path } %i.fa.fa-fw.fa-chevron-right
%i.fa.fa-fw.fa-chevron-right = t(".new")
= t(".new") = link_to notifications_read_path, class: "dropdown-item text-center", data: { turbo_stream: true, turbo_method: :post } do
- notifications.each do |notification| %i.fa.fa-fw.fa-check-double
.dropdown-item = t(".mark_as_read")
= render "notifications/type/#{notification.target.class.name.downcase.split('::').last}", notification: notification - notifications.each do |notification|
.dropdown-item
= render "notifications/type/#{notification.target.class.name.downcase.split('::').last}", notification:

View File

@ -0,0 +1,13 @@
= turbo_stream.update "notifications-dropdown-list" do
= render "navigation/dropdown/notifications", notifications:
= turbo_stream.update "notification-desktop-icon" do
- if notification_count.nil?
%i.fa.fa-bell-o
- else
%i.fa.fa-bell
%span.visually-hidden= t("navigation.notifications")
%span.badge= notification_count
= turbo_stream.update "notification-mobile-count" do
%span.badge.badge-primary= notification_count

View File

@ -318,6 +318,7 @@ en:
none: :notifications.index.none none: :notifications.index.none
all: "Show all notifications" all: "Show all notifications"
new: "Show all new notifications" new: "Show all new notifications"
mark_as_read: "Mark all as read"
profile: profile:
profile: "Show profile" profile: "Show profile"
settings: "Settings" settings: "Settings"

View File

@ -143,6 +143,7 @@ Rails.application.routes.draw do
get "/list/:list_name", to: "timeline#list", as: :list_timeline get "/list/:list_name", to: "timeline#list", as: :list_timeline
get "/notifications(/:type)", to: "notifications#index", as: :notifications, defaults: { type: "new" } get "/notifications(/:type)", to: "notifications#index", as: :notifications, defaults: { type: "new" }
post "/notifications", to: "notifications#read", as: :notifications_read
post "/inbox/create", to: "inbox#create", as: :inbox_create post "/inbox/create", to: "inbox#create", as: :inbox_create
get "/inbox", to: "inbox#show", as: :inbox get "/inbox", to: "inbox#show", as: :inbox

View File

@ -3,39 +3,62 @@
require "rails_helper" require "rails_helper"
describe NotificationsController do describe NotificationsController do
subject { get :index, params: { type: :new } } describe "#index" do
subject { get :index, params: { type: :new } }
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
before do before do
sign_in(user) sign_in(user)
end end
context "user has no notifications" do context "user has no notifications" do
it "should show an empty list" do it "should show an empty list" do
subject subject
expect(response).to render_template(:index) expect(response).to render_template(:index)
expect(controller.instance_variable_get(:@notifications)).to be_empty expect(controller.instance_variable_get(:@notifications)).to be_empty
end
end
context "user has notifications" do
let(:other_user) { FactoryBot.create(:user) }
let(:another_user) { FactoryBot.create(:user) }
let(:question) { FactoryBot.create(:question, user:) }
let!(:answer) { FactoryBot.create(:answer, question:, user: other_user) }
let!(:subscription) { Subscription.create(user:, answer:) }
let!(:comment) { FactoryBot.create(:comment, answer:, user: other_user) }
it "should show a list of notifications" do
subject
expect(response).to render_template(:index)
expect(controller.instance_variable_get(:@notifications)).to have_attributes(size: 2)
end
it "marks notifications as read" do
expect { subject }.to change { Notification.for(user).where(new: true).count }.from(2).to(0)
end
end end
end end
context "user has notifications" do describe "#read" do
let(:other_user) { FactoryBot.create(:user) } subject { post :read, format: :turbo_stream }
let(:another_user) { FactoryBot.create(:user) }
let(:question) { FactoryBot.create(:question, user: user) }
let!(:answer) { FactoryBot.create(:answer, question: question, user: other_user) }
let!(:subscription) { Subscription.create(user: user, answer: answer) }
let!(:comment) { FactoryBot.create(:comment, answer: answer, user: other_user) }
it "should show a list of notifications" do let(:recipient) { FactoryBot.create(:user) }
subject let(:notification_count) { rand(8..20) }
expect(response).to render_template(:index)
expect(controller.instance_variable_get(:@notifications)).to have_attributes(size: 2)
end
it "marks notifications as read" do context "user has some unread notifications" do
expect { subject }.to change { Notification.for(user).where(new: true).count }.from(2).to(0) before do
notification_count.times do
FactoryBot.create(:user).follow(recipient)
end
sign_in(recipient)
end
it "marks all notifications as read" do
expect { subject }.to change { Notification.where(recipient:, new: true).count }.from(notification_count).to(0)
end
end end
end end
end end