Stack notifications everywhere

We had client-side notification stacking in the tray since launch, but now we want to stack notifications in the notifications page, in API responses and in email bundles.
This commit is contained in:
Stanko K.R.
2026-02-12 10:04:32 +01:00
parent 680a718379
commit 36ee253a1a
35 changed files with 265 additions and 320 deletions
@@ -8,7 +8,7 @@ class Cards::ReadingsControllerTest < ActionDispatch::IntegrationTest
test "create" do
freeze_time
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: false, to: true do
assert_changes -> { accesses(:writebook_kevin).reload.accessed_at }, from: nil, to: Time.current do
post card_reading_url(cards(:logo)), as: :turbo_stream
end
@@ -17,31 +17,20 @@ class Cards::ReadingsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
test "read one notification on card visit" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
test "read notification on card visit" do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: false, to: true do
post card_reading_path(cards(:logo)), as: :turbo_stream
end
assert_response :success
end
test "read multiple notifications on card visit" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: false, to: true do
post card_reading_path(cards(:logo)), as: :turbo_stream
end
end
assert_response :success
end
test "destroy" do
freeze_time
notifications(:logo_published_kevin).read
notifications(:logo_assignment_kevin).read
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: true, to: false do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: true, to: false do
assert_changes -> { accesses(:writebook_kevin).reload.accessed_at }, to: Time.current do
delete card_reading_url(cards(:logo)), as: :turbo_stream
end
@@ -50,26 +39,13 @@ class Cards::ReadingsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
test "unread one notification on destroy" do
notifications(:logo_published_kevin).read
test "unread notification on destroy" do
notifications(:logo_assignment_kevin).read
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: true, to: false do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: true, to: false do
delete card_reading_path(cards(:logo)), as: :turbo_stream
end
assert_response :success
end
test "unread multiple notifications on destroy" do
notifications(:logo_published_kevin).read
notifications(:logo_assignment_kevin).read
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: true, to: false do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: true, to: false do
delete card_reading_path(cards(:logo)), as: :turbo_stream
end
end
assert_response :success
end
end
@@ -6,7 +6,7 @@ class Notifications::BulkReadingsControllerTest < ActionDispatch::IntegrationTes
end
test "create marks all notifications as read" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: false, to: true do
assert_changes -> { notifications(:layout_commented_kevin).reload.read? }, from: false, to: true do
post bulk_reading_path
end
@@ -24,7 +24,7 @@ class Notifications::BulkReadingsControllerTest < ActionDispatch::IntegrationTes
end
test "create as JSON" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
assert_changes -> { notifications(:logo_assignment_kevin).reload.read? }, from: false, to: true do
assert_changes -> { notifications(:layout_commented_kevin).reload.read? }, from: false, to: true do
post bulk_reading_path, as: :json
end
@@ -3,38 +3,37 @@ require "test_helper"
class Notifications::ReadingsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
@notification = notifications(:logo_assignment_kevin)
end
test "create" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
post notification_reading_path(notifications(:logo_published_kevin), format: :turbo_stream)
assert_changes -> { @notification.reload.read? }, from: false, to: true do
post notification_reading_path(@notification, format: :turbo_stream)
assert_response :success
end
end
test "destroy" do
notification = notifications(:logo_published_kevin)
notification.read # Mark as read first
@notification.read
assert_changes -> { notification.reload.read? }, from: true, to: false do
delete notification_reading_path(notification, format: :turbo_stream)
assert_changes -> { @notification.reload.read? }, from: true, to: false do
delete notification_reading_path(@notification, format: :turbo_stream)
assert_response :success
end
end
test "create as JSON" do
assert_changes -> { notifications(:logo_published_kevin).reload.read? }, from: false, to: true do
post notification_reading_path(notifications(:logo_published_kevin)), as: :json
assert_changes -> { @notification.reload.read? }, from: false, to: true do
post notification_reading_path(@notification), as: :json
assert_response :no_content
end
end
test "destroy as JSON" do
notification = notifications(:logo_published_kevin)
notification.read
@notification.read
assert_changes -> { notification.reload.read? }, from: true, to: false do
delete notification_reading_path(notification), as: :json
assert_changes -> { @notification.reload.read? }, from: true, to: false do
delete notification_reading_path(@notification), as: :json
assert_response :no_content
end
end
@@ -10,18 +10,17 @@ class NotificationsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
assert_kind_of Array, @response.parsed_body
assert @response.parsed_body.any? { |n| n["id"] == notifications(:logo_published_kevin).id }
assert @response.parsed_body.any? { |n| n["id"] == notifications(:logo_assignment_kevin).id }
end
test "index as JSON includes notification attributes" do
get notifications_path, as: :json
notification = @response.parsed_body.find { |n| n["id"] == notifications(:logo_published_kevin).id }
notification = @response.parsed_body.find { |n| n["id"] == notifications(:logo_assignment_kevin).id }
assert_not_nil notification["title"]
assert_not_nil notification["body"]
assert_not_nil notification["created_at"]
assert_not_nil notification["card"]
assert_not_nil notification["creator"]
assert_not_nil notification["unread_count"]
end
end