diff --git a/app/assets/javascripts/discourse/controllers/composer.js b/app/assets/javascripts/discourse/controllers/composer.js index e7d02fbd3c9..297d635fc0f 100644 --- a/app/assets/javascripts/discourse/controllers/composer.js +++ b/app/assets/javascripts/discourse/controllers/composer.js @@ -27,6 +27,8 @@ import EmberObject, { computed } from "@ember/object"; import deprecated from "discourse-common/lib/deprecated"; function loadDraft(store, opts) { + let promise = Promise.resolve(); + opts = opts || {}; let draft = opts.draft; @@ -59,10 +61,10 @@ function loadDraft(store, opts) { attrs[f] = draft[f] || opts[f]; }); - composer.open(attrs); - - return composer; + promise = promise.then(() => composer.open(attrs)).then(() => composer); } + + return promise; } const _popupMenuOptionsCallbacks = []; @@ -799,7 +801,8 @@ export default Controller.extend({ this.setProperties({ showEditReason: false, editReason: null, - scopedCategoryId: null + scopedCategoryId: null, + skipAutoSave: true }); // Scope the categories drop down to the category we opened the composer with. @@ -820,7 +823,7 @@ export default Controller.extend({ composerModel = null; } - return new Promise((resolve, reject) => { + let promise = new Promise((resolve, reject) => { if (composerModel && composerModel.replyDirty) { // If we're already open, we don't have to do anything if ( @@ -870,7 +873,7 @@ export default Controller.extend({ opts.draft = data.draft; } opts.draftSequence = data.draft_sequence; - this._setModel(composerModel, opts); + return this._setModel(composerModel, opts); }) .then(resolve, reject); } @@ -884,72 +887,88 @@ export default Controller.extend({ if (data.draft) { opts.draft = data.draft; opts.draftSequence = data.draft_sequence; - this.open(opts); + return this.open(opts); } }); } - this._setModel(composerModel, opts); - resolve(); + this._setModel(composerModel, opts).then(resolve, reject); }); + + promise = promise.finally(() => { + this.skipAutoSave = false; + }); + return promise; }, // Given a potential instance and options, set the model for this composer. - _setModel(composerModel, opts) { + _setModel(optionalComposerModel, opts) { + let promise = Promise.resolve(); + this.set("linkLookup", null); - if (opts.draft) { - composerModel = loadDraft(this.store, opts); - if (composerModel) { - composerModel.set("topic", opts.topic); + promise = promise.then(() => { + if (opts.draft) { + return loadDraft(this.store, opts).then(model => { + if (!model) { + throw new Error("draft was not found"); + } + return model; + }); + } else { + let model = + optionalComposerModel || this.store.createRecord("composer"); + return model.open(opts).then(() => model); } - } else { - composerModel = composerModel || this.store.createRecord("composer"); - composerModel.open(opts); - } - - this.set("model", composerModel); - composerModel.setProperties({ - composeState: Composer.OPEN, - isWarning: false }); - if (!this.model.targetRecipients) { - if (opts.usernames) { - deprecated("`usernames` is deprecated, use `recipients` instead."); - this.model.set("targetRecipients", opts.usernames); - } else if (opts.recipients) { - this.model.set("targetRecipients", opts.recipients); + promise.then(composerModel => { + this.set("model", composerModel); + + composerModel.setProperties({ + composeState: Composer.OPEN, + isWarning: false + }); + + if (!this.model.targetRecipients) { + if (opts.usernames) { + deprecated("`usernames` is deprecated, use `recipients` instead."); + this.model.set("targetRecipients", opts.usernames); + } else if (opts.recipients) { + this.model.set("targetRecipients", opts.recipients); + } } - } - if ( - opts.topicTitle && - opts.topicTitle.length <= this.siteSettings.max_topic_title_length - ) { - this.model.set("title", opts.topicTitle); - } + if ( + opts.topicTitle && + opts.topicTitle.length <= this.siteSettings.max_topic_title_length + ) { + this.model.set("title", opts.topicTitle); + } - if (opts.topicCategoryId) { - this.model.set("categoryId", opts.topicCategoryId); - } + if (opts.topicCategoryId) { + this.model.set("categoryId", opts.topicCategoryId); + } - if (opts.topicTags && !this.site.mobileView && this.site.can_tag_topics) { - let tags = escapeExpression(opts.topicTags) - .split(",") - .slice(0, this.siteSettings.max_tags_per_topic); + if (opts.topicTags && !this.site.mobileView && this.site.can_tag_topics) { + let tags = escapeExpression(opts.topicTags) + .split(",") + .slice(0, this.siteSettings.max_tags_per_topic); - tags.forEach( - (tag, index, array) => - (array[index] = tag.substring(0, this.siteSettings.max_tag_length)) - ); + tags.forEach( + (tag, index, array) => + (array[index] = tag.substring(0, this.siteSettings.max_tag_length)) + ); - this.model.set("tags", tags); - } + this.model.set("tags", tags); + } - if (opts.topicBody) { - this.model.set("reply", opts.topicBody); - } + if (opts.topicBody) { + this.model.set("reply", opts.topicBody); + } + }); + + return promise; }, viewNewReply() { @@ -1009,10 +1028,12 @@ export default Controller.extend({ }, cancelComposer(differentDraft = false) { + this.skipAutoSave = true; + const keyPrefix = this.model.action === "edit" ? "post.abandon_edit" : "post.abandon"; - return new Promise(resolve => { + let promise = new Promise(resolve => { if (this.get("model.hasMetaData") || this.get("model.replyDirty")) { bootbox.dialog(I18n.t(keyPrefix + ".confirm"), [ { @@ -1051,6 +1072,10 @@ export default Controller.extend({ }); } }); + + return promise.finally(() => { + this.skipAutoSave = false; + }); }, shrink() { @@ -1073,7 +1098,14 @@ export default Controller.extend({ @observes("model.reply", "model.title") _shouldSaveDraft() { - debounce(this, this._saveDraft, 2000); + if ( + this.model && + !this.model.loading && + !this.skipAutoSave && + !this.model.disableDrafts + ) { + debounce(this, this._saveDraft, 2000); + } }, @discourseComputed("model.categoryId", "lastValidatedAt") diff --git a/app/assets/javascripts/discourse/models/composer.js b/app/assets/javascripts/discourse/models/composer.js index 1b53ed8927c..9d0dcb8c451 100644 --- a/app/assets/javascripts/discourse/models/composer.js +++ b/app/assets/javascripts/discourse/models/composer.js @@ -673,14 +673,16 @@ const Composer = RestModel.extend({ quote - If we're opening a reply from a quote, the quote we're making */ open(opts) { + let promise = Promise.resolve(); + if (!opts) opts = {}; - this.set("loading", false); + this.set("loading", true); const replyBlank = isEmpty(this.reply); const composer = this; if (!replyBlank && (opts.reply || isEdit(opts.action)) && this.replyDirty) { - return; + return promise; } if (opts.action === REPLY && isEdit(this.action)) { @@ -741,11 +743,11 @@ const Composer = RestModel.extend({ } if (opts.postId) { - this.set("loading", true); - - this.store - .find("post", opts.postId) - .then(post => composer.setProperties({ post, loading: false })); + promise = promise.then(() => + this.store + .find("post", opts.postId) + .then(post => composer.setProperties({ post })) + ); } // If we are editing a post, load it. @@ -759,15 +761,16 @@ const Composer = RestModel.extend({ } this.setProperties(topicProps); - this.store.find("post", opts.post.id).then(post => { - composer.setProperties({ - reply: post.raw, - originalText: post.raw, - loading: false - }); + promise = promise.then(() => + this.store.find("post", opts.post.id).then(post => { + composer.setProperties({ + reply: post.raw, + originalText: post.raw + }); - composer.appEvents.trigger("composer:reply-reloaded", composer); - }); + composer.appEvents.trigger("composer:reply-reloaded", composer); + }) + ); } else if (opts.action === REPLY && opts.quote) { this.setProperties({ reply: opts.quote, @@ -785,7 +788,9 @@ const Composer = RestModel.extend({ } if (!isEdit(opts.action) || !opts.post) { - composer.appEvents.trigger("composer:reply-reloaded", composer); + promise = promise.then(() => + composer.appEvents.trigger("composer:reply-reloaded", composer) + ); } // Ensure additional draft fields are set @@ -793,7 +798,9 @@ const Composer = RestModel.extend({ this.set(_add_draft_fields[f], opts[f]); }); - return false; + return promise.finally(() => { + this.set("loading", false); + }); }, // Overwrite to implement custom logic