discussion.rb 4.0 KB
Newer Older
1
class Discussion
2
  MEMOIZED_VALUES = [] # rubocop:disable Style/MutableConstant
3

4
  attr_reader :notes
5 6 7 8 9 10 11 12 13 14 15

  delegate  :created_at,
            :project,
            :author,

            :noteable,
            :for_commit?,
            :for_merge_request?,

            to: :first_note

D
Douwe Maan 已提交
16 17 18
  delegate  :resolved_at,
            :resolved_by,

19 20
            to: :last_resolved_note,
            allow_nil: true
D
Douwe Maan 已提交
21

22 23 24
  def self.build(notes, noteable = nil)
    notes.first.discussion_class(noteable).new(notes, noteable)
  end
25

26 27 28
  def self.build_collection(notes, noteable = nil)
    notes.group_by { |n| n.discussion_id(noteable) }.values.map { |notes| build(notes, noteable) }
  end
29

30 31
  def self.discussion_id(note)
    Digest::SHA1.hexdigest(build_discussion_id(note).join("-"))
32 33
  end

34 35 36
  # Optionally override the discussion ID at runtime depending on circumstances
  def self.override_discussion_id(note)
    nil
37 38
  end

39 40 41
  def self.build_discussion_id(note)
    noteable_id = note.noteable_id || note.commit_id
    [:discussion, note.noteable_type.try(:underscore), noteable_id]
42 43
  end

44 45 46 47 48 49 50 51
  def self.original_discussion_id(note)
    original_discussion_id = build_original_discussion_id(note)
    if original_discussion_id
      Digest::SHA1.hexdigest(original_discussion_id.join("-"))
    else
      note.discussion_id
    end
  end
52

53 54 55 56 57 58 59 60 61
  # Optionally build a separate original discussion ID that will never change,
  # if the main discussion ID _can_ change, like in the case of DiffDiscussion.
  def self.build_original_discussion_id(note)
    nil
  end

  def initialize(notes, noteable = nil)
    @notes = notes
    @noteable = noteable
62 63
  end

64 65 66 67 68 69 70 71
  def last_updated_at
    last_note.created_at
  end

  def last_updated_by
    last_note.author
  end

72
  def id
73
    first_note.discussion_id(noteable)
74
  end
75

D
Douwe Maan 已提交
76
  alias_method :to_param, :id
77

78 79 80 81
  def original_id
    first_note.original_discussion_id
  end

82
  def diff_discussion?
83 84 85 86 87
    false
  end

  def render_as_individual_notes?
    false
88 89
  end

90 91
  def potentially_resolvable?
    first_note.for_merge_request?
92 93
  end

D
Douwe Maan 已提交
94
  def resolvable?
95
    return @resolvable if @resolvable.present?
96

97
    @resolvable = potentially_resolvable? && notes.any?(&:resolvable?)
D
Douwe Maan 已提交
98
  end
99
  MEMOIZED_VALUES << :resolvable
D
Douwe Maan 已提交
100 101

  def resolved?
102
    return @resolved if @resolved.present?
103 104

    @resolved = resolvable? && notes.none?(&:to_be_resolved?)
D
Douwe Maan 已提交
105
  end
106
  MEMOIZED_VALUES << :resolved
D
Douwe Maan 已提交
107

108
  def first_note
109
    @first_note ||= notes.first
110
  end
111
  MEMOIZED_VALUES << :first_note
112

113
  def first_note_to_resolve
114 115 116
    return unless resolvable?

    @first_note_to_resolve ||= notes.find(&:to_be_resolved?)
117
  end
118 119 120 121 122 123 124 125
  MEMOIZED_VALUES << :first_note_to_resolve

  def last_resolved_note
    return unless resolved?

    @last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last
  end
  MEMOIZED_VALUES << :last_resolved_note
126

127
  def last_note
128
    @last_note ||= notes.last
129
  end
130
  MEMOIZED_VALUES << :last_note
131

132 133 134 135
  def resolved_notes
    notes.select(&:resolved?)
  end

D
Douwe Maan 已提交
136
  def to_be_resolved?
137
    resolvable? && !resolved?
D
Douwe Maan 已提交
138 139
  end

140 141 142 143 144
  def can_resolve?(current_user)
    return false unless current_user
    return false unless resolvable?

    current_user == self.noteable.author ||
145
      current_user.can?(:resolve_note, self.project)
146 147
  end

148
  def resolve!(current_user)
149 150
    return unless resolvable?

151
    update { |notes| notes.resolve!(current_user) }
152 153 154
  end

  def unresolve!
155 156
    return unless resolvable?

157
    update { |notes| notes.unresolve! }
158 159
  end

D
Douwe Maan 已提交
160 161 162 163 164
  def collapsed?
    if resolvable?
      # New diff discussions only disappear once they are marked resolved
      resolved?
    else
165
      false
D
Douwe Maan 已提交
166 167 168
    end
  end

169
  def expanded?
D
Douwe Maan 已提交
170
    !collapsed?
171 172 173
  end

  def reply_attributes
174
    first_note.slice(:type, :noteable_type, :noteable_id, :commit_id)
175
  end
176 177 178 179

  private

  def update
180 181 182
    # Do not select `Note.resolvable`, so that system notes remain in the collection
    notes_relation = Note.where(id: notes.map(&:id))

183 184 185
    yield(notes_relation)

    # Set the notes array to the updated notes
186
    @notes = notes_relation.fresh.to_a
187

188 189 190
    MEMOIZED_VALUES.each do |var|
      instance_variable_set(:"@#{var}", nil)
    end
191
  end
192
end