diff --git a/javascripts/discourse/initializers/category-icons.js b/javascripts/discourse/initializers/category-icons.js index 85128c4..fdfe9b2 100644 --- a/javascripts/discourse/initializers/category-icons.js +++ b/javascripts/discourse/initializers/category-icons.js @@ -3,6 +3,7 @@ import { h } from "virtual-dom"; import categoriesBoxes from "discourse/components/categories-boxes"; import categoriesBoxesWithTopics from "discourse/components/categories-boxes-with-topics"; import categoryTitleLink from "discourse/components/category-title-link"; +import CategoryHashtagType from "discourse/lib/hashtag-types/category"; import { withPluginApi } from "discourse/lib/plugin-api"; import { isRTL } from "discourse/lib/text-direction"; import { escapeExpression } from "discourse/lib/utilities"; @@ -12,10 +13,29 @@ import { helperContext } from "discourse-common/lib/helpers"; import { iconHTML, iconNode } from "discourse-common/lib/icon-library"; import I18n from "I18n"; +class CategoryHashtagTypeWithIcon extends CategoryHashtagType { + constructor(dict, owner) { + super(owner); + this.dict = dict; + } + generateIconHTML(hashtag) { + const opt = this.dict[hashtag.id]; + if (opt) { + const newIcon = document.createElement("span"); + newIcon.classList.add("hashtag-category-icon"); + newIcon.innerHTML = iconHTML(opt.icon); + newIcon.style.color = opt.color; + return newIcon.outerHTML; + } else { + return super.generateIconHTML(hashtag); + } + } +} + export default { name: "category-icons", - initialize() { + initialize(owner) { withPluginApi("0.8.26", (api) => { let categoryThemeList = settings.category_icon_list.split("|"); let lockIcon = settings.category_lock_icon || "lock"; @@ -199,6 +219,37 @@ export default { } }); } + + if (api.registerHashtagType) { + const site = api.container.lookup("service:site"); + const dict = {}; + for (const str of categoryThemeList) { + let [slug, icon, color, match] = str.split(","); + if (slug && icon) { + slug = slug.toLowerCase(); + for (const cat of site.categories) { + const catSlug = cat.slug.toLowerCase(); + if ( + match === "partial" ? !catSlug.includes(slug) : catSlug !== slug + ) { + continue; + } + const opts = { + icon, + color, + }; + if (!color || color?.match(/categoryColo(u*)r/g)) { + opts.color = `#${cat.color}`; + } + dict[cat.id] = opts; + } + } + } + api.registerHashtagType( + "category", + new CategoryHashtagTypeWithIcon(dict, owner) + ); + } }); }, }; diff --git a/test/acceptance/post-body-category-icons-test.js b/test/acceptance/post-body-category-icons-test.js new file mode 100644 index 0000000..5fbaf95 --- /dev/null +++ b/test/acceptance/post-body-category-icons-test.js @@ -0,0 +1,89 @@ +import { visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import TopicFixtures from "discourse/tests/fixtures/topic"; +import { acceptance } from "discourse/tests/helpers/qunit-helpers"; +import { cloneJSON } from "discourse-common/lib/object"; + +function makeHashtagHTML(category) { + return `${category.name}`; +} + +acceptance("Post body - Category icons", function (needs) { + needs.user(); + + const categories = [ + { + id: 1, + name: "Category 1", + slug: "category-1", + color: "111111", + }, + { + id: 2, + name: "Category 2", + slug: "category-2", + color: "000000", + }, + { + id: 3, + name: "Category 3", + slug: "category-3", + color: "888888", + }, + ]; + + needs.site({ + categories, + }); + + needs.hooks.beforeEach(function () { + settings.category_lock_icon = "wrench"; + settings.category_icon_list = `category-1,wrench,#FF0000|category-2,question-circle,categoryColor`; + }); + + needs.pretender((server, helper) => { + server.get("/t/131.json", () => { + const topicList = cloneJSON(TopicFixtures["/t/130.json"]); + topicList.post_stream.posts[0].cooked = `

${makeHashtagHTML( + categories[0] + )} ${makeHashtagHTML(categories[1])} ${makeHashtagHTML( + categories[2] + )}

`; + return helper.response(topicList); + }); + }); + + test("Icon for category when `category_icon_list` theme setting has been configured", async function (assert) { + await visit("/t/131"); + + assert + .dom( + `.cooked .hashtag-cooked[data-id="1"] .hashtag-category-icon .d-icon-wrench` + ) + .exists("wrench icon is displayed for category-1"); + + assert + .dom(`.cooked .hashtag-cooked[data-id="1"] .hashtag-category-icon`) + .hasStyle( + { color: "rgb(255, 0, 0)" }, + "category-1 's icon has the right color" + ); + + assert + .dom( + `.cooked .hashtag-cooked[data-id="2"] .hashtag-category-icon .d-icon-question-circle` + ) + .exists("question-circle icon is displayed for category-2"); + + assert + .dom(`.cooked .hashtag-cooked[data-id="2"] .hashtag-category-icon`) + .hasStyle( + { color: "rgb(0, 0, 0)" }, + "category-2 's icon has the right categoryColor" + ); + + assert + .dom(`.cooked .hashtag-cooked[data-id="3"] .hashtag-category-badge`) + .exists("unconfigured categories have a default badge"); + }); +});