diff --git a/install/package.json b/install/package.json index cd8b1be579..60ed34bee2 100644 --- a/install/package.json +++ b/install/package.json @@ -108,10 +108,10 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.51", + "nodebb-theme-harmony": "2.2.52", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", - "nodebb-theme-persona": "14.2.27", + "nodebb-theme-persona": "14.2.28", "nodebb-widget-essentials": "7.0.43", "nodemailer": "8.0.2", "nprogress": "0.2.0", diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 39395258cb..7a6d2ba6f2 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -209,7 +209,9 @@ define('forum/groups/details', [ // Add icon selection interface iconBtn.on('click', function () { iconSelect.init(previewIcon, function () { - iconValueEl.val(previewIcon.val()); + const icon = previewIcon.val(); + previewIcon.toggleClass('hidden', !icon || icon === 'fa-nbb-none'); + iconValueEl.val(icon); }); }); diff --git a/src/topics/tags.js b/src/topics/tags.js index 7f60096cd1..48bbb44e15 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -256,14 +256,21 @@ module.exports = function (Topics) { }; async function removeTagsFromTopics(tags) { - await async.eachLimit(tags, 50, async (tag) => { - const tids = await db.getSortedSetRange(`tag:${tag}:topics`, 0, -1); - if (!tids.length) { - return; - } + const uniqTids = new Set(); + const tagsToRemove = new Set(tags); + + await batch.processArray(tags, async (tags) => { + await Promise.all(tags.map(async (tag) => { + await batch.processSortedSet(`tag:${tag}:topics`, async (tids) => { + tids.forEach(tid => uniqTids.add(tid)); + }, { batch: 500 }); + })); + }, { batch: 50 }); + + await batch.processArray(Array.from(uniqTids), async (tids) => { let topicsTags = await Topics.getTopicsTags(tids); topicsTags = topicsTags.map( - topicTags => topicTags.filter(topicTag => topicTag && topicTag !== tag) + topicTags => topicTags.filter(tag => tag && !tagsToRemove.has(tag)) ); await db.setObjectBulk( @@ -271,7 +278,7 @@ module.exports = function (Topics) { `topic:${tid}`, { tags: topicsTags[index].join(',') }, ])) ); - }); + }, { batch: 500 }); } async function removeTagsFromUsers(tags) { diff --git a/test/topics.js b/test/topics.js index e9cb2bf1b1..2b67fa7078 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1638,15 +1638,18 @@ describe('Topic\'s', () => { assert.deepStrictEqual(tags, ['deleteme1', 'deleteme3']); }); - it('should delete tag', (done) => { - topics.deleteTag('javascript', (err) => { - assert.ifError(err); - db.getObject('tag:javascript', (err, data) => { - assert.ifError(err); - assert(!data); - done(); - }); - }); + it('should delete tag', async () => { + await topics.deleteTag('javascript'); + const data = await db.getObject('tag:javascript'); + assert(!data); + }); + + it('should properly remove tags from topic hash when removing all tags of a topic', async () => { + const result1 = await topics.post({ uid: adminUid, tags: ['tag1', 'tag2', 'tag3', 'tag4', 'tag5'], title: 'many tags much wow', content: 'topic 1 content', cid: topic.categoryId }); + await topics.deleteTags(['tag1', 'tag2', 'tag3', 'tag4', 'tag5']); + const topicData = await topics.getTopicData(result1.topicData.tid); + const tags = topicData.tags.map(t => t.value); + assert.deepStrictEqual(tags, []); }); it('should delete category tag as well', async () => {