Commit 5c2f6d7f050222d2601218a0bec1dadcee5fcfa0

Authored by Riyad Preukschas
1 parent 0b3df2f1

Update notes views to support discussions

app/assets/javascripts/notes.js
... ... @@ -20,7 +20,7 @@ var NoteList = {
20 20 // get initial set of notes
21 21 this.getContent();
22 22  
23   - $("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() {
  23 + $("#notes-list, #new-notes-list").on("ajax:success", ".js-note-delete", function() {
24 24 $(this).closest('li').fadeOut(function() {
25 25 $(this).remove();
26 26 NoteList.updateVotes();
... ... @@ -275,16 +275,23 @@ var NoteList = {
275 275 var PerLineNotes = {
276 276 init:
277 277 function() {
  278 + $(".per_line_form .hide-button").on("click", function(){
  279 + $(this).closest(".per_line_form").hide();
  280 + return false;
  281 + });
  282 +
278 283 /**
279 284 * Called when clicking on the "add note" or "reply" button for a diff line.
280 285 *
281 286 * Shows the note form below the line.
282 287 * Sets some hidden fields in the form.
283 288 */
284   - $(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) {
  289 + $(".diff_file_content").on("click", ".js-note-add-to-diff-line", function(e) {
285 290 var form = $(".per_line_form");
286 291 $(this).closest("tr").after(form);
287 292 form.find("#note_line_code").val($(this).data("lineCode"));
  293 + form.find("#note_noteable_type").val($(this).data("noteableType"));
  294 + form.find("#note_noteable_id").val($(this).data("noteableId"));
288 295 form.show();
289 296 e.preventDefault();
290 297 });
... ... @@ -297,7 +304,7 @@ var PerLineNotes = {
297 304 * Removes the actual note from view.
298 305 * Removes the reply button if the last note for that line has been removed.
299 306 */
300   - $(".diff_file_content").on("ajax:success", ".delete-note", function() {
  307 + $(".diff_file_content").on("ajax:success", ".js-note-delete", function() {
301 308 var trNote = $(this).closest("tr");
302 309 trNote.fadeOut(function() {
303 310 $(this).remove();
... ...
app/assets/stylesheets/sections/notes.scss
1 1 /**
2 2 * Notes
3   - *
4 3 */
5 4 #notes-list,
6 5 #new-notes-list {
... ... @@ -8,6 +7,133 @@
8 7 list-style: none;
9 8 margin: 0px;
10 9 padding: 0px;
  10 +
  11 + .discussion-header,
  12 + .note-header {
  13 + @extend .cgray;
  14 + padding-top: 5px;
  15 + padding-bottom: 15px;
  16 +
  17 + .avatar {
  18 + float: left;
  19 + margin-right: 10px;
  20 + }
  21 +
  22 + .discussion-last-update,
  23 + .note-last-update {
  24 + font-style: italic;
  25 + }
  26 + .note-author {
  27 + color: $style_color;
  28 + font-weight: bold;
  29 + &:hover {
  30 + color: $primary_color;
  31 + }
  32 + }
  33 + }
  34 +
  35 + .discussion {
  36 + padding: 8px 0;
  37 + overflow: hidden;
  38 + display: block;
  39 + position:relative;
  40 +
  41 + .discussion-body {
  42 + margin-left: 50px;
  43 +
  44 + .diff_file,
  45 + .discussion-hidden,
  46 + .notes {
  47 + @extend .borders;
  48 + background-color: #F9F9F9;
  49 + }
  50 + .diff_file .note {
  51 + border-bottom: 0px;
  52 + padding: 0px;
  53 + }
  54 + .discussion-hidden .note {
  55 + @extend .cgray;
  56 + padding: 8px;
  57 + text-align: center;
  58 + }
  59 + .notes .note {
  60 + border-color: #ddd;
  61 + padding: 8px;
  62 + }
  63 + }
  64 + }
  65 +
  66 + .note {
  67 + padding: 8px 0;
  68 + overflow: hidden;
  69 + display: block;
  70 + position:relative;
  71 + p { color: $style_color; }
  72 +
  73 + .avatar {
  74 + margin-top:3px;
  75 + }
  76 + .note-body {
  77 + margin-left:45px;
  78 + padding-top: 5px;
  79 + }
  80 + .note-header {
  81 + padding-bottom: 5px;
  82 + }
  83 + }
  84 +}
  85 +
  86 +#notes-list:not(.reversed) .note,
  87 +#notes-list:not(.reversed) .discussion,
  88 +#new-notes-list:not(.reversed) .note,
  89 +#new-notes-list:not(.reversed) .discussion {
  90 + border-bottom: 1px solid #eee;
  91 +}
  92 +#notes-list.reversed .note,
  93 +#notes-list.reversed .discussion,
  94 +#new-notes-list.reversed .note,
  95 +#new-notes-list.reversed .discussion {
  96 + border-top: 1px solid #eee;
  97 +}
  98 +
  99 +
  100 +/**
  101 + * Discussion/Note Actions
  102 + */
  103 +.discussion,
  104 +.note {
  105 + &.note:hover {
  106 + .note-actions { display: block; }
  107 + }
  108 + .discussion-header:hover {
  109 + .discussion-actions { display: block; }
  110 + }
  111 +
  112 + .discussion-actions,
  113 + .note-actions {
  114 + display: none;
  115 + float: right;
  116 +
  117 + [class^="icon-"],
  118 + [class*="icon-"] {
  119 + font-size: 16px;
  120 + line-height: 16px;
  121 + vertical-align: middle;
  122 + }
  123 +
  124 + a {
  125 + @extend .cgray;
  126 +
  127 + &:hover {
  128 + color: $primary_color;
  129 + &.danger { @extend .cred; }
  130 + }
  131 + }
  132 + }
  133 +}
  134 +.diff_file .note .note-actions {
  135 + right: 0;
  136 + top: 0;
11 137 }
12 138  
13 139 .issue_notes,
... ... @@ -18,13 +144,19 @@
18 144 }
19 145 }
20 146  
21   -/* Note textare */
22   -#note_note {
23   - height: 80px;
24   - width: 99%;
25   - font-size: 14px;
  147 +/*
  148 + * New Note Form
  149 + */
  150 +.new_note {
  151 + /* Note textare */
  152 + #note_note {
  153 + height:80px;
  154 + width:99%;
  155 + font-size:14px;
  156 + }
26 157 }
27 158  
  159 +
28 160 #new_note {
29 161 .attach_holder {
30 162 display: none;
... ...
app/controllers/notes_controller.rb
... ... @@ -6,13 +6,15 @@ class NotesController < ProjectResourceController
6 6 respond_to :js
7 7  
8 8 def index
  9 + @target_note = Note.new(noteable_type: params[:target_type].camelize,
  10 + noteable_id: params[:target_id])
  11 + @target = @target_note.noteable
9 12 @notes = Notes::LoadContext.new(project, current_user, params).execute
10 13  
11 14 if params[:target_type] == "merge_request"
12   - @mixed_targets = true
13   - @main_target_type = params[:target_type].camelize
14   - @discussions = discussions_from_notes
15   - @has_diff = true
  15 + @has_diff = true
  16 + @mixed_targets = true
  17 + @discussions = discussions_from_notes
16 18 elsif params[:target_type] == "commit"
17 19 @has_diff = true
18 20 end
... ... @@ -72,6 +74,6 @@ class NotesController < ProjectResourceController
72 74  
73 75 # Helps to distinguish e.g. commit notes in mr notes list
74 76 def for_main_target?(note)
75   - !@mixed_targets || (@main_target_type == note.noteable_type && !note.for_diff_line?)
  77 + !@mixed_targets || (@target.class.name == note.noteable_type && !note.for_diff_line?)
76 78 end
77 79 end
... ...
app/helpers/notes_helper.rb
... ... @@ -9,13 +9,18 @@ module NotesHelper
9 9  
10 10 # Helps to distinguish e.g. commit notes in mr notes list
11 11 def note_for_main_target?(note)
12   - !@mixed_targets || (@main_target_type == note.noteable_type && !note.for_diff_line?)
  12 + !@mixed_targets || (@target.class.name == note.noteable_type && !note.for_diff_line?)
13 13 end
14 14  
15 15 def link_to_commit_diff_line_note(note)
16 16 if note.for_commit_diff_line?
17 17 link_to "#{note.diff_file_name}:L#{note.diff_new_line}", project_commit_path(@project, note.noteable, anchor: note.line_code)
18 18 end
  19 + end
19 20  
  21 + def link_to_merge_request_diff_line_note(note)
  22 + if note.for_merge_request_diff_line?
  23 + link_to "#{note.diff_file_name}:L#{note.diff_new_line}", diffs_project_merge_request_path(note.project, note.noteable_id, anchor: note.line_code)
  24 + end
20 25 end
21 26 end
... ...
app/models/note.rb
... ... @@ -79,7 +79,7 @@ class Note < ActiveRecord::Base
79 79 end
80 80  
81 81 def discussion_id
82   - @discussion_id ||= [noteable_type, noteable_id, line_code].join.underscore.to_sym
  82 + @discussion_id ||= [:discussion, noteable_type.underscore, noteable_id, line_code].join("-").to_sym
83 83 end
84 84  
85 85 # Returns true if this is a downvote note,
... ...
app/views/merge_requests/diffs.html.haml
1 1 = render "show"
2   -
3   -:javascript
4   - $(function(){
5   - PerLineNotes.init();
6   - });
... ...
app/views/notes/_discussion.html.haml 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +- note = discussion_notes.first
  2 +.discussion.js-details-container.js-toggler-container.open{ class: note.discussion_id }
  3 + .discussion-header
  4 + .discussion-actions
  5 + = link_to "javascript:;", class: "js-details-target turn-on js-toggler-target" do
  6 + %i.icon-eye-close
  7 + Hide discussion
  8 + = link_to "javascript:;", class: "js-details-target turn-off js-toggler-target" do
  9 + %i.icon-eye-open
  10 + Show discussion
  11 + = image_tag gravatar_icon(note.author.email), class: "avatar s32"
  12 + %div
  13 + = link_to note.author_name, project_team_member_path(@project, @project.team_member_by_id(note.author)), class: "note-author"
  14 + - if note.for_merge_request?
  15 + started a discussion on this merge request diff
  16 + = link_to_merge_request_diff_line_note(note)
  17 + - elsif note.for_commit?
  18 + started a discussion on commit
  19 + #{link_to note.noteable.short_id, project_commit_path(@project, note.noteable)}
  20 + = link_to_commit_diff_line_note(note) if note.for_diff_line?
  21 + - else
  22 + %cite.cgray started a discussion
  23 + %div
  24 + - if discussion_notes.size > 1
  25 + - last_note = discussion_notes.last
  26 + last updated by
  27 + = link_to last_note.author_name, project_team_member_path(@project, @project.team_member_by_id(last_note.author)), class: "note-author"
  28 + %span.discussion-last-update
  29 + = time_ago_in_words(last_note.updated_at)
  30 + ago
  31 + .discussion-body
  32 + - if note.for_diff_line?
  33 + .diff_file.content
  34 + = render "notes/discussion_diff", discussion_notes: discussion_notes, note: note
  35 + - else
  36 + .notes.content
  37 + = render discussion_notes
  38 +
  39 + -# will be shown when the other one is hidden
  40 + .discussion-hidden.content.hide
  41 + .note
  42 + %em Hidden discussion.
  43 + = link_to "javascript:;", class: "js-details-target js-toggler-target" do
  44 + %i.icon-eye-open
  45 + Show
  46 +
... ...
app/views/notes/_discussion_diff.html.haml 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +- diff = note.diff
  2 +.diff_file_header
  3 + %i.icon-file
  4 + - if diff.deleted_file
  5 + %span{id: "#{diff.a_path}"}= diff.a_path
  6 + - else
  7 + %span{id: "#{diff.b_path}"}= diff.b_path
  8 + %br/
  9 +.diff_file_content
  10 + %table
  11 + - each_diff_line(diff.diff.lines.to_a, note.diff_file_index) do |line, type, line_code, line_new, line_old|
  12 + %tr.line_holder{ id: line_code }
  13 + - if type == "match"
  14 + %td.old_line= "..."
  15 + %td.new_line= "..."
  16 + %td.line_content.matched= line
  17 + - else
  18 + %td.old_line= raw(type == "new" ? " " : line_old)
  19 + %td.new_line= raw(type == "old" ? " " : line_new)
  20 + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line}  "
  21 +
  22 + - if line_code == note.line_code
  23 + = render "notes/per_line_notes_with_reply", notes: discussion_notes
  24 + - break # cut off diff after notes
... ...
app/views/notes/_note.html.haml
1   -%li{id: dom_id(note), class: "note"}
2   - = image_tag gravatar_icon(note.author.email), class: "avatar s32"
3   - %div.note-author
4   - %strong= note.author_name
5   - = link_to "##{dom_id(note)}", name: dom_id(note) do
6   - %cite.cgray
7   - = time_ago_in_words(note.updated_at)
8   - ago
9   -
10   - - unless note_for_main_target?(note)
11   - - if note.for_commit?
12   - %span.cgray
13   - on #{link_to note.noteable.short_id, project_commit_path(@project, note.noteable)}
14   - = link_to_commit_diff_line_note(note) if note.for_diff_line?
  1 +%li{ id: dom_id(note), class: dom_class(note), data: { discussion: note.discussion_id } }
  2 + .note-header
  3 + .note-actions
  4 + = link_to "##{dom_id(note)}", name: dom_id(note) do
  5 + %i.icon-link
  6 + Link here
  7 +  
  8 + - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
  9 + = link_to project_note_path(@project, note), method: :delete, confirm: 'Are you sure?', remote: true, class: "danger js-note-delete" do
  10 + %i.icon-remove-circle
  11 + = image_tag gravatar_icon(note.author.email), class: "avatar s32"
  12 + = link_to note.author_name, project_team_member_path(@project, @project.team_member_by_id(note.author)), class: "note-author"
  13 + %span.note-last-update
  14 + = time_ago_in_words(note.updated_at)
  15 + ago
15 16  
16 17 -# only show vote if it's a note for the main target
17 18 - if note_for_main_target?(note)
... ... @@ -24,13 +25,8 @@
24 25 %i.icon-thumbs-down
25 26 \-1
26 27  
27   - -# remove button
28   - - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
29   - = link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do
30   - %i.icon-trash
31   - Remove
32 28  
33   - %div.note-title
  29 + .note-body
34 30 = preserve do
35 31 = markdown(note.note)
36 32 - if note.attachment.url
... ...
app/views/notes/_notes.html.haml
1   -- @notes.each do |note|
2   - - next unless note.author
3   - = render "note", note: note
4   -
  1 +- if @discussions.present?
  2 + - @discussions.each do |discussion_notes|
  3 + - note = discussion_notes.first
  4 + - if note_for_main_target?(note)
  5 + = render discussion_notes
  6 + - else
  7 + = render 'discussion', discussion_notes: discussion_notes
  8 +- else
  9 + - @notes.each do |note|
  10 + - next unless note.author
  11 + = render 'note', note: note
... ...
app/views/notes/_per_line_form.html.haml
... ... @@ -17,7 +17,7 @@
17 17 .note_actions
18 18 .buttons
19 19 = f.submit 'Add Comment', class: "btn save-btn submit_note submit_inline_note", id: "submit_note"
20   - = link_to "Cancel", "#", class: "btn hide-button"
  20 + = link_to "Cancel", "javascript:;", class: "btn hide-button"
21 21 .options
22 22 %h6.left Notify via email:
23 23 .labels
... ... @@ -29,11 +29,3 @@
29 29 = label_tag :notify_author do
30 30 = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
31 31 %span Commit author
32   -
33   -:javascript
34   - $(function(){
35   - $(".per_line_form .hide-button").bind("click", function(){
36   - $('.per_line_form').hide();
37   - return false;
38   - });
39   - });
... ...
app/views/notes/_per_line_note_link.html.haml
1 1 = link_to "",
2 2 "#",
3   - id: "line-note-#{line_code}",
4   - class: "line_note_link",
  3 + id: "add-diff-line-note-#{line_code}",
  4 + class: "line_note_link js-note-add-to-diff-line",
5 5 data: @comments_target.merge({ line_code: line_code }),
6   - title: "Add comment on line #{line_code[/[0-9]+$/]}"
  6 + title: "Add a comment to this line"
... ...
app/views/notes/_per_line_reply_button.html.haml
1 1 %tr.line_notes_row.reply
2 2 %td{colspan: 3}
3   - = link_to "#",
4   - class: "line_note_reply_link",
  3 + = link_to "javascript:;",
  4 + class: "line_note_reply_link js-note-add-to-diff-line",
5 5 data: { line_code: note.line_code,
6 6 noteable_type: note.noteable_type,
7 7 noteable_id: note.noteable_id },
8   - title: "Add note for this line" do
  8 + title: "Add a comment to this line" do
9 9 %i.icon-comment
10 10 Reply
... ...
app/views/notes/index.js.haml
... ... @@ -15,3 +15,7 @@
15 15 - if loading_more_notes?
16 16 :plain
17 17 NoteList.finishedLoadingMore();
  18 +
  19 +- if @has_diff
  20 + :plain
  21 + PerLineNotes.init();
... ...