discussion.rb 4.1 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 92 93
  def new_discussion?
    notes.length == 1
  end

94 95
  def potentially_resolvable?
    first_note.for_merge_request?
96 97
  end

D
Douwe Maan 已提交
98
  def resolvable?
99
    return @resolvable if @resolvable.present?
100

101
    @resolvable = potentially_resolvable? && notes.any?(&:resolvable?)
D
Douwe Maan 已提交
102
  end
103
  MEMOIZED_VALUES << :resolvable
D
Douwe Maan 已提交
104 105

  def resolved?
106
    return @resolved if @resolved.present?
107 108

    @resolved = resolvable? && notes.none?(&:to_be_resolved?)
D
Douwe Maan 已提交
109
  end
110
  MEMOIZED_VALUES << :resolved
D
Douwe Maan 已提交
111

112
  def first_note
113
    @first_note ||= notes.first
114
  end
115
  MEMOIZED_VALUES << :first_note
116

117
  def first_note_to_resolve
118 119 120
    return unless resolvable?

    @first_note_to_resolve ||= notes.find(&:to_be_resolved?)
121
  end
122 123 124 125 126 127 128 129
  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
130

131
  def last_note
132
    @last_note ||= notes.last
133
  end
134
  MEMOIZED_VALUES << :last_note
135

136 137 138 139
  def resolved_notes
    notes.select(&:resolved?)
  end

D
Douwe Maan 已提交
140
  def to_be_resolved?
141
    resolvable? && !resolved?
D
Douwe Maan 已提交
142 143
  end

144 145 146 147 148
  def can_resolve?(current_user)
    return false unless current_user
    return false unless resolvable?

    current_user == self.noteable.author ||
149
      current_user.can?(:resolve_note, self.project)
150 151
  end

152
  def resolve!(current_user)
153 154
    return unless resolvable?

155
    update { |notes| notes.resolve!(current_user) }
156 157 158
  end

  def unresolve!
159 160
    return unless resolvable?

161
    update { |notes| notes.unresolve! }
162 163
  end

D
Douwe Maan 已提交
164 165 166 167 168
  def collapsed?
    if resolvable?
      # New diff discussions only disappear once they are marked resolved
      resolved?
    else
169
      false
D
Douwe Maan 已提交
170 171 172
    end
  end

173
  def expanded?
D
Douwe Maan 已提交
174
    !collapsed?
175 176 177
  end

  def reply_attributes
178
    first_note.slice(:type, :noteable_type, :noteable_id, :commit_id)
179
  end
180 181 182 183

  private

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

187 188 189
    yield(notes_relation)

    # Set the notes array to the updated notes
190
    @notes = notes_relation.fresh.to_a
191

192 193 194
    MEMOIZED_VALUES.each do |var|
      instance_variable_set(:"@#{var}", nil)
    end
195
  end
196
end