diff --git a/app/assets/javascripts/discourse/components/small-action.js.es6 b/app/assets/javascripts/discourse/components/small-action.js.es6 index 977b408c807..4ffa7ac63f2 100644 --- a/app/assets/javascripts/discourse/components/small-action.js.es6 +++ b/app/assets/javascripts/discourse/components/small-action.js.es6 @@ -10,7 +10,8 @@ const icons = { 'pinned_globally.enabled': 'thumb-tack', 'pinned_globally.disabled': 'thumb-tack unpinned', 'visible.enabled': 'eye', - 'visible.disabled': 'eye-slash' + 'visible.disabled': 'eye-slash', + 'split_topic': 'sign-out' }; export function actionDescription(actionCode, createdAt) { diff --git a/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 b/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 index c923aaf2dab..44f374ddbf8 100644 --- a/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/merge-topic.js.es6 @@ -1,12 +1,15 @@ import Presence from 'discourse/mixins/presence'; import SelectedPostsCount from 'discourse/mixins/selected-posts-count'; import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; +import { movePosts, mergeTopic } from 'discourse/models/topic'; // Modal related to merging of topics -export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, Presence, { +export default Ember.Controller.extend(SelectedPostsCount, ModalFunctionality, Presence, { needs: ['topic'], + saving: false, + selectedTopicId: null, + topicController: Em.computed.alias('controllers.topic'), selectedPosts: Em.computed.alias('topicController.selectedPosts'), selectedReplies: Em.computed.alias('topicController.selectedReplies'), @@ -22,38 +25,40 @@ export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, P return I18n.t('topic.merge_topic.title'); }.property('saving'), - onShow: function() { + onShow() { this.set('controllers.modal.modalClass', 'split-modal'); }, actions: { - movePostsToExistingTopic: function() { + movePostsToExistingTopic() { + const topicId = this.get('model.id'); + this.set('saving', true); - var promise = null; + let promise = null; if (this.get('allPostsSelected')) { - promise = Discourse.Topic.mergeTopic(this.get('id'), this.get('selectedTopicId')); + promise = mergeTopic(topicId, this.get('selectedTopicId')); } else { - var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }), - replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }); + const postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }); + const replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }); - promise = Discourse.Topic.movePosts(this.get('id'), { + promise = movePosts(topicId, { destination_topic_id: this.get('selectedTopicId'), post_ids: postIds, reply_post_ids: replyPostIds }); } - var mergeTopicController = this; + const self = this; promise.then(function(result) { // Posts moved - mergeTopicController.send('closeModal'); - mergeTopicController.get('topicController').send('toggleMultiSelect'); + self.send('closeModal'); + self.get('topicController').send('toggleMultiSelect'); Em.run.next(function() { Discourse.URL.routeTo(result.url); }); - }, function() { - // Error moving posts - mergeTopicController.flash(I18n.t('topic.merge_topic.error')); - mergeTopicController.set('saving', false); + }).catch(function() { + self.flash(I18n.t('topic.merge_topic.error')); + }).finally(function() { + self.set('saving', false); }); return false; } diff --git a/app/assets/javascripts/discourse/controllers/split-topic.js.es6 b/app/assets/javascripts/discourse/controllers/split-topic.js.es6 index 98aed2dccea..0c75ae35037 100644 --- a/app/assets/javascripts/discourse/controllers/split-topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/split-topic.js.es6 @@ -1,15 +1,20 @@ import Presence from 'discourse/mixins/presence'; import SelectedPostsCount from 'discourse/mixins/selected-posts-count'; import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; +import { extractError } from 'discourse/lib/ajax-error'; +import { movePosts } from 'discourse/models/topic'; // Modal related to auto closing of topics -export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, Presence, { +export default Ember.Controller.extend(SelectedPostsCount, ModalFunctionality, Presence, { needs: ['topic'], + topicName: null, + saving: false, + categoryId: null, topicController: Em.computed.alias('controllers.topic'), selectedPosts: Em.computed.alias('topicController.selectedPosts'), selectedReplies: Em.computed.alias('topicController.selectedReplies'), + allPostsSelected: Em.computed.alias('topicController.allPostsSelected'), buttonDisabled: function() { if (this.get('saving')) return true; @@ -21,7 +26,7 @@ export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, P return I18n.t('topic.split_topic.action'); }.property('saving'), - onShow: function() { + onShow() { this.setProperties({ 'controllers.modal.modalClass': 'split-modal', saving: false, @@ -31,39 +36,29 @@ export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, P }, actions: { - movePostsToNewTopic: function() { + movePostsToNewTopic() { this.set('saving', true); - var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }), - replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }), - self = this, - categoryId = this.get('categoryId'), - saveOpts = { - title: this.get('topicName'), - post_ids: postIds, - reply_post_ids: replyPostIds - }; + const postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); }), + replyPostIds = this.get('selectedReplies').map(function(p) { return p.get('id'); }), + self = this, + categoryId = this.get('categoryId'), + saveOpts = { + title: this.get('topicName'), + post_ids: postIds, + reply_post_ids: replyPostIds + }; if (!Ember.isNone(categoryId)) { saveOpts.category_id = categoryId; } - Discourse.Topic.movePosts(this.get('id'), saveOpts).then(function(result) { + movePosts(this.get('model.id'), saveOpts).then(function(result) { // Posts moved self.send('closeModal'); self.get('topicController').send('toggleMultiSelect'); - Em.run.next(function() { Discourse.URL.routeTo(result.url); }); + Ember.run.next(function() { Discourse.URL.routeTo(result.url); }); }).catch(function(xhr) { - - var error = I18n.t('topic.split_topic.error'); - - if (xhr) { - var json = xhr.responseJSON; - if (json && json.errors) { - error = json.errors[0]; - } - } - - // Error moving posts - self.flash(error); + self.flash(extractError(xhr, I18n.t('topic.split_topic.error'))); + }).finally(function() { self.set('saving', false); }); return false; diff --git a/app/assets/javascripts/discourse/lib/ajax-error.js.es6 b/app/assets/javascripts/discourse/lib/ajax-error.js.es6 index 04d6d0dbc7e..bb2a915727f 100644 --- a/app/assets/javascripts/discourse/lib/ajax-error.js.es6 +++ b/app/assets/javascripts/discourse/lib/ajax-error.js.es6 @@ -1,4 +1,4 @@ -function extractError(error) { +export function extractError(error, defaultMessage) { if (error instanceof Error) { Ember.Logger.error(error.stack); } @@ -42,7 +42,7 @@ function extractError(error) { } } - return parsedError || I18n.t('generic_error'); + return parsedError || defaultMessage || I18n.t('generic_error'); } export function throwAjaxError(undoCallback) { diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6 index 0f06dc48471..24550874112 100644 --- a/app/assets/javascripts/discourse/models/topic.js.es6 +++ b/app/assets/javascripts/discourse/models/topic.js.es6 @@ -1,3 +1,4 @@ +import { flushMap } from 'discourse/models/store'; import RestModel from 'discourse/models/rest'; const Topic = RestModel.extend({ @@ -462,28 +463,6 @@ Topic.reopenClass({ return Discourse.ajax(url + ".json", {data: data}); }, - mergeTopic(topicId, destinationTopicId) { - const promise = Discourse.ajax("/t/" + topicId + "/merge-topic", { - type: 'POST', - data: {destination_topic_id: destinationTopicId} - }).then(function (result) { - if (result.success) return result; - promise.reject(new Error("error merging topic")); - }); - return promise; - }, - - movePosts(topicId, opts) { - const promise = Discourse.ajax("/t/" + topicId + "/move-posts", { - type: 'POST', - data: opts - }).then(function (result) { - if (result.success) return result; - promise.reject(new Error("error moving posts topic")); - }); - return promise; - }, - changeOwners(topicId, opts) { const promise = Discourse.ajax("/t/" + topicId + "/change-owner", { type: 'POST', @@ -523,4 +502,24 @@ Topic.reopenClass({ } }); +function moveResult(result) { + if (result.success) { + // We should be hesitant to flush the map but moving ids is one rare case + flushMap(); + return result; + } + throw "error moving posts topic"; +} + +export function movePosts(topicId, data) { + return Discourse.ajax("/t/" + topicId + "/move-posts", { type: 'POST', data }).then(moveResult); +} + +export function mergeTopic(topicId, destinationTopicId) { + return Discourse.ajax("/t/" + topicId + "/merge-topic", { + type: 'POST', + data: {destination_topic_id: destinationTopicId} + }).then(moveResult); +} + export default Topic; diff --git a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs index a05ac14f22d..e4bf364fc2e 100644 --- a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs +++ b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs @@ -1,10 +1,4 @@
{{{i18n 'topic.merge_topic.instructions' count=selectedPostsCount}}}