193 lines
4.0 KiB
Ruby
193 lines
4.0 KiB
Ruby
class Discussion
|
|
MEMOIZED_VALUES = [] # rubocop:disable Style/MutableConstant
|
|
|
|
attr_reader :notes
|
|
|
|
delegate :created_at,
|
|
:project,
|
|
:author,
|
|
|
|
:noteable,
|
|
:for_commit?,
|
|
:for_merge_request?,
|
|
|
|
to: :first_note
|
|
|
|
delegate :resolved_at,
|
|
:resolved_by,
|
|
|
|
to: :last_resolved_note,
|
|
allow_nil: true
|
|
|
|
def self.build(notes, noteable = nil)
|
|
notes.first.discussion_class(noteable).new(notes, noteable)
|
|
end
|
|
|
|
def self.build_collection(notes, noteable = nil)
|
|
notes.group_by { |n| n.discussion_id(noteable) }.values.map { |notes| build(notes, noteable) }
|
|
end
|
|
|
|
def self.discussion_id(note)
|
|
Digest::SHA1.hexdigest(build_discussion_id(note).join("-"))
|
|
end
|
|
|
|
# Optionally override the discussion ID at runtime depending on circumstances
|
|
def self.override_discussion_id(note)
|
|
nil
|
|
end
|
|
|
|
def self.build_discussion_id(note)
|
|
noteable_id = note.noteable_id || note.commit_id
|
|
[:discussion, note.noteable_type.try(:underscore), noteable_id]
|
|
end
|
|
|
|
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
|
|
|
|
# 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
|
|
end
|
|
|
|
def last_updated_at
|
|
last_note.created_at
|
|
end
|
|
|
|
def last_updated_by
|
|
last_note.author
|
|
end
|
|
|
|
def id
|
|
first_note.discussion_id(noteable)
|
|
end
|
|
|
|
alias_method :to_param, :id
|
|
|
|
def original_id
|
|
first_note.original_discussion_id
|
|
end
|
|
|
|
def diff_discussion?
|
|
false
|
|
end
|
|
|
|
def render_as_individual_notes?
|
|
false
|
|
end
|
|
|
|
def potentially_resolvable?
|
|
first_note.for_merge_request?
|
|
end
|
|
|
|
def resolvable?
|
|
return @resolvable if @resolvable.present?
|
|
|
|
@resolvable = potentially_resolvable? && notes.any?(&:resolvable?)
|
|
end
|
|
MEMOIZED_VALUES << :resolvable
|
|
|
|
def resolved?
|
|
return @resolved if @resolved.present?
|
|
|
|
@resolved = resolvable? && notes.none?(&:to_be_resolved?)
|
|
end
|
|
MEMOIZED_VALUES << :resolved
|
|
|
|
def first_note
|
|
@first_note ||= notes.first
|
|
end
|
|
MEMOIZED_VALUES << :first_note
|
|
|
|
def first_note_to_resolve
|
|
return unless resolvable?
|
|
|
|
@first_note_to_resolve ||= notes.find(&:to_be_resolved?)
|
|
end
|
|
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
|
|
|
|
def last_note
|
|
@last_note ||= notes.last
|
|
end
|
|
MEMOIZED_VALUES << :last_note
|
|
|
|
def resolved_notes
|
|
notes.select(&:resolved?)
|
|
end
|
|
|
|
def to_be_resolved?
|
|
resolvable? && !resolved?
|
|
end
|
|
|
|
def can_resolve?(current_user)
|
|
return false unless current_user
|
|
return false unless resolvable?
|
|
|
|
current_user == self.noteable.author ||
|
|
current_user.can?(:resolve_note, self.project)
|
|
end
|
|
|
|
def resolve!(current_user)
|
|
return unless resolvable?
|
|
|
|
update { |notes| notes.resolve!(current_user) }
|
|
end
|
|
|
|
def unresolve!
|
|
return unless resolvable?
|
|
|
|
update { |notes| notes.unresolve! }
|
|
end
|
|
|
|
def collapsed?
|
|
if resolvable?
|
|
# New diff discussions only disappear once they are marked resolved
|
|
resolved?
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def expanded?
|
|
!collapsed?
|
|
end
|
|
|
|
def reply_attributes
|
|
first_note.slice(:type, :noteable_type, :noteable_id, :commit_id)
|
|
end
|
|
|
|
private
|
|
|
|
def update
|
|
# Do not select `Note.resolvable`, so that system notes remain in the collection
|
|
notes_relation = Note.where(id: notes.map(&:id))
|
|
|
|
yield(notes_relation)
|
|
|
|
# Set the notes array to the updated notes
|
|
@notes = notes_relation.fresh.to_a
|
|
|
|
MEMOIZED_VALUES.each do |var|
|
|
instance_variable_set(:"@#{var}", nil)
|
|
end
|
|
end
|
|
end
|