Skip to content

Commit 58acba4

Browse files
committed
List changeset comment notifications
1 parent 4e4d96d commit 58acba4

11 files changed

Lines changed: 204 additions & 16 deletions

File tree

.rubocop_todo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Metrics/BlockNesting:
5656
# Offense count: 23
5757
# Configuration parameters: CountComments, CountAsOne.
5858
Metrics/ClassLength:
59-
Max: 345
59+
Max: 340
6060

6161
# Offense count: 72
6262
# Configuration parameters: AllowedMethods, AllowedPatterns.

app/controllers/notifications_controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ class NotificationsController < ApplicationController
99
authorize_resource :class => false
1010

1111
before_action :check_database_readable
12+
13+
def index
14+
@notifications = UserNotifications.new(current_user).visible
15+
end
1216
end

app/models/user.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ class User < ApplicationRecord
9191

9292
has_many :reports
9393

94+
has_many :notifications, :as => :recipient, :class_name => "Noticed::Notification"
95+
9496
has_many :social_links
9597
accepts_nested_attributes_for :social_links, :allow_destroy => true
9698

app/models/user_notifications.rb

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# frozen_string_literal: true
2+
3+
class UserNotifications
4+
class Notification
5+
def self.from(notification)
6+
partial_classname = notification.class.name.sub("Notifier::Notification", "")
7+
klass = "UserNotifications::#{partial_classname}Notification".constantize
8+
klass.new(notification)
9+
end
10+
11+
def initialize(notification)
12+
@notification = notification
13+
end
14+
15+
def visible?
16+
true
17+
end
18+
19+
def to_partial_path
20+
partial_basename = @notification.class.name.sub("Notifier::Notification", "").underscore
21+
"notifications/#{partial_basename}"
22+
end
23+
24+
def timestamp
25+
record.created_at
26+
end
27+
28+
private
29+
30+
def record
31+
@notification.record
32+
end
33+
end
34+
35+
class ChangesetCommentNotification < Notification
36+
delegate :changeset, :to => :record
37+
delegate :visible?, :to => :record
38+
39+
def commenter
40+
record.author
41+
end
42+
43+
def comment_id
44+
record.id
45+
end
46+
47+
def comment_body
48+
record.body
49+
end
50+
end
51+
52+
include Enumerable
53+
54+
LISTABLE_NOTIFICATIONS = %w[
55+
ChangesetCommentNotifier::Notification
56+
].freeze
57+
58+
def initialize(user)
59+
@user = user
60+
end
61+
62+
def visible
63+
select(&:visible?)
64+
end
65+
66+
def each(&)
67+
@user
68+
.notifications
69+
.where(:type => LISTABLE_NOTIFICATIONS)
70+
.newest_first
71+
.map { |record| Notification.from(record) }
72+
.each(&)
73+
end
74+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<%# locals: (notification:) %>
2+
3+
<article class="user-notification">
4+
<p class="text-body-secondary mb-1">
5+
<%= t(
6+
".notification_header_html",
7+
:changeset_comment_link => link_to(
8+
t(".changeset_comment"),
9+
changeset_path(
10+
notification.changeset,
11+
:anchor => "c#{notification.comment_id}"
12+
)
13+
),
14+
:commenter_name_link => link_to(
15+
notification.commenter.display_name,
16+
notification.commenter
17+
),
18+
:time_ago => friendly_date_ago(notification.timestamp)
19+
) %>
20+
</p>
21+
<blockquote class="mx-2 fst-italic">
22+
<p class="text-truncate"><%= notification.comment_body.truncate(200, :separator => " ") %></p>
23+
</blockquote>
24+
</article>
25+
26+
<hr>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
<% content_for :heading do %>
22
<h1><%= t ".title" %></h1>
33
<% end %>
4+
5+
<% @notifications.each do |notification| %>
6+
<%= render :partial => notification.to_partial_path, :object => notification, :as => :notification %>
7+
<% end %>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
class Notifications
6+
class ChangesetCommentViewTest < ActionView::TestCase
7+
def test_basic
8+
comment_author = build_stubbed(
9+
:user,
10+
:display_name => "Helpful Commenter"
11+
)
12+
changeset = build_stubbed(:changeset)
13+
changeset_comment = build_stubbed(
14+
:changeset_comment,
15+
:author => comment_author,
16+
:changeset => changeset
17+
)
18+
notification = Struct.new(:record).new(changeset_comment)
19+
notification_wrapper = UserNotifications::ChangesetCommentNotification.new(notification)
20+
21+
render "notifications/changeset_comment", :notification => notification_wrapper
22+
23+
assert_dom ".user-notification p", "Changeset comment from Helpful Commenter less than 1 minute ago"
24+
assert_dom ".user-notification blockquote", changeset.comment
25+
end
26+
end
27+
end

test/factories/notifications.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
FactoryBot.define do
4+
factory :changeset_comment_notification, :class => "ChangesetCommentNotifier::Notification" do
5+
event :factory => :changeset_comment_notifier
6+
recipient :factory => :user
7+
end
8+
9+
factory :changeset_comment_notifier, :class => "ChangesetCommentNotifier" do
10+
record :factory => :changeset_comment
11+
end
12+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
class UserNotificationsTest < ActiveSupport::TestCase
6+
def test_visible_skips_hidden_changeset_comments
7+
create(:language, :code => "en")
8+
changeset_author = create(:user)
9+
changeset = create(:changeset, :user => changeset_author)
10+
comments = create_list(:changeset_comment, 3, :changeset => changeset)
11+
comments.each do |comment|
12+
event = create(:changeset_comment_notifier, :record => comment)
13+
create(:changeset_comment_notification, :event => event, :recipient => changeset_author)
14+
end
15+
16+
assert_equal 3, UserNotifications.new(changeset_author).visible.count
17+
18+
comments.first.update(:visible => false)
19+
assert_equal 2, UserNotifications.new(changeset_author).visible.count
20+
end
21+
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
require "application_system_test_case"
4+
5+
class OnsiteNotificationsTest < ApplicationSystemTestCase
6+
test "read latest notifications" do
7+
changeset_author = create(:user)
8+
commenter = create(:user, :display_name => "Commenter")
9+
setup_changeset_comment(
10+
:changeset_author => changeset_author,
11+
:commenter => commenter
12+
)
13+
14+
sign_in_as(changeset_author)
15+
16+
click_on changeset_author.display_name
17+
click_on "My Notifications"
18+
19+
assert_text "Notifications"
20+
assert_text "Changeset comment from Commenter"
21+
end
22+
23+
private
24+
25+
def setup_changeset_comment(changeset_author:, commenter:)
26+
changeset = create(:changeset, :user => changeset_author)
27+
create(:changeset_subscription, :changeset => changeset, :subscriber => changeset_author)
28+
29+
comment = create(:changeset_comment, :changeset => changeset, :author => commenter)
30+
create(:changeset_subscription, :changeset => changeset, :subscriber => commenter)
31+
ChangesetCommentNotifier.with(:record => comment).deliver
32+
end
33+
end

0 commit comments

Comments
 (0)