diff --git a/app/controllers/api/v1/widget/conversations_controller.rb b/app/controllers/api/v1/widget/conversations_controller.rb index 431ffe2d5..df0ef64ff 100644 --- a/app/controllers/api/v1/widget/conversations_controller.rb +++ b/app/controllers/api/v1/widget/conversations_controller.rb @@ -28,6 +28,7 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController conversation.contact_last_seen_at = DateTime.now.utc conversation.save! + ::Conversations::MarkMessagesAsReadJob.perform_later(conversation) head :ok end diff --git a/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue b/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue index 4ea351679..e03cc74ed 100644 --- a/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue +++ b/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue @@ -214,6 +214,10 @@ export default { if (this.isAWhatsAppChannel || this.isATwilioChannel) { return this.sourceId && this.isDelivered; } + // We will consider messages as delivered for web widget inbox if they are sent + if (this.isAWebWidgetInbox) { + return this.isSent; + } return false; }, diff --git a/app/jobs/conversations/mark_messages_as_read_job.rb b/app/jobs/conversations/mark_messages_as_read_job.rb new file mode 100644 index 000000000..ce828521f --- /dev/null +++ b/app/jobs/conversations/mark_messages_as_read_job.rb @@ -0,0 +1,13 @@ +class Conversations::MarkMessagesAsReadJob < ApplicationJob + queue_as :low + + def perform(conversation) + # Mark every message created before the user's viewing time as read. + conversation.messages.where(status: %w[sent delivered]) + .where.not(message_type: 'incoming') + .where('created_at <= ?', + conversation.contact_last_seen_at).find_each do |message| + message.update!(status: 'read') + end + end +end diff --git a/spec/controllers/api/v1/widget/conversations_controller_spec.rb b/spec/controllers/api/v1/widget/conversations_controller_spec.rb index 40aed4616..26e525d3a 100644 --- a/spec/controllers/api/v1/widget/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/widget/conversations_controller_spec.rb @@ -170,6 +170,7 @@ RSpec.describe '/api/v1/widget/conversations/toggle_typing', type: :request do it 'returns the correct conversation params' do allow(Rails.configuration.dispatcher).to receive(:dispatch) expect(conversation.contact_last_seen_at).to be_nil + expect(Conversations::MarkMessagesAsReadJob).to receive(:perform_later).with(conversation) post '/api/v1/widget/conversations/update_last_seen', headers: { 'X-Auth-Token' => token }, diff --git a/spec/jobs/conversations/mark_messages_as_read_job_spec.rb b/spec/jobs/conversations/mark_messages_as_read_job_spec.rb new file mode 100644 index 000000000..18ddb41f5 --- /dev/null +++ b/spec/jobs/conversations/mark_messages_as_read_job_spec.rb @@ -0,0 +1,61 @@ +require 'rails_helper' + +RSpec.describe Conversations::MarkMessagesAsReadJob do + subject(:job) { described_class.perform_later(account) } + + let!(:account) { create(:account) } + let!(:conversation) { create(:conversation, account: account, contact_last_seen_at: DateTime.now.utc) } + let!(:message) { create(:message, conversation: conversation, message_type: 'outgoing', status: 'sent', created_at: 1.day.ago) } + + it 'enqueues the job' do + expect { job }.to have_enqueued_job(described_class) + .with(account) + .on_queue('low') + end + + context 'when called' do + it 'marks all sent messages in a conversation as read' do + expect do + described_class.perform_now(conversation) + message.reload + end.to change(message, :status).from('sent').to('read') + end + + it 'marks all delivered messages in a conversation as read' do + message.update!(status: 'delivered') + expect do + described_class.perform_now(conversation) + message.reload + end.to change(message, :status).from('delivered').to('read') + end + + it 'marks all templates messages in a conversation as read' do + message.update!(status: 'delivered', message_type: 'template') + expect do + described_class.perform_now(conversation) + message.reload + end.to change(message, :status).from('delivered').to('read') + end + + it 'does not mark failed messages as read' do + message.update!(status: 'failed') + expect do + described_class.perform_now(conversation) + end.not_to change(message.reload, :status) + end + + it 'does not mark incoming messages as read' do + message.update!(message_type: 'incoming') + expect do + described_class.perform_now(conversation) + end.not_to change(message.reload, :status) + end + + it 'does not mark messages created after the contact last seen time as read' do + message.update!(created_at: DateTime.now.utc) + expect do + described_class.perform_now(conversation) + end.not_to change(message.reload, :status) + end + end +end