From 1100320b0cc4a1cc0f88eaf9f4d4d5c4b6f13c5c Mon Sep 17 00:00:00 2001
From: Fatih Acet <acetfatih@gmail.com>
Date: Fri, 14 Oct 2016 00:11:43 +0300
Subject: [PATCH] Refactor discussion edit widget to have only one at a time.

---
 .../javascripts/lib/utils/common_utils.js     |  12 ++
 app/assets/javascripts/notes.js               | 106 +++++++++++-------
 app/assets/stylesheets/pages/notes.scss       |   9 ++
 app/views/projects/notes/_edit_form.html.haml |   2 +
 app/views/projects/notes/_note.html.haml      |   3 +-
 app/views/projects/notes/_notes.html.haml     |   3 +
 6 files changed, 96 insertions(+), 39 deletions(-)

diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 0a0e73e0ccc..06f85897f1a 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -129,4 +129,16 @@
 
   })(window);
 
+  gl.utils.isElementVisibleInViewport = function(el) {
+    var rect = el.getBoundingClientRect();
+    var height = Math.max(document.documentElement.clientHeight, window.innerHeight);
+    return !(rect.bottom - 110 < 0 || rect.top - height >= 0); // -110 for sticky GitLab navigation header
+  }
+
+  gl.utils.animateToElement = function($el) {
+    return $('body, html').animate({
+      scrollTop: $el.offset().top - 110
+    }, 200);
+  }
+
 }).call(this);
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index a8b9a352870..1f30368472d 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -63,7 +63,7 @@
       // change note in UI after update
       $(document).on("ajax:success", "form.edit-note", this.updateNote);
       // Edit note link
-      $(document).on("click", ".js-note-edit", this.showEditForm);
+      $(document).on("click", ".js-note-edit", this.showEditForm.bind(this));
       $(document).on("click", ".note-edit-cancel", this.cancelEdit);
       // Reopen and close actions for Issue/MR combined with note form submit
       $(document).on("click", ".js-comment-button", this.updateCloseButton);
@@ -466,6 +466,9 @@
       var $html, $note_li;
       // Convert returned HTML to a jQuery object so we can modify it further
       $html = $(note.html);
+
+      $('.note-edit-form').insertBefore('.notes-form');
+
       gl.utils.localTimeAgo($('.js-timeago', $html));
       $html.renderGFM();
       $html.find('.js-task-list-container').taskList('enable');
@@ -480,48 +483,73 @@
     };
 
 
+    Notes.prototype.checkContentToAllowEditing = function($el) {
+      var initialContent = $el.find('.original-note-content').text().trim();
+      var currentContent = $el.find('.note-textarea').val();
+      var isAllowed = true;
+
+      if (currentContent === initialContent) {
+        this.removeNoteEditForm($el);
+        $el.find('.js-md-write-button').trigger('click');
+      }
+      else {
+        var $buttons = $el.find('.note-form-actions');
+        var isButtonsVisible = gl.utils.isElementVisibleInViewport($buttons[0]);
+        var isWidgetVisible = gl.utils.isElementVisibleInViewport($el[0]);
+
+        if (!isButtonsVisible || !isWidgetVisible) {
+          gl.utils.animateToElement($el);
+        }
+
+        $el.find('.js-edit-warning').show();
+        $el.find('.js-md-write-button').trigger('click');
+        isAllowed = false;
+      }
+
+      return isAllowed;
+    }
+
+
     /*
     Called in response to clicking the edit note link
 
     Replaces the note text with the note edit form
     Adds a data attribute to the form with the original content of the note for cancellations
-     */
-
+    */
     Notes.prototype.showEditForm = function(e, scrollTo, myLastNote) {
-      var $noteText, done, form, note;
       e.preventDefault();
-      note = $(this).closest(".note");
-      note.addClass("is-editting");
-      form = note.find(".note-edit-form");
+
+      var $currentlyEditing = $('.note.is-editting');
+      if ($currentlyEditing.length) {
+        var isEditAllowed = this.checkContentToAllowEditing($currentlyEditing);
+
+        if (!isEditAllowed) {
+          return;
+        }
+      }
+
+      var note = $(e.target).closest('.note');
+      var $editForm = $('.note-edit-form');
+      var $originalContentEl = note.find('.original-note-content');
+      var originalContent = $originalContentEl.text().trim();
+      var postUrl = $originalContentEl.data('post-url');
+      var form = note.find('.note-edit-form');
+      var $noteText = form.find('.js-note-text');
+      var noteTextVal = $noteText.val(); // Neat little trick to put the cursor at the end
+
+      note.addClass('is-editting');
+      $editForm.insertAfter(note.find('.note-text'));
+      $editForm.find('.js-note-text').val(originalContent);
+      $editForm.find('form').attr('action', postUrl);
+
       form.addClass('current-note-edit-form');
-      // Show the attachment delete link
-      note.find(".js-note-attachment-delete").show();
-      done = function($noteText) {
-        var noteTextVal;
-        // Neat little trick to put the cursor at the end
-        noteTextVal = $noteText.val();
-        // Store the original note text in a data attribute to retrieve if a user cancels edit.
-        form.find('form.edit-note').data('original-note', noteTextVal);
-        return $noteText.val('').val(noteTextVal);
-      };
+      note.find('.js-note-attachment-delete').show(); // Show the attachment delete link
       new GLForm(form);
-      if ((scrollTo != null) && (myLastNote != null)) {
-        // scroll to the bottom
-        // so the open of the last element doesn't make a jump
-        $('html, body').scrollTop($(document).height());
-        return $('html, body').animate({
-          scrollTop: myLastNote.offset().top - 150
-        }, 500, function() {
-          var $noteText;
-          $noteText = form.find(".js-note-text");
-          $noteText.focus();
-          return done($noteText);
-        });
-      } else {
-        $noteText = form.find('.js-note-text');
-        $noteText.focus();
-        return done($noteText);
-      }
+
+      $noteText.focus();
+      // Store the original note text in a data attribute to retrieve if a user cancels edit.
+      form.find('form.edit-note').data('original-note', noteTextVal);
+      $noteText.val('').val(noteTextVal);
     };
 
 
@@ -532,15 +560,17 @@
      */
 
     Notes.prototype.cancelEdit = function(e) {
-      var note;
       e.preventDefault();
-      note = $(e.target).closest('.note');
+      var note = $(e.target).closest('.note');
+      note.find('.js-edit-warning').hide();
+      note.find('.js-md-write-button').trigger('click');
+      $('.note-edit-form').insertBefore('.notes-form');
       return this.removeNoteEditForm(note);
     };
 
+
     Notes.prototype.removeNoteEditForm = function(note) {
-      var form;
-      form = note.find(".current-note-edit-form");
+      var form = note.find(".current-note-edit-form");
       note.removeClass("is-editting");
       form.removeClass("current-note-edit-form");
       // Replace markdown textarea text with original note text.
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 21d72791e81..4eb81473e2b 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -601,3 +601,12 @@ ul.notes {
     position: relative;
   }
 }
+
+
+.note-edit-warning.settings-message {
+  display: none;
+  position: relative;
+  top: 1px;
+  left: 7px;
+  padding: 5px 10px;
+}
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
index 8620f492282..245a7c74ebf 100644
--- a/app/views/projects/notes/_edit_form.html.haml
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -7,5 +7,7 @@
 
     .note-form-actions.clearfix
       = f.submit 'Save Comment', class: 'btn btn-nr btn-save js-comment-button'
+      %span.settings-message.note-edit-warning.js-edit-warning
+        Finish editing this message first!
       %button.btn.btn-nr.btn-cancel.note-edit-cancel{ type: 'button' }
         Cancel
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index eb869ea85bf..fc4cb3f4a6d 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -67,7 +67,8 @@
             = note.redacted_note_html
         = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
         - if note_editable
-          = render 'projects/notes/edit_form', note: note
+          .original-note-content.hidden{data: {post_url: namespace_project_note_path(@project.namespace, @project, note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore}}
+            #{note.note}
         .note-awards
           = render 'award_emoji/awards_block', awardable: note, inline: false
         - if note.system
diff --git a/app/views/projects/notes/_notes.html.haml b/app/views/projects/notes/_notes.html.haml
index 022578bd6db..64d26d9ad50 100644
--- a/app/views/projects/notes/_notes.html.haml
+++ b/app/views/projects/notes/_notes.html.haml
@@ -6,3 +6,6 @@
       = render 'discussions/discussion', discussion: discussion
 - else
   = render partial: "projects/notes/note", collection: @notes, as: :note
+
+= render 'projects/notes/edit_form', note: @notes[0]
+= hidden_field_tag :authenticity_token, form_authenticity_token
-- 
GitLab