From cc74c3f9ab402be173b89c5ec91f0ea92c10ea00 Mon Sep 17 00:00:00 2001 From: Gerhard Schlager Date: Fri, 30 Oct 2020 02:02:35 +0100 Subject: [PATCH] FEATURE: Batch process topic bulk actions (#10980) Topics are processed in chunks of 30 in order to prevent timeouts. --- .../app/controllers/topic-bulk-actions.js | 67 ++++++++++++++++--- .../app/templates/modal/bulk-progress.hbs | 1 + config/locales/client.en.yml | 3 + 3 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/templates/modal/bulk-progress.hbs diff --git a/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js b/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js index 4d88bec3e1..b863206725 100644 --- a/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js +++ b/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js @@ -5,6 +5,7 @@ import ModalFunctionality from "discourse/mixins/modal-functionality"; import Topic from "discourse/models/topic"; import Category from "discourse/models/category"; import bootbox from "bootbox"; +import { Promise } from "rsvp"; const _buttons = []; @@ -84,6 +85,7 @@ export default Controller.extend(ModalFunctionality, { emptyTags: empty("tags"), categoryId: alias("model.category.id"), + processedTopicCount: 0, onShow() { const topics = this.get("model.topics"); @@ -101,23 +103,70 @@ export default Controller.extend(ModalFunctionality, { }, perform(operation) { + this.set("processedTopicCount", 0); + this.send("changeBulkTemplate", "modal/bulk-progress"); this.set("loading", true); - const topics = this.get("model.topics"); - return Topic.bulkOperation(topics, operation) - .then((result) => { - this.set("loading", false); - if (result && result.topic_ids) { - return result.topic_ids.map((t) => topics.findBy("id", t)); - } - return result; - }) + return this._processChunks(operation) .catch(() => { bootbox.alert(I18n.t("generic_error")); + }) + .finally(() => { this.set("loading", false); }); }, + _generateTopicChunks(allTopics) { + let startIndex = 0; + const chunkSize = 30; + const chunks = []; + + while (startIndex < allTopics.length) { + let topics = allTopics.slice(startIndex, startIndex + chunkSize); + chunks.push(topics); + startIndex += chunkSize; + } + + return chunks; + }, + + _processChunks(operation) { + const allTopics = this.get("model.topics"); + const topicChunks = this._generateTopicChunks(allTopics); + const topicIds = []; + + const tasks = topicChunks.map((topics) => () => { + return Topic.bulkOperation(topics, operation).then((result) => { + this.set( + "processedTopicCount", + this.get("processedTopicCount") + topics.length + ); + return result; + }); + }); + + return new Promise((resolve, reject) => { + const resolveNextTask = () => { + if (tasks.length === 0) { + const topics = topicIds.map((id) => allTopics.findBy("id", id)); + return resolve(topics); + } + + tasks + .shift()() + .then((result) => { + if (result && result.topic_ids) { + topicIds.push(...result.topic_ids); + } + resolveNextTask(); + }) + .catch(reject); + }; + + resolveNextTask(); + }); + }, + forEachPerformed(operation, cb) { this.perform(operation).then((topics) => { if (topics) { diff --git a/app/assets/javascripts/discourse/app/templates/modal/bulk-progress.hbs b/app/assets/javascripts/discourse/app/templates/modal/bulk-progress.hbs new file mode 100644 index 0000000000..8fb41beeee --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/modal/bulk-progress.hbs @@ -0,0 +1 @@ +

{{html-safe (i18n "topics.bulk.progress" count=processedTopicCount)}}

diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 3f30571cab..6a44c0dad2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2199,6 +2199,9 @@ en: choose_append_tags: "Choose new tags to append for these topics:" changed_tags: "The tags of those topics were changed." remove_tags: "Remove Tags" + progress: + one: "Progress: %{count} topic" + other: "Progress: %{count} topics" none: unread: "You have no unread topics."