From 23786bcb52de9f1a979c804bccbeef815558bf8e Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Thu, 26 Mar 2026 14:01:26 +0530 Subject: [PATCH] chore: mark conversation notifications as read on visit (#13906) --- .../v1/accounts/conversations_controller.rb | 2 ++ .../mark_conversation_read_service.rb | 25 +++++++++++++++++++ .../accounts/conversations_controller_spec.rb | 17 +++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 app/services/notification/mark_conversation_read_service.rb diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb index 52d829441..6159f804d 100644 --- a/app/controllers/api/v1/accounts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/conversations_controller.rb @@ -116,6 +116,8 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro # High-traffic accounts generate excessive DB writes when agents frequently switch between conversations. # Throttle last_seen updates to once per hour when there are no unread messages to reduce DB load. # Always update immediately if there are unread messages to maintain accurate read/unread state. + # Visiting a conversation should clear any unread inbox notifications for this conversation. + Notification::MarkConversationReadService.new(user: Current.user, account: Current.account, conversation: @conversation).perform return update_last_seen_on_conversation(DateTime.now.utc, true) if assignee? && @conversation.assignee_unread_messages.any? return update_last_seen_on_conversation(DateTime.now.utc, false) if !assignee? && @conversation.unread_messages.any? diff --git a/app/services/notification/mark_conversation_read_service.rb b/app/services/notification/mark_conversation_read_service.rb new file mode 100644 index 000000000..687121c52 --- /dev/null +++ b/app/services/notification/mark_conversation_read_service.rb @@ -0,0 +1,25 @@ +class Notification::MarkConversationReadService + pattr_initialize [:user!, :account!, :conversation!] + + def perform + return unless user.is_a?(User) + + notifications.find_each do |notification| + notification.update!(read_at: read_at) + end + end + + private + + def notifications + user.notifications.where( + account: account, + primary_actor: conversation, + read_at: nil + ) + end + + def read_at + @read_at ||= Time.current + end +end diff --git a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb index ab70d1c65..e007f4900 100644 --- a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -729,6 +729,23 @@ RSpec.describe 'Conversations API', type: :request do expect(conversation.reload.assignee_last_seen_at).not_to be_nil end + it 'marks unread notifications as read when updating last seen' do + allow(Rails.configuration.dispatcher).to receive(:dispatch) + notification = create(:notification, account: account, user: agent, primary_actor: conversation, read_at: nil) + + post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/update_last_seen", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(notification.reload.read_at).to be_present + expect(Rails.configuration.dispatcher).to have_received(:dispatch).with( + 'notification.updated', + kind_of(Time), + hash_including(notification: have_attributes(id: notification.id)) + ) + end + it 'throttles updates within an hour when there are no unread messages' do conversation.update!(agent_last_seen_at: 30.minutes.ago) # Ensure all messages are older than agent_last_seen_at (no unread messages)