FEATURE: composer option to reload page and force save draft (#10773)

FEATURE: composer option to reload page and force save draft (#10773)

diff --git a/app/assets/javascripts/discourse/app/models/composer.js b/app/assets/javascripts/discourse/app/models/composer.js
index 44ff23a..0d6c436 100644
--- a/app/assets/javascripts/discourse/app/models/composer.js
+++ b/app/assets/javascripts/discourse/app/models/composer.js
@@ -113,6 +113,7 @@ const Composer = RestModel.extend({
   noBump: false,
   draftSaving: false,
   draftSaved: false,
+  draftForceSave: false,
 
   archetypes: reads("site.archetypes"),
 
@@ -1171,7 +1172,8 @@ const Composer = RestModel.extend({
       this.draftKey,
       this.draftSequence,
       data,
-      this.messageBus.clientId
+      this.messageBus.clientId,
+      { forceSave: this.draftForceSave }
     )
       .then((result) => {
         if (result.draft_sequence) {
@@ -1186,6 +1188,7 @@ const Composer = RestModel.extend({
           this.setProperties({
             draftSaved: true,
             draftConflictUser: null,
+            draftForceSave: false,
           });
         }
       })
@@ -1203,10 +1206,29 @@ const Composer = RestModel.extend({
           const json = e.jqXHR.responseJSON;
           draftStatus = json.errors[0];
           if (json.extras && json.extras.description) {
-            bootbox.alert(json.extras.description);
+            const buttons = [];
+
+            // ignore and force save draft
+            buttons.push({
+              label: I18n.t("composer.ignore"),
+              class: "btn",
+              callback: () => {
+                this.set("draftForceSave", true);
+              },
+            });
+
+            // reload
+            buttons.push({
+              label: I18n.t("composer.reload"),
+              class: "btn btn-primary",
+              callback: () => {
+                window.location.reload();
+              },
+            });
+
+            bootbox.dialog(json.extras.description, buttons);
           }
         }
-
         this.setProperties({
           draftStatus: draftStatus || I18n.t("composer.drafts_offline"),
           draftConflictUser: null,
diff --git a/app/assets/javascripts/discourse/app/models/draft.js b/app/assets/javascripts/discourse/app/models/draft.js
index cc09c97..25bddd8 100644
--- a/app/assets/javascripts/discourse/app/models/draft.js
+++ b/app/assets/javascripts/discourse/app/models/draft.js
@@ -23,11 +23,17 @@ Draft.reopenClass({
     return current;
   },
 
-  save(key, sequence, data, clientId) {
+  save(key, sequence, data, clientId, { forceSave = false } = {}) {
     data = typeof data === "string" ? data : JSON.stringify(data);
     return ajax("/draft.json", {
       type: "POST",
-      data: { draft_key: key, sequence, data, owner: clientId },
+      data: {
+        draft_key: key,
+        sequence,
+        data,
+        owner: clientId,
+        force_save: forceSave,
+      },
     });
   },
 });
diff --git a/app/controllers/draft_controller.rb b/app/controllers/draft_controller.rb
index 22cedd3..3cc77a3 100644
--- a/app/controllers/draft_controller.rb
+++ b/app/controllers/draft_controller.rb
@@ -22,7 +22,8 @@ class DraftController < ApplicationController
           params[:draft_key],
           params[:sequence].to_i,
           params[:data],
-          params[:owner]
+          params[:owner],
+          force_save: params[:force_save]
         )
       rescue Draft::OutOfSequence
 
diff --git a/app/models/draft.rb b/app/models/draft.rb
index 2165a0a..6d34463 100644
--- a/app/models/draft.rb
+++ b/app/models/draft.rb
@@ -9,8 +9,9 @@ class Draft < ActiveRecord::Base
 
   class OutOfSequence < StandardError; end
 
-  def self.set(user, key, sequence, data, owner = nil)
+  def self.set(user, key, sequence, data, owner = nil, force_save: false)
     return 0 if !User.human_user_id?(user.id)
+    force_save = force_save.to_s == "true"
 
     if SiteSetting.backup_drafts_to_pm_length > 0 && SiteSetting.backup_drafts_to_pm_length < data.length
       backup_draft(user, key, sequence, data)
@@ -41,10 +42,11 @@ class Draft < ActiveRecord::Base
     current_sequence ||= 0
 
     if draft_id
-      if current_sequence != sequence
+      if !force_save && (current_sequence != sequence)
         raise Draft::OutOfSequence
       end
 
+      sequence = current_sequence if force_save
       sequence += 1
 
       # we need to keep upping our sequence on every save
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 34c4cc9..5c3006c 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1942,6 +1942,9 @@ en:
           label: "Toggle topic bump"
           desc: "Reply without changing latest reply date"
 
+      reload: "Reload"
+      ignore: "Ignore"
+
     notifications:
       tooltip:
         regular:
diff --git a/spec/models/draft_spec.rb b/spec/models/draft_spec.rb
index 980f079..df1c3df 100644
--- a/spec/models/draft_spec.rb
+++ b/spec/models/draft_spec.rb
@@ -137,6 +137,15 @@ describe Draft do
     expect(Draft.get(user, "test", 1)).to eq "hello"
   end
 
+  it "should disregard draft sequence if force_save is true" do
+    Draft.set(user, "test", 0, "data")
+    DraftSequence.next!(user, "test")
+    Draft.set(user, "test", 1, "hello")
+
+    seq = Draft.set(user, "test", 0, "foo", nil, force_save: true)
+    expect(seq).to eq(2)
+  end
+
   it 'can cleanup old drafts' do
     key = Draft::NEW_TOPIC
 

GitHub sha: 025490ec

This commit appears in #10773 which was approved by eviltrout. It was merged by techAPJ.