class Conversations::UnreadCounts::Refresher attr_reader :conversation, :changed_attributes def initialize(conversation, changed_attributes: nil) @conversation = conversation @changed_attributes = changed_attributes.is_a?(Hash) ? changed_attributes : {} end def perform return false unless base_ready? || assignment_ready? before_memberships = store.memberships_for_keys(affected_cache_keys, conversation.id) refresh_base_membership if base_ready? refresh_assignment_membership if assignment_ready? after_memberships = store.memberships_for_keys(affected_cache_keys, conversation.id) before_memberships != after_memberships end private def affected_cache_keys keys = [] keys.concat(affected_base_keys) if base_ready? keys.concat(affected_assignment_keys) if assignment_ready? keys.uniq end def affected_base_keys affected_inbox_ids.flat_map do |inbox_id| [store.inbox_key(account.id, inbox_id)] + affected_label_ids.map { |label_id| store.label_inbox_key(account.id, label_id, inbox_id) } + affected_team_ids.map { |team_id| store.team_inbox_key(account.id, team_id, inbox_id) } end end def affected_assignment_keys affected_inbox_ids.flat_map do |inbox_id| affected_assignee_ids.flat_map do |assignee_id| assignment_keys_for(inbox_id, assignee_id) end end end def assignment_keys_for(inbox_id, assignee_id) keys = assignee_id.present? ? assignee_keys_for(inbox_id, assignee_id) : unassigned_keys_for(inbox_id) keys + affected_team_ids.map { |team_id| team_assignment_key_for(team_id, inbox_id, assignee_id) } end def assignee_keys_for(inbox_id, assignee_id) [store.inbox_assignee_key(account.id, inbox_id, assignee_id)] + affected_label_ids.map { |label_id| store.label_inbox_assignee_key(account.id, label_id, inbox_id, assignee_id) } end def unassigned_keys_for(inbox_id) [store.inbox_unassigned_key(account.id, inbox_id)] + affected_label_ids.map { |label_id| store.label_inbox_unassigned_key(account.id, label_id, inbox_id) } end def refresh_base_membership store.remove_base_membership( account_id: account.id, inbox_ids: affected_inbox_ids, label_ids: affected_label_ids, team_ids: affected_team_ids, conversation_id: conversation.id ) return unless unread? store.add_base_membership( account_id: account.id, inbox_id: conversation.inbox_id, label_ids: current_label_ids, team_id: conversation.team_id, conversation_id: conversation.id ) end def refresh_assignment_membership store.remove_assignment_membership( account_id: account.id, inbox_ids: affected_inbox_ids, label_ids: affected_label_ids, assignee_ids: affected_assignee_ids, team_ids: affected_team_ids, conversation_id: conversation.id ) return unless unread? store.add_assignment_membership( account_id: account.id, inbox_id: conversation.inbox_id, label_ids: current_label_ids, assignee_id: conversation.assignee_id, team_id: conversation.team_id, conversation_id: conversation.id ) end def unread? # Sidebar unread counts intentionally track only open conversations. return false unless conversation.open? incoming_messages = conversation.messages.incoming.where(account_id: account.id) if conversation.agent_last_seen_at incoming_messages = incoming_messages.where(Message.arel_table[:created_at].gt(conversation.agent_last_seen_at)) end incoming_messages.exists? end def affected_inbox_ids [previous_value_for(:inbox_id), conversation.inbox_id].compact.uniq end def affected_assignee_ids return [conversation.assignee_id] unless changed_attribute?(:assignee_id) [previous_value_for(:assignee_id), conversation.assignee_id].uniq end def affected_label_ids (previous_label_ids + current_label_ids).uniq end def affected_team_ids [previous_value_for(:team_id), conversation.team_id].compact.uniq end def previous_label_ids label_ids_for(previous_value_for(:label_list) || previous_value_for(:cached_label_list) || conversation.cached_label_list) end def current_label_ids @current_label_ids ||= label_ids_for(conversation.cached_label_list) end def label_ids_for(label_list) labels = label_list.is_a?(Array) ? label_list : label_list.to_s.split(',') label_titles = labels.map(&:to_s).map(&:strip).compact_blank labels_by_title.values_at(*label_titles).compact end def previous_value_for(attribute) change = changed_attributes[attribute.to_s] || changed_attributes[attribute.to_sym] change&.first end def changed_attribute?(attribute) changed_attributes.key?(attribute.to_s) || changed_attributes.key?(attribute.to_sym) end def team_assignment_key_for(team_id, inbox_id, assignee_id) return store.team_inbox_assignee_key(account.id, team_id, inbox_id, assignee_id) if assignee_id.present? store.team_inbox_unassigned_key(account.id, team_id, inbox_id) end def labels_by_title @labels_by_title ||= account.labels.pluck(:title, :id).to_h end def base_ready? @base_ready ||= store.base_ready?(account.id) end def assignment_ready? @assignment_ready ||= store.assignment_ready?(account.id) end def account conversation.account end def store ::Conversations::UnreadCounts::Store end end