1# frozen_string_literal: true
2
3module ResolvableNote
4  extend ActiveSupport::Concern
5
6  # Names of all subclasses of `Note` that can be resolvable.
7  RESOLVABLE_TYPES = %w(DiffNote DiscussionNote).freeze
8
9  included do
10    belongs_to :resolved_by, class_name: "User"
11
12    validates :resolved_by, presence: true, if: :resolved?
13
14    # Keep this scope in sync with `#potentially_resolvable?`
15    scope :potentially_resolvable, -> { where(type: RESOLVABLE_TYPES).where(noteable_type: Noteable.resolvable_types) }
16    # Keep this scope in sync with `#resolvable?`
17    scope :resolvable, -> { potentially_resolvable.user }
18
19    scope :resolved, -> { resolvable.where.not(resolved_at: nil) }
20    scope :unresolved, -> { resolvable.where(resolved_at: nil) }
21  end
22
23  class_methods do
24    # This method must be kept in sync with `#resolve!`
25    def resolve!(current_user)
26      unresolved.update_all(resolved_at: Time.current, resolved_by_id: current_user.id)
27    end
28
29    # This method must be kept in sync with `#unresolve!`
30    def unresolve!
31      resolved.update_all(resolved_at: nil, resolved_by_id: nil)
32    end
33  end
34
35  # Keep this method in sync with the `potentially_resolvable` scope
36  def potentially_resolvable?
37    RESOLVABLE_TYPES.include?(self.class.name) && noteable&.supports_resolvable_notes?
38  end
39
40  # Keep this method in sync with the `resolvable` scope
41  def resolvable?
42    potentially_resolvable? && !system?
43  end
44
45  def resolved?
46    return false unless resolvable?
47
48    self.resolved_at.present?
49  end
50
51  def to_be_resolved?
52    resolvable? && !resolved?
53  end
54
55  # If you update this method remember to also update `.resolve!`
56  def resolve_without_save(current_user, resolved_by_push: false)
57    return false unless resolvable?
58    return false if resolved?
59
60    self.resolved_at = Time.current
61    self.resolved_by = current_user
62    self.resolved_by_push = resolved_by_push
63
64    true
65  end
66
67  # If you update this method remember to also update `.unresolve!`
68  def unresolve_without_save
69    return false unless resolvable?
70    return false unless resolved?
71
72    self.resolved_at = nil
73    self.resolved_by = nil
74
75    true
76  end
77
78  def resolve!(current_user, resolved_by_push: false)
79    resolve_without_save(current_user, resolved_by_push: resolved_by_push) &&
80      save!
81  end
82
83  def unresolve!
84    unresolve_without_save && save!
85  end
86end
87