summaryrefslogtreecommitdiff
path: root/app/models/discussion.rb
blob: cc5869331120a08ee948ee5aed595f2f3d1e6494 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class Discussion
  NUMBER_OF_TRUNCATED_DIFF_LINES = 16

  attr_reader :first_note, :last_note, :notes

  delegate  :created_at,
            :project,
            :author,

            :noteable,
            :for_commit?,
            :for_merge_request?,

            :line_code,
            :diff_file,
            :for_line?,
            :active?,

            to: :first_note

  delegate  :resolved_at,
            :resolved_by,

            to: :last_note

  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
    @last_note = notes.last
    @notes = notes
  end

  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

  def resolvable?
    diff_discussion? && notes.any?(&:resolvable?)
  end

  def resolved?
    notes.none?(&:to_be_resolved?)
  end

  def to_be_resolved?
    notes.any?(&:to_be_resolved?)
  end

  def can_resolve?(current_user)
    return false unless current_user
    return false unless resolvable?

    current_user == self.noteable.author ||
      can?(current_user, :push_code, self.project)
  end

  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

  def for_target?(target)
    self.noteable == target && !diff_discussion?
  end

  def expanded?
    !diff_discussion? || active?
  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