Commit caeb65b1892c8a140d8f72f6aafa3f5fd1d3cbc3

Authored by randx
2 parents a82977c6 07eec9c6

Merge branch 'change-notes-order' of https://github.com/riyad/gitlabhq into riyad-change-notes-order

app/assets/javascripts/note.js
... ... @@ -1,182 +0,0 @@
1   -var NoteList = {
2   -
3   - notes_path: null,
4   - target_params: null,
5   - target_id: 0,
6   - target_type: null,
7   - first_id: 0,
8   - last_id: 0,
9   - disable:false,
10   -
11   - init:
12   - function(tid, tt, path) {
13   - this.notes_path = path + ".js";
14   - this.target_id = tid;
15   - this.target_type = tt;
16   - this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
17   -
18   - // get notes
19   - this.getContent();
20   -
21   - // get new notes every n seconds
22   - this.initRefresh();
23   -
24   - $('.delete-note').live('ajax:success', function() {
25   - $(this).closest('li').fadeOut(); });
26   -
27   - $(".note-form-holder").live("ajax:before", function(){
28   - $(".submit_note").disable()
29   - })
30   -
31   - $(".note-form-holder").live("ajax:complete", function(){
32   - $(".submit_note").enable()
33   - })
34   -
35   - disableButtonIfEmptyField(".note-text", ".submit_note");
36   -
37   - $(".note-text").live("focus", function(){
38   - $(this).css("height", "80px");
39   - $('.note_advanced_opts').show();
40   - });
41   -
42   - $("#note_attachment").change(function(e){
43   - var val = $('.input-file').val();
44   - var filename = val.replace(/^.*[\\\/]/, '');
45   - $(".file_name").text(filename);
46   - });
47   -
48   - },
49   -
50   -
51   - /**
52   - * Load new notes to fresh list called 'new_notes_list':
53   - * - Replace 'new_notes_list' with new list every n seconds
54   - * - Append new notes to this list after submit
55   - */
56   -
57   - initRefresh:
58   - function() {
59   - // init timer
60   - var intNew = setInterval("NoteList.getNew()", 10000);
61   - },
62   -
63   - replace:
64   - function(html) {
65   - $("#new_notes_list").html(html);
66   - },
67   -
68   - prepend:
69   - function(id, html) {
70   - if(id != this.last_id) {
71   - $("#new_notes_list").prepend(html);
72   - }
73   - },
74   -
75   - getNew:
76   - function() {
77   - // refersh notes list
78   - $.ajax({
79   - type: "GET",
80   - url: this.notes_path,
81   - data: "last_id=" + this.last_id + this.target_params,
82   - dataType: "script"});
83   - },
84   -
85   - refresh:
86   - function() {
87   - // refersh notes list
88   - $.ajax({
89   - type: "GET",
90   - url: this.notes_path,
91   - data: "first_id=" + this.first_id + "&last_id=" + this.last_id + this.target_params,
92   - dataType: "script"});
93   - },
94   -
95   -
96   - /**
97   - * Init load of notes:
98   - * 1. Get content with ajax call
99   - * 2. Set content of notes list with loaded one
100   - */
101   -
102   -
103   - getContent:
104   - function() {
105   - $.ajax({
106   - type: "GET",
107   - url: this.notes_path,
108   - data: "?" + this.target_params,
109   - complete: function(){ $('.status').removeClass("loading")},
110   - beforeSend: function() { $('.status').addClass("loading") },
111   - dataType: "script"});
112   - },
113   -
114   - setContent:
115   - function(fid, lid, html) {
116   - this.last_id = lid;
117   - this.first_id = fid;
118   - $("#notes-list").html(html);
119   -
120   - // Init infinite scrolling
121   - this.initLoadMore();
122   - },
123   -
124   -
125   - /**
126   - * Paging for old notes when scroll to bottom:
127   - * 1. Init scroll events with 'initLoadMore'
128   - * 2. Load onlder notes with 'getOld' method
129   - * 3. append old notes to bottom of list with 'append'
130   - *
131   - */
132   - getOld:
133   - function() {
134   - $('.loading').show();
135   - $.ajax({
136   - type: "GET",
137   - url: this.notes_path,
138   - data: "first_id=" + this.first_id + this.target_params,
139   - complete: function(){ $('.status').removeClass("loading")},
140   - beforeSend: function() { $('.status').addClass("loading") },
141   - dataType: "script"});
142   - },
143   -
144   - append:
145   - function(id, html) {
146   - if(this.first_id == id) {
147   - this.disable = true;
148   - } else {
149   - this.first_id = id;
150   - $("#notes-list").append(html);
151   - }
152   - },
153   -
154   - initLoadMore:
155   - function() {
156   - $(document).endlessScroll({
157   - bottomPixels: 400,
158   - fireDelay: 1000,
159   - fireOnce:true,
160   - ceaseFire: function() {
161   - return NoteList.disable;
162   - },
163   - callback: function(i) {
164   - NoteList.getOld();
165   - }
166   - });
167   - }
168   -};
169   -
170   -var PerLineNotes = {
171   - init:
172   - function() {
173   - $(".line_note_link, .line_note_reply_link").live("click", function(e) {
174   - var form = $(".per_line_form");
175   - $(this).closest("tr").after(form);
176   - form.find("#note_line_code").val($(this).attr("line_code"));
177   - form.show();
178   - return false;
179   - });
180   - disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
181   - }
182   -}
app/assets/javascripts/notes.js 0 → 100644
... ... @@ -0,0 +1,251 @@
  1 +var NoteList = {
  2 +
  3 + notes_path: null,
  4 + target_params: null,
  5 + target_id: 0,
  6 + target_type: null,
  7 + top_id: 0,
  8 + bottom_id: 0,
  9 + loading_more_disabled: false,
  10 + reversed: false,
  11 +
  12 + init:
  13 + function(tid, tt, path) {
  14 + this.notes_path = path + ".js";
  15 + this.target_id = tid;
  16 + this.target_type = tt;
  17 + this.reversed = $("#notes-list").hasClass("reversed");
  18 + this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
  19 +
  20 + // get initial set of notes
  21 + this.getContent();
  22 +
  23 + $("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() {
  24 + $(this).closest('li').fadeOut();
  25 + });
  26 +
  27 + $(".note-form-holder").on("ajax:before", function(){
  28 + $(".submit_note").disable()
  29 + })
  30 +
  31 + $(".note-form-holder").on("ajax:complete", function(){
  32 + $(".submit_note").enable()
  33 + })
  34 +
  35 + disableButtonIfEmptyField(".note-text", ".submit_note");
  36 +
  37 + $(".note-text").on("focus", function(){
  38 + $(this).css("height", "80px");
  39 + $('.note_advanced_opts').show();
  40 + });
  41 +
  42 + $("#note_attachment").change(function(e){
  43 + var val = $('.input-file').val();
  44 + var filename = val.replace(/^.*[\\\/]/, '');
  45 + $(".file_name").text(filename);
  46 + });
  47 + },
  48 +
  49 +
  50 + /**
  51 + * Handle loading the initial set of notes.
  52 + * And set up loading more notes when scrolling to the bottom of the page.
  53 + */
  54 +
  55 +
  56 + /**
  57 + * Gets an inital set of notes.
  58 + */
  59 + getContent:
  60 + function() {
  61 + $.ajax({
  62 + type: "GET",
  63 + url: this.notes_path,
  64 + data: "?" + this.target_params,
  65 + complete: function(){ $('.notes-status').removeClass("loading")},
  66 + beforeSend: function() { $('.notes-status').addClass("loading") },
  67 + dataType: "script"});
  68 + },
  69 +
  70 + /**
  71 + * Called in response to getContent().
  72 + * Replaces the content of #notes-list with the given html.
  73 + */
  74 + setContent:
  75 + function(first_id, last_id, html) {
  76 + this.top_id = first_id;
  77 + this.bottom_id = last_id;
  78 + $("#notes-list").html(html);
  79 +
  80 + // init infinite scrolling
  81 + this.initLoadMore();
  82 +
  83 + // init getting new notes
  84 + if (this.reversed) {
  85 + this.initRefreshNew();
  86 + }
  87 + },
  88 +
  89 +
  90 + /**
  91 + * Handle loading more notes when scrolling to the bottom of the page.
  92 + * The id of the last note in the list is in this.bottom_id.
  93 + *
  94 + * Set up refreshing only new notes after all notes have been loaded.
  95 + */
  96 +
  97 +
  98 + /**
  99 + * Initializes loading more notes when scrolling to the bottom of the page.
  100 + */
  101 + initLoadMore:
  102 + function() {
  103 + $(document).endlessScroll({
  104 + bottomPixels: 400,
  105 + fireDelay: 1000,
  106 + fireOnce:true,
  107 + ceaseFire: function() {
  108 + return NoteList.loading_more_disabled;
  109 + },
  110 + callback: function(i) {
  111 + NoteList.getMore();
  112 + }
  113 + });
  114 + },
  115 +
  116 + /**
  117 + * Gets an additional set of notes.
  118 + */
  119 + getMore:
  120 + function() {
  121 + // only load more notes if there are no "new" notes
  122 + $('.loading').show();
  123 + $.ajax({
  124 + type: "GET",
  125 + url: this.notes_path,
  126 + data: "loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id + this.target_params,
  127 + complete: function(){ $('.notes-status').removeClass("loading")},
  128 + beforeSend: function() { $('.notes-status').addClass("loading") },
  129 + dataType: "script"});
  130 + },
  131 +
  132 + /**
  133 + * Called in response to getMore().
  134 + * Append notes to #notes-list.
  135 + */
  136 + appendMoreNotes:
  137 + function(id, html) {
  138 + if(id != this.bottom_id) {
  139 + this.bottom_id = id;
  140 + $("#notes-list").append(html);
  141 + }
  142 + },
  143 +
  144 + /**
  145 + * Called in response to getMore().
  146 + * Disables loading more notes when scrolling to the bottom of the page.
  147 + * Initalizes refreshing new notes.
  148 + */
  149 + finishedLoadingMore:
  150 + function() {
  151 + this.loading_more_disabled = true;
  152 +
  153 + // from now on only get new notes
  154 + if (!this.reversed) {
  155 + this.initRefreshNew();
  156 + }
  157 + },
  158 +
  159 +
  160 + /**
  161 + * Handle refreshing and adding of new notes.
  162 + *
  163 + * New notes are all notes that are created after the site has been loaded.
  164 + * The "old" notes are in #notes-list the "new" ones will be in #new-notes-list.
  165 + * The id of the last "old" note is in this.bottom_id.
  166 + */
  167 +
  168 +
  169 + /**
  170 + * Initializes getting new notes every n seconds.
  171 + */
  172 + initRefreshNew:
  173 + function() {
  174 + setInterval("NoteList.getNew()", 10000);
  175 + },
  176 +
  177 + /**
  178 + * Gets the new set of notes.
  179 + */
  180 + getNew:
  181 + function() {
  182 + $.ajax({
  183 + type: "GET",
  184 + url: this.notes_path,
  185 + data: "loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id) + this.target_params,
  186 + dataType: "script"});
  187 + },
  188 +
  189 + /**
  190 + * Called in response to getNew().
  191 + * Replaces the content of #new-notes-list with the given html.
  192 + */
  193 + replaceNewNotes:
  194 + function(html) {
  195 + $("#new-notes-list").html(html);
  196 + },
  197 +
  198 + /**
  199 + * Adds a single note to #new-notes-list.
  200 + */
  201 + appendNewNote:
  202 + function(id, html) {
  203 + if (this.reversed) {
  204 + $("#new-notes-list").prepend(html);
  205 + } else {
  206 + $("#new-notes-list").append(html);
  207 + }
  208 + }
  209 +};
  210 +
  211 +var PerLineNotes = {
  212 + init:
  213 + function() {
  214 + /**
  215 + * Called when clicking on the "add note" or "reply" button for a diff line.
  216 + *
  217 + * Shows the note form below the line.
  218 + * Sets some hidden fields in the form.
  219 + */
  220 + $(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) {
  221 + var form = $(".per_line_form");
  222 + $(this).closest("tr").after(form);
  223 + form.find("#note_line_code").val($(this).data("lineCode"));
  224 + form.show();
  225 + return false;
  226 + });
  227 +
  228 + disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
  229 +
  230 + /**
  231 + * Called in response to successfully deleting a note on a diff line.
  232 + *
  233 + * Removes the actual note from view.
  234 + * Removes the reply button if the last note for that line has been removed.
  235 + */
  236 + $(".diff_file_content").on("ajax:success", ".delete-note", function() {
  237 + var trNote = $(this).closest("tr");
  238 + trNote.fadeOut(function() {
  239 + $(this).remove();
  240 + });
  241 +
  242 + // check if this is the last note for this line
  243 + // elements must really be removed for this to work reliably
  244 + var trLine = trNote.prev();
  245 + var trRpl = trNote.next();
  246 + if (trLine.hasClass("line_holder") && trRpl.hasClass("reply")) {
  247 + trRpl.fadeOut(function() { $(this).remove(); });
  248 + }
  249 + });
  250 + }
  251 +}
... ...
app/assets/stylesheets/sections/notes.scss
... ... @@ -3,14 +3,17 @@
3 3 *
4 4 */
5 5 #notes-list,
6   -#new_notes_list {
  6 +#new-notes-list {
7 7 display:block;
8 8 list-style:none;
9 9 margin:0px;
10 10 padding:0px;
11 11 }
12 12  
13   -#new_notes_list li:last-child{
  13 +#new-notes-list:not(.reversed) {
  14 + border-top:1px solid #aaa;
  15 +}
  16 +#new-notes-list.reversed {
14 17 border-bottom:1px solid #aaa;
15 18 }
16 19  
... ... @@ -48,7 +51,6 @@
48 51  
49 52 .note {
50 53 padding: 8px 0;
51   - border-bottom: 1px solid #eee;
52 54 overflow: hidden;
53 55 display: block;
54 56 img {float: left; margin-right: 10px;}
... ... @@ -70,6 +72,18 @@
70 72 .delete-note { display:block; }
71 73 }
72 74 }
  75 +#notes-list:not(.reversed) .note,
  76 +#new-notes-list:not(.reversed) .note {
  77 + border-bottom: 1px solid #eee;
  78 +}
  79 +#notes-list.reversed .note,
  80 +#new-notes-list.reversed .note {
  81 + border-top: 1px solid #eee;
  82 +}
  83 +
  84 +.notes-status {
  85 + margin: 18px;
  86 +}
73 87  
74 88  
75 89 p.notify_controls input{
... ...
app/contexts/notes/load_context.rb
... ... @@ -3,30 +3,31 @@ module Notes
3 3 def execute
4 4 target_type = params[:target_type]
5 5 target_id = params[:target_id]
6   - first_id = params[:first_id]
7   - last_id = params[:last_id]
  6 + after_id = params[:after_id]
  7 + before_id = params[:before_id]
8 8  
9 9  
10 10 @notes = case target_type
11   - when "commit"
12   - then project.commit_notes(project.commit(target_id)).fresh.limit(20)
13   - when "snippet"
14   - then project.snippets.find(target_id).notes
15   - when "wall"
16   - then project.common_notes.order("created_at DESC").fresh.limit(50)
  11 + when "commit"
  12 + project.commit_notes(project.commit(target_id)).fresh.limit(20)
17 13 when "issue"
18   - then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
  14 + project.issues.find(target_id).notes.inc_author.fresh.limit(20)
19 15 when "merge_request"
20   - then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
  16 + project.merge_requests.find(target_id).notes.inc_author.fresh.limit(20)
  17 + when "snippet"
  18 + project.snippets.find(target_id).notes.fresh
  19 + when "wall"
  20 + # this is the only case, where the order is DESC
  21 + project.common_notes.order("created_at DESC, id DESC").limit(50)
21 22 when "wiki"
22   - then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20]
  23 + project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20]
23 24 end
24 25  
25   - @notes = if last_id
26   - @notes.where("id > ?", last_id)
27   - elsif first_id
28   - @notes.where("id < ?", first_id)
29   - else
  26 + @notes = if after_id
  27 + @notes.where("id > ?", after_id)
  28 + elsif before_id
  29 + @notes.where("id < ?", before_id)
  30 + else
30 31 @notes
31 32 end
32 33 end
... ...
app/helpers/notes_helper.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +module NotesHelper
  2 + def loading_more_notes?
  3 + params[:loading_more].present?
  4 + end
  5 +
  6 + def loading_new_notes?
  7 + params[:loading_new].present?
  8 + end
  9 +end
... ...
app/models/note.rb
... ... @@ -36,7 +36,7 @@ class Note &lt; ActiveRecord::Base
36 36 scope :today, where("created_at >= :date", date: Date.today)
37 37 scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
38 38 scope :since, lambda { |day| where("created_at >= :date", date: (day)) }
39   - scope :fresh, order("created_at DESC")
  39 + scope :fresh, order("created_at ASC, id ASC")
40 40 scope :inc_author_project, includes(:project, :author)
41 41 scope :inc_author, includes(:author)
42 42  
... ...
app/views/commits/_text_file.html.haml
... ... @@ -13,14 +13,11 @@
13 13 %td.old_line
14 14 = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
15 15 - if @comments_allowed
16   - = link_to "", "#", class: "line_note_link", "line_code" => line_code, title: "Add note for this line"
  16 + = render "notes/per_line_note_link", line_code: line_code
17 17 %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
18 18 %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;"
19 19  
20 20 - if @comments_allowed
21   - - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at).reverse
  21 + - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
22 22 - unless comments.empty?
23   - - comments.each_with_index do |note, i|
24   - = render "notes/reply_button", line_code: line_code if i.zero?
25   - = render "notes/per_line_show", note: note
26   - - @line_notes.reject!{ |n| n == note }
  23 + = render "notes/per_line_notes_with_reply", notes: comments
... ...
app/views/commits/show.html.haml
1 1 = render "commits/commit_box"
2 2 = render "commits/diffs", diffs: @commit.diffs
3   -= render "notes/notes", tid: @commit.id, tt: "commit"
  3 += render "notes/notes_with_form", tid: @commit.id, tt: "commit"
4 4 = render "notes/per_line_form"
5 5  
6 6  
... ...
app/views/issues/show.html.haml
... ... @@ -61,4 +61,4 @@
61 61 = markdown @issue.description
62 62  
63 63  
64   -.issue_notes#notes= render "notes/notes", tid: @issue.id, tt: "issue"
  64 +.issue_notes#notes= render "notes/notes_with_form", tid: @issue.id, tt: "issue"
... ...
app/views/merge_requests/_show.html.haml
... ... @@ -16,7 +16,7 @@
16 16 Diff
17 17  
18 18 .merge_request_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
19   - = render("notes/notes", tid: @merge_request.id, tt: "merge_request")
  19 + = render("notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")
20 20 .merge-request-diffs
21 21 = render "merge_requests/show/diffs" if @diffs
22 22 .status
... ...
app/views/merge_requests/show.js.haml
1 1 :plain
2   - $(".merge-request-notes").html("#{escape_javascript(render("notes/notes", tid: @merge_request.id, tt: "merge_request"))}");
  2 + $(".merge-request-notes").html("#{escape_javascript(render notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")}");
... ...
app/views/notes/_common_form.html.haml 0 → 100644
... ... @@ -0,0 +1,39 @@
  1 +.note-form-holder
  2 + = form_for [@project, @note], remote: "true", multipart: true do |f|
  3 + %h3.page_title Leave a comment
  4 + -if @note.errors.any?
  5 + .alert-message.block-message.error
  6 + - @note.errors.full_messages.each do |msg|
  7 + %div= msg
  8 +
  9 + = f.hidden_field :noteable_id
  10 + = f.hidden_field :noteable_type
  11 + = f.text_area :note, size: 255, class: 'note-text'
  12 + #preview-note.preview_note.hide
  13 + .hint
  14 + .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
  15 + .clearfix
  16 +
  17 + .row.note_advanced_opts.hide
  18 + .span3
  19 + = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"
  20 + = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'
  21 + .span4.notify_opts
  22 + %h6.left Notify via email:
  23 + = label_tag :notify do
  24 + = check_box_tag :notify, 1, @note.noteable_type != "Commit"
  25 + %span Project team
  26 +
  27 + - if @note.notify_only_author?(current_user)
  28 + = label_tag :notify_author do
  29 + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
  30 + %span Commit author
  31 + .span5.attachments
  32 + %h6.left Attachment:
  33 + %span.file_name File name...
  34 +
  35 + .input.input_file
  36 + %a.file_upload.btn.small Upload File
  37 + = f.file_field :attachment, class: "input-file"
  38 + %span.hint Any file less than 10 MB
  39 +
... ...
app/views/notes/_create_common.js.haml
... ... @@ -1,12 +0,0 @@
1   -- if note.valid?
2   - :plain
3   - $(".note-form-holder .error").remove();
4   - $('.note-form-holder textarea').val("");
5   - $('.note-form-holder #preview-link').text('Preview');
6   - $('.note-form-holder #preview-note').hide();
7   - $('.note-form-holder').show();
8   - NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}");
9   -- else
10   - :plain
11   - $(".note-form-holder").replaceWith("#{escape_javascript(render('form'))}");
12   -
app/views/notes/_create_common_note.js.haml 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +- if note.valid?
  2 + :plain
  3 + $(".note-form-holder .error").remove();
  4 + $('.note-form-holder textarea').val("");
  5 + $('.note-form-holder #preview-link').text('Preview');
  6 + $('.note-form-holder #preview-note').hide();
  7 + $('.note-form-holder').show();
  8 + NoteList.appendNewNote(#{note.id}, "#{escape_javascript(render "notes/note", note: note)}");
  9 +
  10 +- else
  11 + :plain
  12 + $(".note-form-holder").replaceWith("#{escape_javascript(render 'form')}");
  13 +
... ...
app/views/notes/_create_line.js.haml
... ... @@ -1,8 +0,0 @@
1   -- if note.valid?
2   - :plain
3   - $(".per_line_form").hide();
4   - $('.line-note-form-holder textarea').val("");
5   - $("a.line_note_reply_link[line_code='#{note.line_code}']").closest("tr").remove();
6   - var trEl = $(".#{note.line_code}").parent();
7   - trEl.after("#{escape_javascript(render partial: "notes/per_line_show", locals: {note: note})}");
8   - trEl.after("#{escape_javascript(render partial: "notes/reply_button", locals: {line_code: note.line_code})}");
app/views/notes/_create_per_line_note.js.haml 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +- if note.valid?
  2 + :plain
  3 + // hide and reset the form
  4 + $(".per_line_form").hide();
  5 + $('.line-note-form-holder textarea').val("");
  6 +
  7 + // find the reply button for this line
  8 + // (might not be there if this is the first note)
  9 + var trRpl = $("a.line_note_reply_link[data-line-code='#{note.line_code}']").closest("tr");
  10 + if (trRpl.size() == 0) {
  11 + // find the commented line ...
  12 + var trEl = $(".#{note.line_code}").parent();
  13 + // ... and insert the note and the reply button after it
  14 + trEl.after("#{escape_javascript(render "notes/per_line_reply_button", line_code: note.line_code)}");
  15 + trEl.after("#{escape_javascript(render "notes/per_line_note", note: note)}");
  16 + } else {
  17 + // instert new note before reply button
  18 + trRpl.before("#{escape_javascript(render "notes/per_line_note", note: note)}");
  19 + }
... ...
app/views/notes/_form.html.haml
... ... @@ -1,39 +0,0 @@
1   -.note-form-holder
2   - = form_for [@project, @note], remote: "true", multipart: true do |f|
3   - %h3.page_title Leave a comment
4   - -if @note.errors.any?
5   - .alert-message.block-message.error
6   - - @note.errors.full_messages.each do |msg|
7   - %div= msg
8   -
9   - = f.hidden_field :noteable_id
10   - = f.hidden_field :noteable_type
11   - = f.text_area :note, size: 255, class: 'note-text'
12   - #preview-note.preview_note.hide
13   - .hint
14   - .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
15   - .clearfix
16   -
17   - .row.note_advanced_opts.hide
18   - .span3
19   - = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"
20   - = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'
21   - .span4.notify_opts
22   - %h6.left Notify via email:
23   - = label_tag :notify do
24   - = check_box_tag :notify, 1, @note.noteable_type != "Commit"
25   - %span Project team
26   -
27   - - if @note.notify_only_author?(current_user)
28   - = label_tag :notify_author do
29   - = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
30   - %span Commit author
31   - .span5.attachments
32   - %h6.left Attachment:
33   - %span.file_name File name...
34   -
35   - .input.input_file
36   - %a.file_upload.btn.small Upload File
37   - = f.file_field :attachment, class: "input-file"
38   - %span.hint Any file less than 10 MB
39   -
app/views/notes/_load.js.haml
... ... @@ -1,17 +0,0 @@
1   -- unless @notes.blank?
2   - - if params[:last_id]
3   - :plain
4   - NoteList.replace("#{escape_javascript(render(partial: 'notes/notes_list'))}");
5   -
6   - - elsif params[:first_id]
7   - :plain
8   - NoteList.append(#{@notes.last.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
9   -
10   - - else
11   - :plain
12   - NoteList.setContent(#{@notes.last.id}, #{@notes.first.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
13   -
14   -- else
15   - - if params[:first_id]
16   - :plain
17   - NoteList.append(#{params[:first_id]}, "");
app/views/notes/_note.html.haml 0 → 100644
... ... @@ -0,0 +1,21 @@
  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 + - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
  10 + = link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do
  11 + %i.icon-trash
  12 + Remove
  13 +
  14 + %div.note-title
  15 + = preserve do
  16 + = markdown(note.note)
  17 + - if note.attachment.url
  18 + .right
  19 + %div.file
  20 + = link_to note.attachment_identifier, note.attachment.url, target: "_blank"
  21 + .clear
... ...
app/views/notes/_notes.html.haml
1   -- if can? current_user, :write_note, @project
2   - = render "notes/form"
3   -.clear
4   -%hr
5   -%ul#new_notes_list
6   -%ul#notes-list
7   -.status
  1 +- @notes.each do |note|
  2 + - next unless note.author
  3 + = render "note", note: note
8 4  
9   -
10   -:javascript
11   - $(function(){
12   - NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
13   - });
... ...
app/views/notes/_notes_list.html.haml
... ... @@ -1,4 +0,0 @@
1   -- @notes.each do |note|
2   - - next unless note.author
3   - = render partial: "notes/show", locals: {note: note}
4   -
app/views/notes/_notes_with_form.html.haml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +%ul#notes-list
  2 +%ul#new-notes-list
  3 +.notes-status
  4 +
  5 +- if can? current_user, :write_note, @project
  6 + = render "notes/common_form"
  7 +
  8 +:javascript
  9 + $(function(){
  10 + NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
  11 + });
... ...
app/views/notes/_per_line_note.html.haml 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +%tr.line_notes_row
  2 + %td{colspan: 3}
  3 + %ul
  4 + = render "notes/note", note: note
  5 +
... ...
app/views/notes/_per_line_note_link.html.haml 0 → 100644
... ... @@ -0,0 +1 @@
  1 += link_to "", "#", class: "line_note_link", data: { line_code: line_code }, title: "Add note for this line"
... ...
app/views/notes/_per_line_notes_with_reply.html.haml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +- notes.each do |note|
  2 + = render "notes/per_line_note", note: note
  3 += render "notes/per_line_reply_button", line_code: notes.first.line_code
... ...
app/views/notes/_per_line_reply_button.html.haml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +%tr.line_notes_row.reply
  2 + %td{colspan: 3}
  3 + %i.icon-comment
  4 + = link_to "Reply", "#", class: "line_note_reply_link", data: { line_code: line_code }, title: "Add note for this line"
... ...
app/views/notes/_per_line_show.html.haml
... ... @@ -1,5 +0,0 @@
1   -%tr.line_notes_row
2   - %td{colspan: 3}
3   - %ul
4   - = render partial: "notes/show", locals: {note: note}
5   -
app/views/notes/_reply_button.html.haml
... ... @@ -1,4 +0,0 @@
1   -%tr.line_notes_row.reply
2   - %td{colspan: 3}
3   - %i.icon-comment
4   - = link_to "Reply", "#", class: "line_note_reply_link", "line_code" => line_code, title: "Add note for this line"
app/views/notes/_reversed_notes_with_form.html.haml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +- if can? current_user, :write_note, @project
  2 + = render "notes/common_form"
  3 +
  4 +%ul.reversed#new-notes-list
  5 +%ul.reversed#notes-list
  6 +.notes-status
  7 +
  8 +:javascript
  9 + $(function(){
  10 + NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
  11 + });
0 12 \ No newline at end of file
... ...
app/views/notes/_show.html.haml
... ... @@ -1,21 +0,0 @@
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   - - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
10   - = link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do
11   - %i.icon-trash
12   - Remove
13   -
14   - %div.note-title
15   - = preserve do
16   - = markdown(note.note)
17   - - if note.attachment.url
18   - .right
19   - %div.file
20   - = link_to note.attachment_identifier, note.attachment.url, target: "_blank"
21   - .clear
app/views/notes/create.js.haml
1 1 - if @note.line_code
2   - = render "create_line", note: @note
  2 + = render "create_per_line_note", note: @note
3 3 - else
4   - = render "create_common", note: @note
  4 + = render "create_common_note", note: @note
5 5  
6 6 -# Enable submit button
7 7 :plain
... ...
app/views/notes/index.js.haml
1   -= render "notes/load"
  1 +- unless @notes.blank?
  2 + - if loading_more_notes?
  3 + :plain
  4 + NoteList.appendMoreNotes(#{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}");
  5 +
  6 + - elsif loading_new_notes?
  7 + :plain
  8 + NoteList.replaceNewNotes("#{escape_javascript(render 'notes/notes')}");
  9 +
  10 + - else
  11 + :plain
  12 + NoteList.setContent(#{@notes.first.id}, #{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}");
  13 +
  14 +- else
  15 + - if loading_more_notes?
  16 + :plain
  17 + NoteList.finishedLoadingMore();
... ...
app/views/projects/wall.html.haml
1 1 %div.wall_page
2   - = render "notes/notes", tid: nil, tt: "wall"
  2 + = render "notes/reversed_notes_with_form", tid: nil, tt: "wall"
... ...
app/views/snippets/show.html.haml
... ... @@ -17,4 +17,4 @@
17 17 %div{class: current_user.dark_scheme ? "black" : ""}
18 18 = raw @snippet.colorize(options: { linenos: 'True'})
19 19  
20   -= render "notes/notes", tid: @snippet.id, tt: "snippet"
  20 += render "notes/notes_with_form", tid: @snippet.id, tt: "snippet"
... ...
app/views/wikis/show.html.haml
... ... @@ -21,4 +21,4 @@
21 21 Delete this page
22 22  
23 23 %hr
24   -.wiki_notes#notes= render "notes/notes", tid: @wiki.id, tt: "wiki"
  24 +.wiki_notes#notes= render "notes/notes_with_form", tid: @wiki.id, tt: "wiki"
... ...