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
|
|
|
|
Change diff highlight/truncate for reusability
Previously the `truncated_diff_lines` method for outputting a discussion diff took in already highlighted lines, which meant it wasn't reuseable for truncating ANY lines. In the way it was used, it also meant that for any email truncation, the whole diff was being highlighted before being truncated, meaning wasted time highlighting lines that wouldn't even be used (granted, they were being memoized, so perhaps this wasn't that great of an issue). I refactored truncation away from highlighting, in order to truncate formatted diffs for text templates in email, using `>`s to designate each line, but otherwise retaining the parsing already done to create `diff_lines`.
Additionally, while notes on merge requests or commits had already been tested, there was no existing test for notes on a diff on an MR or commit. Added mailer tests for such, and a unit test for truncating diff lines.
2016-08-25 12:38:07 -04:00
|
|
|
delegate :blob,
|
|
|
|
:highlighted_diff_lines,
|
2016-08-31 12:18:26 -04:00
|
|
|
:diff_lines,
|
Change diff highlight/truncate for reusability
Previously the `truncated_diff_lines` method for outputting a discussion diff took in already highlighted lines, which meant it wasn't reuseable for truncating ANY lines. In the way it was used, it also meant that for any email truncation, the whole diff was being highlighted before being truncated, meaning wasted time highlighting lines that wouldn't even be used (granted, they were being memoized, so perhaps this wasn't that great of an issue). I refactored truncation away from highlighting, in order to truncate formatted diffs for text templates in email, using `>`s to designate each line, but otherwise retaining the parsing already done to create `diff_lines`.
Additionally, while notes on merge requests or commits had already been tested, there was no existing test for notes on a diff on an MR or commit. Added mailer tests for such, and a unit test for truncating diff lines.
2016-08-25 12:38:07 -04:00
|
|
|
|
|
|
|
to: :diff_file,
|
|
|
|
allow_nil: true
|
2016-07-20 18:18:18 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2016-10-26 17:21:50 -04:00
|
|
|
def first_note_to_resolve
|
|
|
|
@first_note_to_resolve ||= notes.detect(&:to_be_resolved?)
|
|
|
|
end
|
|
|
|
|
2016-09-02 16:29:47 -04:00
|
|
|
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
|
2016-08-31 12:18:26 -04:00
|
|
|
def truncated_diff_lines(highlight: true)
|
2016-11-17 17:22:39 -05:00
|
|
|
lines = highlight ? highlighted_diff_lines : diff_lines
|
2016-07-20 18:18:18 -04:00
|
|
|
prev_lines = []
|
|
|
|
|
2016-11-17 17:22:39 -05:00
|
|
|
lines.each do |line|
|
2016-07-20 18:18:18 -04:00
|
|
|
if line.meta?
|
|
|
|
prev_lines.clear
|
|
|
|
else
|
|
|
|
prev_lines << line
|
2016-11-17 17:22:39 -05:00
|
|
|
|
2016-07-20 18:18:18 -04:00
|
|
|
break if for_line?(line)
|
2016-11-17 17:22:39 -05:00
|
|
|
|
2016-07-20 18:18:18 -04:00
|
|
|
prev_lines.shift if prev_lines.length >= NUMBER_OF_TRUNCATED_DIFF_LINES
|
|
|
|
end
|
|
|
|
end
|
2016-11-17 17:22:39 -05:00
|
|
|
|
2016-07-20 18:18:18 -04:00
|
|
|
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
|