DEV: Add experimental plugin API to replace tag icon in sidebar (#21675)

Why this change?

This change allows plugins or themes to replace the tag icon in the
sidebar. The color of the icon can be customised as well.

However, do note that this change is marked experimental as we intend to
support custom icons for tags in the near term as part of Discourse core.
Therefore, the plugin API will become obsolete once that happens and we
are marking it experimental to avoid having to deprecate it.
This commit is contained in:
Alan Guo Xiang Tan 2023-05-24 09:50:54 +09:00 committed by GitHub
parent 2aa5fc927e
commit e880ede3d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 138 additions and 16 deletions

View File

@ -12,6 +12,7 @@
@currentWhen={{sectionLink.currentWhen}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@models={{sectionLink.models}}
data-tag-name={{sectionLink.tagName}}
/>

View File

@ -1,4 +1,5 @@
import Component from "@glimmer/component";
import { isHex } from "discourse/components/sidebar/section-link";
export default class extends Component {
get prefixValue() {
@ -11,8 +12,10 @@ export default class extends Component {
let hexValues = this.args.prefixValue;
hexValues = hexValues.reduce((acc, color) => {
if (color?.match(/^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)) {
acc.push(`#${color} 50%`);
const hexCode = isHex(color);
if (hexCode) {
acc.push(`#${hexCode} 50%`);
}
return acc;

View File

@ -1,6 +1,21 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
/**
* Checks if a given string is a valid color hex code.
*
* @param {String|undefined} input Input string to check if it is a valid color hex code. Can be in the form of "FFFFFF" or "#FFFFFF" or "FFF" or "#FFF".
* @returns {String|undefined} Returns the matching color hex code without the leading `#` if it is valid, otherwise returns undefined. Example: "FFFFFF" or "FFF".
*/
export function isHex(input) {
const match = input?.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/);
if (match) {
return match[1];
} else {
return;
}
}
export default class SectionLink extends Component {
@service currentUser;
@ -53,12 +68,12 @@ export default class SectionLink extends Component {
}
get prefixColor() {
const color = this.args.prefixColor;
const hexCode = isHex(this.args.prefixColor);
if (!color || !color.match(/^\w{6}$/)) {
return "";
if (hexCode) {
return `#${hexCode}`;
} else {
return;
}
return "#" + color;
}
}

View File

@ -22,6 +22,7 @@
@currentWhen={{sectionLink.currentWhen}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@badgeText={{sectionLink.badgeText}}
@models={{sectionLink.models}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}

View File

@ -109,6 +109,7 @@ import {
registerCustomCategorySectionLinkPrefix,
registerCustomCountable as registerUserCategorySectionLinkCountable,
} from "discourse/lib/sidebar/user/categories-section/category-section-link";
import { registerCustomTagSectionLinkPrefixIcon } from "discourse/lib/sidebar/user/tags-section/base-tag-section-link";
import { REFRESH_COUNTS_APP_EVENT_NAME as REFRESH_USER_SIDEBAR_CATEGORIES_SECTION_COUNTS_APP_EVENT_NAME } from "discourse/components/sidebar/user/categories-section";
import DiscourseURL from "discourse/lib/url";
import { registerNotificationTypeRenderer } from "discourse/lib/notification-types-manager";
@ -1965,6 +1966,38 @@ class PluginApi {
});
}
/**
* EXPERIMENTAL. Do not use.
* Register a custom prefix for a sidebar tag section link.
*
* Example:
*
* ```
* api.registerCustomTagSectionLinkPrefixValue({
* tagName: "tag1",
* prefixType: "icon",
* prefixValue: "wrench",
* prefixColor: "#FF0000"
* });
* ```
*
* @params {Object} arg - An object
* @params {string} arg.tagName - The name of the tag
* @params {string} arg.prefixValue - The name of a FontAwesome 5 icon.
* @params {string} arg.prefixColor - The color represented using hexadecimal to use for the prefix. Example: "#FF0000" or "#FFF".
*/
registerCustomTagSectionLinkPrefixIcon({
tagName,
prefixValue,
prefixColor,
}) {
registerCustomTagSectionLinkPrefixIcon({
tagName,
prefixValue,
prefixColor,
});
}
/**
* EXPERIMENTAL. Do not use.
* Triggers a refresh of the counts for all category section links under the categories section for a logged in user.

View File

@ -1,6 +1,28 @@
let customTagSectionLinkPrefixIcons = {};
export function registerCustomTagSectionLinkPrefixIcon({
tagName,
prefixValue,
prefixColor,
}) {
customTagSectionLinkPrefixIcons[tagName] = {
prefixValue,
prefixColor,
};
}
export function resetCustomTagSectionLinkPrefixIcons() {
for (let key in customTagSectionLinkPrefixIcons) {
if (customTagSectionLinkPrefixIcons.hasOwnProperty(key)) {
delete customTagSectionLinkPrefixIcons[key];
}
}
}
export default class BaseTagSectionLink {
constructor({ tagName }) {
constructor({ tagName, currentUser }) {
this.tagName = tagName;
this.currentUser = currentUser;
}
get name() {
@ -16,6 +38,10 @@ export default class BaseTagSectionLink {
}
get prefixValue() {
return "tag";
return customTagSectionLinkPrefixIcons[this.tagName]?.prefixValue || "tag";
}
get prefixColor() {
return customTagSectionLinkPrefixIcons[this.tagName]?.prefixColor;
}
}

View File

@ -1,11 +1,6 @@
import BaseTagSectionLink from "discourse/lib/sidebar/user/tags-section/base-tag-section-link";
export default class PMTagSectionLink extends BaseTagSectionLink {
constructor({ currentUser }) {
super(...arguments);
this.currentUser = currentUser;
}
get models() {
return [this.currentUser, this.tagName];
}

View File

@ -12,10 +12,9 @@ export default class TagSectionLink extends BaseTagSectionLink {
@tracked hideCount =
this.currentUser?.sidebarListDestination !== UNREAD_LIST_DESTINATION;
constructor({ topicTrackingState, currentUser }) {
constructor({ topicTrackingState }) {
super(...arguments);
this.topicTrackingState = topicTrackingState;
this.currentUser = currentUser;
this.refreshCounts();
}

View File

@ -15,6 +15,7 @@ import {
resetCustomCategorySectionLinkPrefix,
resetCustomCountables,
} from "discourse/lib/sidebar/user/categories-section/category-section-link";
import { resetCustomTagSectionLinkPrefixIcons } from "discourse/lib/sidebar/user/tags-section/base-tag-section-link";
import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sidebar";
import { bind } from "discourse-common/utils/decorators";
@ -22,6 +23,7 @@ acceptance("Sidebar - Plugin API", function (needs) {
needs.user({});
needs.settings({
tagging_enabled: true,
navigation_menu: "sidebar",
});
@ -820,4 +822,51 @@ acceptance("Sidebar - Plugin API", function (needs) {
resetCustomCategorySectionLinkPrefix();
}
});
test("Customizing the prefix icon used in a tag section link for a particular tag", async function (assert) {
try {
return await withPluginApi(PLUGIN_API_VERSION, async (api) => {
updateCurrentUser({
display_sidebar_tags: true,
sidebar_tags: [
{ name: "tag2", pm_only: false },
{ name: "tag1", pm_only: false },
{ name: "tag3", pm_only: false },
],
});
api.registerCustomTagSectionLinkPrefixIcon({
tagName: "tag1",
prefixValue: "wrench",
prefixColor: "#FF0000", // rgb(255, 0, 0)
});
await visit("/");
assert.ok(
exists(
`.sidebar-section-link[data-tag-name="tag1"] .prefix-icon.d-icon-wrench`
),
"wrench icon is displayed for tag1 section link's prefix icon"
);
assert.strictEqual(
query(
`.sidebar-section-link[data-tag-name="tag1"] .sidebar-section-link-prefix`
).style.color,
"rgb(255, 0, 0)",
"tag1 section link's prefix icon has the right color"
);
assert.ok(
exists(
`.sidebar-section-link[data-tag-name="tag2"] .prefix-icon.d-icon-tag`
),
"default tag icon is displayed for tag2 section link's prefix icon"
);
});
} finally {
resetCustomTagSectionLinkPrefixIcons();
}
});
});