2016-07-20 18:18:18 -04:00
|
|
|
class Discussion
|
|
|
|
NUMBER_OF_TRUNCATED_DIFF_LINES = 16
|
|
|
|
|
2016-09-02 16:29:47 -04:00
|
|
|
attr_reader :notes
|
2016-07-20 18:18:18 -04:00
|
|
|
|
|
|
|
delegate :created_at,
|
|
|
|
:project,
|
|
|
|
:author,
|
|
|
|
|
|
|
|
:noteable,
|
|
|
|
:for_commit?,
|
|
|
|
:for_merge_request?,
|
|
|
|
|
|
|
|
:line_code,
|
2016-08-19 13:38:40 -04:00
|
|
|
:original_line_code,
|
2016-07-20 18:18:18 -04:00
|
|
|
:diff_file,
|
|
|
|
:for_line?,
|
|
|
|
:active?,
|
|
|
|
|
|
|
|
to: :first_note
|
|
|
|
|
2016-07-26 00:36:03 -04:00
|
|
|
delegate :resolved_at,
|
|
|
|
:resolved_by,
|
|
|
|
|
2016-07-28 23:14:45 -04:00
|
|
|
to: :last_resolved_note,
|
|
|
|
allow_nil: true
|
2016-07-26 00:36:03 -04:00
|
|
|
|
2016-07-20 18:18:18 -04: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)
|
|
|
|
@notes = notes
|
|
|
|
end
|
|
|
|
|
2016-07-28 23:14:45 -04:00
|
|
|
def last_resolved_note
|
|
|
|
return unless resolved?
|
|
|
|
|
|
|
|
@last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last
|
|
|
|
end
|
|
|
|
|
2016-07-26 00:49:21 -04:00
|
|
|
def last_updated_at
|
|
|
|
last_note.created_at
|
|
|
|
end
|
|
|
|
|
|
|
|
def last_updated_by
|
|
|
|
last_note.author
|
|
|
|
end
|
|
|
|
|
2016-07-20 18:18:18 -04:00
|
|
|
def id
|
|
|
|
first_note.discussion_id
|
|
|
|
end
|
2016-08-17 13:14:44 -04:00
|
|
|
|
2016-08-15 19:45:23 -04:00
|
|
|
alias_method :to_param, :id
|
2016-07-20 18:18:18 -04:00
|
|
|
|
|
|
|
def diff_discussion?
|
|
|
|
first_note.diff_note?
|
|
|
|
end
|
|
|
|
|
|
|
|
def legacy_diff_discussion?
|
|
|
|
notes.any?(&:legacy_diff_note?)
|
|
|
|
end
|
|
|
|
|
2016-07-26 00:36:03 -04:00
|
|
|
def resolvable?
|
2016-09-02 16:29:47 -04:00
|
|
|
return @resolvable if @resolvable.present?
|
2016-08-03 18:32:00 -04:00
|
|
|
|
|
|
|
@resolvable = diff_discussion? && notes.any?(&:resolvable?)
|
2016-07-26 00:36:03 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def resolved?
|
2016-09-02 16:29:47 -04:00
|
|
|
return @resolved if @resolved.present?
|
2016-08-03 18:32:00 -04:00
|
|
|
|
|
|
|
@resolved = resolvable? && notes.none?(&:to_be_resolved?)
|
2016-07-26 00:36:03 -04:00
|
|
|
end
|
|
|
|
|
2016-09-02 16:29:47 -04:00
|
|
|
def first_note
|
|
|
|
@first_note ||= @notes.first
|
|
|
|
end
|
|
|
|
|
|
|
|
def last_note
|
|
|
|
@last_note ||= @notes.last
|
|
|
|
end
|
|
|
|
|
2016-07-28 23:14:45 -04:00
|
|
|
def resolved_notes
|
|
|
|
notes.select(&:resolved?)
|
|
|
|
end
|
|
|
|
|
2016-07-26 00:36:03 -04:00
|
|
|
def to_be_resolved?
|
2016-08-12 16:49:25 -04:00
|
|
|
resolvable? && !resolved?
|
2016-07-26 00:36:03 -04:00
|
|
|
end
|
|
|
|
|
2016-07-26 00:40:44 -04:00
|
|
|
def can_resolve?(current_user)
|
|
|
|
return false unless current_user
|
|
|
|
return false unless resolvable?
|
|
|
|
|
|
|
|
current_user == self.noteable.author ||
|
2016-08-17 13:14:44 -04:00
|
|
|
current_user.can?(:resolve_note, self.project)
|
2016-07-26 00:40:44 -04:00
|
|
|
end
|
|
|
|
|
2016-07-26 00:37:22 -04:00
|
|
|
def resolve!(current_user)
|
2016-08-12 16:49:25 -04:00
|
|
|
return unless resolvable?
|
|
|
|
|
2016-09-02 16:29:47 -04:00
|
|
|
update { |notes| notes.resolve!(current_user) }
|
2016-07-26 00:37:22 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def unresolve!
|
2016-08-12 16:49:25 -04:00
|
|
|
return unless resolvable?
|
|
|
|
|
2016-09-02 16:29:47 -04:00
|
|
|
update { |notes| notes.unresolve! }
|
2016-07-26 00:37:22 -04:00
|
|
|
end
|
|
|
|
|
2016-07-20 18:18:18 -04:00
|
|
|
def for_target?(target)
|
|
|
|
self.noteable == target && !diff_discussion?
|
|
|
|
end
|
|
|
|
|
2016-08-01 10:55:51 -04:00
|
|
|
def active?
|
2016-09-02 16:29:47 -04:00
|
|
|
return @active if @active.present?
|
2016-08-01 10:55:51 -04:00
|
|
|
|
|
|
|
@active = first_note.active?
|
|
|
|
end
|
|
|
|
|
2016-07-26 00:46:13 -04: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 18:18:18 -04:00
|
|
|
def expanded?
|
2016-07-26 00:46:13 -04:00
|
|
|
!collapsed?
|
2016-07-20 18:18:18 -04: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
|
2016-09-02 16:29:47 -04:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def update
|
|
|
|
notes_relation = DiffNote.where(id: notes.map(&:id)).fresh
|
|
|
|
yield(notes_relation)
|
|
|
|
|
|
|
|
# Set the notes array to the updated notes
|
|
|
|
@notes = notes_relation.to_a
|
|
|
|
|
|
|
|
# Reset the memoized values
|
|
|
|
@last_resolved_note = @resolvable = @resolved = @first_note = @last_note = nil
|
|
|
|
end
|
2016-07-20 18:18:18 -04:00
|
|
|
end
|