2016-07-20 22:18:18 +00:00
|
|
|
class Discussion
|
|
|
|
NUMBER_OF_TRUNCATED_DIFF_LINES = 16
|
|
|
|
|
2016-07-26 04:36:03 +00:00
|
|
|
attr_reader :first_note, :last_note, :notes
|
2016-07-20 22:18:18 +00:00
|
|
|
|
|
|
|
delegate :created_at,
|
|
|
|
:project,
|
|
|
|
:author,
|
|
|
|
|
|
|
|
:noteable,
|
|
|
|
:for_commit?,
|
|
|
|
:for_merge_request?,
|
|
|
|
|
|
|
|
:line_code,
|
|
|
|
:diff_file,
|
|
|
|
:for_line?,
|
|
|
|
:active?,
|
|
|
|
|
|
|
|
to: :first_note
|
|
|
|
|
2016-07-26 04:36:03 +00:00
|
|
|
delegate :resolved_at,
|
|
|
|
:resolved_by,
|
|
|
|
|
2016-07-29 03:14:45 +00:00
|
|
|
to: :last_resolved_note,
|
|
|
|
allow_nil: true
|
2016-07-26 04:36:03 +00:00
|
|
|
|
2016-07-20 22:18:18 +00:00
|
|
|
delegate :blob, :highlighted_diff_lines, to: :diff_file, allow_nil: true
|
|
|
|
|
|
|
|
def self.for_notes(notes)
|
|
|
|
notes.group_by(&:discussion_id).values.map { |notes| new(notes) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.for_diff_notes(notes)
|
|
|
|
notes.group_by(&:line_code).values.map { |notes| new(notes) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(notes)
|
|
|
|
@first_note = notes.first
|
2016-07-26 04:36:03 +00:00
|
|
|
@last_note = notes.last
|
2016-07-20 22:18:18 +00:00
|
|
|
@notes = notes
|
|
|
|
end
|
|
|
|
|
2016-07-29 03:14:45 +00:00
|
|
|
def last_resolved_note
|
|
|
|
return unless resolved?
|
|
|
|
|
|
|
|
@last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last
|
|
|
|
end
|
|
|
|
|
2016-07-26 04:49:21 +00:00
|
|
|
def last_updated_at
|
|
|
|
last_note.created_at
|
|
|
|
end
|
|
|
|
|
|
|
|
def last_updated_by
|
|
|
|
last_note.author
|
|
|
|
end
|
|
|
|
|
2016-07-20 22:18:18 +00:00
|
|
|
def id
|
|
|
|
first_note.discussion_id
|
|
|
|
end
|
|
|
|
|
|
|
|
def diff_discussion?
|
|
|
|
first_note.diff_note?
|
|
|
|
end
|
|
|
|
|
|
|
|
def legacy_diff_discussion?
|
|
|
|
notes.any?(&:legacy_diff_note?)
|
|
|
|
end
|
|
|
|
|
2016-07-26 04:36:03 +00:00
|
|
|
def resolvable?
|
2016-08-03 22:32:00 +00:00
|
|
|
return @resolvable if defined?(@resolvable)
|
|
|
|
|
|
|
|
@resolvable = diff_discussion? && notes.any?(&:resolvable?)
|
2016-07-26 04:36:03 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def resolved?
|
2016-08-03 22:32:00 +00:00
|
|
|
return @resolved if defined?(@resolved)
|
|
|
|
|
|
|
|
@resolved = resolvable? && notes.none?(&:to_be_resolved?)
|
2016-07-26 04:36:03 +00:00
|
|
|
end
|
|
|
|
|
2016-07-29 03:14:45 +00:00
|
|
|
def resolved_notes
|
|
|
|
notes.select(&:resolved?)
|
|
|
|
end
|
|
|
|
|
2016-07-26 04:36:03 +00:00
|
|
|
def to_be_resolved?
|
2016-08-03 22:32:00 +00:00
|
|
|
return @to_be_resolved if defined?(@to_be_resolved)
|
|
|
|
|
|
|
|
@to_be_resolved = notes.any?(&:to_be_resolved?)
|
2016-07-26 04:36:03 +00:00
|
|
|
end
|
|
|
|
|
2016-07-26 04:40:44 +00:00
|
|
|
def can_resolve?(current_user)
|
|
|
|
return false unless current_user
|
|
|
|
return false unless resolvable?
|
|
|
|
|
|
|
|
current_user == self.noteable.author ||
|
2016-07-27 09:17:05 +00:00
|
|
|
current_user.can?(:push_code, self.project)
|
2016-07-26 04:40:44 +00:00
|
|
|
end
|
|
|
|
|
2016-07-26 04:37:22 +00:00
|
|
|
def resolve!(current_user)
|
|
|
|
notes.each do |note|
|
|
|
|
note.resolve!(current_user) if note.resolvable?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def unresolve!
|
|
|
|
notes.each do |note|
|
|
|
|
note.unresolve! if note.resolvable?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-07-20 22:18:18 +00:00
|
|
|
def for_target?(target)
|
|
|
|
self.noteable == target && !diff_discussion?
|
|
|
|
end
|
|
|
|
|
2016-08-01 14:55:51 +00:00
|
|
|
def active?
|
|
|
|
return @active if defined?(@active)
|
|
|
|
|
|
|
|
@active = first_note.active?
|
|
|
|
end
|
|
|
|
|
2016-07-26 04:46:13 +00:00
|
|
|
def collapsed?
|
|
|
|
return false unless diff_discussion?
|
|
|
|
|
|
|
|
if resolvable?
|
|
|
|
# New diff discussions only disappear once they are marked resolved
|
|
|
|
resolved?
|
|
|
|
else
|
|
|
|
# Old diff discussions disappear once they become outdated
|
|
|
|
!active?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-07-20 22:18:18 +00:00
|
|
|
def expanded?
|
2016-07-26 04:46:13 +00:00
|
|
|
!collapsed?
|
2016-07-20 22:18:18 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def reply_attributes
|
|
|
|
data = {
|
|
|
|
noteable_type: first_note.noteable_type,
|
|
|
|
noteable_id: first_note.noteable_id,
|
|
|
|
commit_id: first_note.commit_id,
|
|
|
|
discussion_id: self.id,
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff_discussion?
|
|
|
|
data[:note_type] = first_note.type
|
|
|
|
|
|
|
|
data.merge!(first_note.diff_attributes)
|
|
|
|
end
|
|
|
|
|
|
|
|
data
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns an array of at most 16 highlighted lines above a diff note
|
|
|
|
def truncated_diff_lines
|
|
|
|
prev_lines = []
|
|
|
|
|
|
|
|
highlighted_diff_lines.each do |line|
|
|
|
|
if line.meta?
|
|
|
|
prev_lines.clear
|
|
|
|
else
|
|
|
|
prev_lines << line
|
|
|
|
|
|
|
|
break if for_line?(line)
|
|
|
|
|
|
|
|
prev_lines.shift if prev_lines.length >= NUMBER_OF_TRUNCATED_DIFF_LINES
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
prev_lines
|
|
|
|
end
|
|
|
|
end
|