From 52e8ede8c35b994970d36f2a90e2accd53c8b9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 11 Mar 2026 16:52:25 -0400 Subject: [PATCH 1/2] fix: group badge on group details page --- install/package.json | 4 ++-- public/src/client/groups/details.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/install/package.json b/install/package.json index 3e8446284a..4a9cfa33a8 100644 --- a/install/package.json +++ b/install/package.json @@ -107,10 +107,10 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.6", "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.1", "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); }); }); From 0a94cecb0d7a2188b1ad0881ae18c4428fc345f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 11 Mar 2026 17:21:06 -0400 Subject: [PATCH 2/2] fix: #14084, fix tags not getting properly removed from topics this was happening because the same topic was passed to setObject bulk multiple times with different tag data. the fix is to find the uniq tids first and then only call setObjectBulk once per topic --- src/topics/tags.js | 21 ++++++++++++++------- test/topics.js | 21 ++++++++++++--------- 2 files changed, 26 insertions(+), 16 deletions(-) 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 21ecffd245..f70bd301fb 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1632,15 +1632,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 () => {