Commit 1065ba40 authored by Luke "Jared" Bennett's avatar Luke "Jared" Bennett

Merge branch 'new-resolvable-discussion' of gitlab.com:gitlab-org/gitlab-ce...

Merge branch 'new-resolvable-discussion' of gitlab.com:gitlab-org/gitlab-ce into new-resolvable-discussion
parents 1df9ac23 cc656a11
...@@ -312,8 +312,6 @@ ...@@ -312,8 +312,6 @@
} }
.comment-type-dropdown { .comment-type-dropdown {
background: red;
.dropdown-toggle .fa { .dropdown-toggle .fa {
color: $white-light; color: $white-light;
padding-right: 2px; padding-right: 2px;
......
...@@ -292,17 +292,6 @@ module Issuable ...@@ -292,17 +292,6 @@ module Issuable
self.class.to_ability_name self.class.to_ability_name
end end
# Convert this Issuable class name to a format usable by notifications.
#
# Examples:
#
# issuable.class # => MergeRequest
# issuable.human_class_name # => "merge request"
def human_class_name
@human_class_name ||= self.class.name.titleize.downcase
end
# Returns a Hash of attributes to be used for Twitter card metadata # Returns a Hash of attributes to be used for Twitter card metadata
def card_attributes def card_attributes
{ {
......
module Noteable module Noteable
# Names of all implementers of `Noteable` that support resolvable notes.
RESOLVABLE_TYPES = %w(MergeRequest).freeze
def base_class_name
self.class.base_class.name
end
# Convert this Noteable class name to a format usable by notifications.
#
# Examples:
#
# noteable.class # => MergeRequest
# noteable.human_class_name # => "merge request"
def human_class_name
@human_class_name ||= base_class_name.titleize.downcase
end
def supports_resolvable_notes?
RESOLVABLE_TYPES.include?(base_class_name)
end
def supports_discussions?
DiscussionNote::NOTEABLE_TYPES.include?(base_class_name)
end
def discussion_notes def discussion_notes
notes notes
end end
......
...@@ -20,6 +20,8 @@ module ResolvableDiscussion ...@@ -20,6 +20,8 @@ module ResolvableDiscussion
:last_note :last_note
) )
delegate :potentially_resolvable?, to: :first_note
delegate :resolved_at, delegate :resolved_at,
:resolved_by, :resolved_by,
...@@ -27,11 +29,6 @@ module ResolvableDiscussion ...@@ -27,11 +29,6 @@ module ResolvableDiscussion
allow_nil: true allow_nil: true
end end
# Keep this method in sync with the `potentially_resolvable` scope on `ResolvableNote`
def potentially_resolvable?
for_merge_request?
end
def resolvable? def resolvable?
return @resolvable if @resolvable.present? return @resolvable if @resolvable.present?
......
module ResolvableNote module ResolvableNote
extend ActiveSupport::Concern extend ActiveSupport::Concern
# Names of all subclasses of `Note` that can be resolvable.
RESOLVABLE_TYPES = %w(DiffNote DiscussionNote).freeze RESOLVABLE_TYPES = %w(DiffNote DiscussionNote).freeze
included do included do
...@@ -8,10 +9,8 @@ module ResolvableNote ...@@ -8,10 +9,8 @@ module ResolvableNote
validates :resolved_by, presence: true, if: :resolved? validates :resolved_by, presence: true, if: :resolved?
# Keep this scope in sync with the logic in `#potentially_resolvable?` in subclasses of `Discussion` that are resolvable. # Keep this scope in sync with `#potentially_resolvable?`
# `RESOLVABLE_TYPES` should include names of all subclasses that are resolvable (where the method can return true), and scope :potentially_resolvable, -> { where(type: RESOLVABLE_TYPES).where(noteable_type: Noteable::RESOLVABLE_TYPES) }
# the scope should also match the criteria `ResolvableDiscussion#potentially_resolvable?` puts on resolvability.
scope :potentially_resolvable, -> { where(type: RESOLVABLE_TYPES).where(noteable_type: 'MergeRequest') }
# Keep this scope in sync with `#resolvable?` # Keep this scope in sync with `#resolvable?`
scope :resolvable, -> { potentially_resolvable.user } scope :resolvable, -> { potentially_resolvable.user }
...@@ -31,7 +30,10 @@ module ResolvableNote ...@@ -31,7 +30,10 @@ module ResolvableNote
end end
end end
delegate :potentially_resolvable?, to: :to_discussion # Keep this method in sync with the `potentially_resolvable` scope
def potentially_resolvable?
RESOLVABLE_TYPES.include?(self.class.name) && noteable.supports_resolvable_notes?
end
# Keep this method in sync with the `resolvable` scope # Keep this method in sync with the `resolvable` scope
def resolvable? def resolvable?
......
# A discussion on merge request or commit diffs consisting of `DiffNote` notes. # A discussion on merge request or commit diffs consisting of `DiffNote` notes.
#
# A discussion of this type can be resolvable.
class DiffDiscussion < Discussion class DiffDiscussion < Discussion
include DiscussionOnDiff include DiscussionOnDiff
def self.note_class
DiffNote
end
delegate :position, delegate :position,
:original_position, :original_position,
......
# A note on merge request or commit diffs # A note on merge request or commit diffs
#
# A note of this type can be resolvable.
class DiffNote < Note class DiffNote < Note
include NoteOnDiff include NoteOnDiff
......
# A non-diff discussion on an issue, merge request, commit, or snippet, consisting of `DiscussionNote` notes. # A non-diff discussion on an issue, merge request, commit, or snippet, consisting of `DiscussionNote` notes.
#
# A discussion of this type can be resolvable.
class Discussion class Discussion
include ResolvableDiscussion include ResolvableDiscussion
...@@ -54,6 +56,10 @@ class Discussion ...@@ -54,6 +56,10 @@ class Discussion
nil nil
end end
def self.note_class
DiscussionNote
end
def initialize(notes, noteable = nil) def initialize(notes, noteable = nil)
@notes = notes @notes = notes
@noteable = noteable @noteable = noteable
......
# A note in a non-diff discussion on an issue, merge request, commit, or snippet # A note in a non-diff discussion on an issue, merge request, commit, or snippet.
#
# A note of this type can be resolvable.
class DiscussionNote < Note class DiscussionNote < Note
# Names of all implementers of `Noteable` that support discussions.
NOTEABLE_TYPES = %w(MergeRequest Issue Commit Snippet).freeze NOTEABLE_TYPES = %w(MergeRequest Issue Commit Snippet).freeze
RESOLVABLE_TYPES = %w(MergeRequest).freeze
validates :noteable_type, inclusion: { in: NOTEABLE_TYPES } validates :noteable_type, inclusion: { in: NOTEABLE_TYPES }
......
# A discussion to wrap a single `Note` note on the root of an issue, merge request, # A discussion to wrap a single `Note` note on the root of an issue, merge request,
# commit, or snippet, that is not displayed as a discussion. # commit, or snippet, that is not displayed as a discussion.
#
# A discussion of this type is never resolvable.
class IndividualNoteDiscussion < Discussion class IndividualNoteDiscussion < Discussion
def potentially_resolvable? def self.note_class
false Note
end end
def individual_note? def individual_note?
......
# A discussion on merge request or commit diffs consisting of `LegacyDiffNote` notes. # A discussion on merge request or commit diffs consisting of `LegacyDiffNote` notes.
#
# All new diff discussions are of the type `DiffDiscussion`, but any diff discussions created # All new diff discussions are of the type `DiffDiscussion`, but any diff discussions created
# before the introduction of the new implementation still use `LegacyDiffDiscussion`. # before the introduction of the new implementation still use `LegacyDiffDiscussion`.
#
# A discussion of this type is never resolvable.
class LegacyDiffDiscussion < Discussion class LegacyDiffDiscussion < Discussion
include DiscussionOnDiff include DiscussionOnDiff
...@@ -8,8 +11,8 @@ class LegacyDiffDiscussion < Discussion ...@@ -8,8 +11,8 @@ class LegacyDiffDiscussion < Discussion
true true
end end
def potentially_resolvable? def self.note_class
false LegacyDiffNote
end end
def collapsed? def collapsed?
......
# A note on merge request or commit diffs, using the legacy implementation. # A note on merge request or commit diffs, using the legacy implementation.
#
# All new diff notes are of the type `DiffNote`, but any diff notes created # All new diff notes are of the type `DiffNote`, but any diff notes created
# before the introduction of the new implementation still use `LegacyDiffNote`. # before the introduction of the new implementation still use `LegacyDiffNote`.
#
# A note of this type is never resolvable.
class LegacyDiffNote < Note class LegacyDiffNote < Note
include NoteOnDiff include NoteOnDiff
......
# A note on the root of an issue, merge request, commit, or snippet.
#
# A note of this type is never resolvable.
class Note < ActiveRecord::Base class Note < ActiveRecord::Base
extend ActiveModel::Naming extend ActiveModel::Naming
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
...@@ -225,11 +228,7 @@ class Note < ActiveRecord::Base ...@@ -225,11 +228,7 @@ class Note < ActiveRecord::Base
end end
def can_be_discussion_note? def can_be_discussion_note?
DiscussionNote::NOTEABLE_TYPES.include?(self.noteable_type) && !part_of_discussion? self.noteable.supports_discussions? && !part_of_discussion?
end
def can_be_resolvable?
DiscussionNote::RESOLVABLE_TYPES.include?(self.noteable_type)
end end
def discussion_class(noteable = nil) def discussion_class(noteable = nil)
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# contains that commit, they are displayed as if they were a discussion. # contains that commit, they are displayed as if they were a discussion.
# #
# This represents one of those discussions, consisting of `Note` notes. # This represents one of those discussions, consisting of `Note` notes.
#
# A discussion of this type is never resolvable.
class OutOfContextDiscussion < Discussion class OutOfContextDiscussion < Discussion
# Returns an array of discussion ID components # Returns an array of discussion ID components
def self.build_discussion_id(note) def self.build_discussion_id(note)
...@@ -14,7 +16,7 @@ class OutOfContextDiscussion < Discussion ...@@ -14,7 +16,7 @@ class OutOfContextDiscussion < Discussion
discussion_id(note) discussion_id(note)
end end
def potentially_resolvable? def self.note_class
false Note
end end
end end
...@@ -36,12 +36,14 @@ module Issues ...@@ -36,12 +36,14 @@ module Issues
def item_for_discussion(discussion) def item_for_discussion(discussion)
first_note_to_resolve = discussion.first_note_to_resolve || discussion.first_note first_note_to_resolve = discussion.first_note_to_resolve || discussion.first_note
other_note_count = discussion.notes.size - 1
note_url = Gitlab::UrlBuilder.build(first_note_to_resolve)
is_very_first_note = first_note_to_resolve == discussion.first_note is_very_first_note = first_note_to_resolve == discussion.first_note
action = is_very_first_note ? "started" : "commented on" action = is_very_first_note ? "started" : "commented on"
note_url = Gitlab::UrlBuilder.build(first_note_to_resolve)
other_note_count = discussion.notes.size - 1
discussion_info = "- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): " discussion_info = "- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "
discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0 discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
......
- noteable_type = @note.noteable_type - noteable_name = @note.noteable.human_class_name
.pull-left.btn-group.append-right-10.comment-type-dropdown.js-comment-type-dropdown .pull-left.btn-group.append-right-10.comment-type-dropdown.js-comment-type-dropdown
%input.btn.btn-nr.btn-create.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: 'Comment' } %input.btn.btn-nr.btn-create.comment-btn.js-comment-button.js-comment-submit-button{ type: 'submit', value: 'Comment' }
- if @note.can_be_discussion_note? - if @note.can_be_discussion_note?
= button_tag type: 'button', class: 'btn btn-nr dropdown-toggle comment-btn js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => 'Open comment type dropdown' do = button_tag type: 'button', class: 'btn btn-nr dropdown-toggle comment-btn js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => 'Open comment type dropdown' do
= icon('caret-down') = icon('caret-down')
%ul#resolvable-comment-menu.dropdown-menu{ data: { dropdown: true } } %ul#resolvable-comment-menu.dropdown-menu{ data: { dropdown: true } }
%li#comment.droplab-item-selected{ data: { value: '', 'button-text' => 'Comment', 'secondary-button-text' => "Comment & close #{noteable_type.titleize.downcase}" } } %li#comment.droplab-item-selected{ data: { value: '', 'button-text' => 'Comment', 'secondary-button-text' => "Comment & close #{noteable_name}" } }
= icon('check') = icon('check')
.description .description
%strong Comment %strong Comment
%p= "Add a general comment to this #{noteable_type.titleize.downcase}."
%li.divider
- if @note.can_be_resolvable?
%li#discussion{ data: { value: 'DiscussionNote', 'button-text' => 'Start discussion', 'secondary-button-text' => "Start discussion & close #{noteable_type.titleize.downcase}" } }
= icon('check')
.description
%strong Start discussion
%p %p
Discuss a specific suggestion or question that needs to be resolved. Add a general comment to this #{noteable_name}.
- else
%li#discussion{ data: { value: 'DiscussionNote', 'button-text' => 'Start discussion', 'secondary-button-text' => "Start discussion & close #{noteable_type.titleize.downcase}"} } %li.divider
%li#discussion{ data: { value: 'DiscussionNote', 'button-text' => 'Start discussion', 'secondary-button-text' => "Start discussion & close #{noteable_name}" } }
= icon('check') = icon('check')
.description .description
%strong Start discussion %strong Start discussion
%p %p
Discuss a specific suggestion or question. = succeed '.' do
Discuss a specific suggestion or question
- if @note.noteable.supports_resolvable_notes?
that needs to be resolved
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.error-alert .error-alert
.note-form-actions.clearfix .note-form-actions.clearfix
= render partial: 'projects/notes/comment_type_button', locals: { show_close_button: true } = render partial: 'projects/notes/comment_button'
= yield(:note_actions) = yield(:note_actions)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment