From 8b442709295a5e19c61715b49b9c31f04d2f1371 Mon Sep 17 00:00:00 2001 From: Kelv Date: Wed, 24 Jan 2024 12:00:46 +0800 Subject: [PATCH] DEV: refactor topic-map participants into glimmer component (#25350) * DEV: refactor topic-map participants into glimmer components --- .../topic-map/topic-participant.gjs | 70 +++++++++++ .../topic-map/topic-participants.gjs | 17 +++ .../discourse/app/lib/plugin-api.js | 2 +- .../discourse/app/widgets/topic-map.js | 112 +++++------------- .../components/topic-participant-test.js | 42 +++++++ .../widgets/topic-participant-test.js | 56 --------- 6 files changed, 158 insertions(+), 141 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/topic-map/topic-participant.gjs create mode 100644 app/assets/javascripts/discourse/app/components/topic-map/topic-participants.gjs create mode 100644 app/assets/javascripts/discourse/tests/integration/components/topic-participant-test.js delete mode 100644 app/assets/javascripts/discourse/tests/integration/components/widgets/topic-participant-test.js diff --git a/app/assets/javascripts/discourse/app/components/topic-map/topic-participant.gjs b/app/assets/javascripts/discourse/app/components/topic-map/topic-participant.gjs new file mode 100644 index 00000000000..2d9de5debb3 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/topic-map/topic-participant.gjs @@ -0,0 +1,70 @@ +import Component from "@glimmer/component"; +import { htmlSafe } from "@ember/template"; +import UserAvatarFlair from "discourse/components/user-avatar-flair"; +import { userPath } from "discourse/lib/url"; +import { avatarImg } from "discourse-common/lib/avatar-utils"; +import gt from "truth-helpers/helpers/gt"; + +const addTopicParticipantClassesCallbacks = []; + +export function addTopicParticipantClassesCallback(callback) { + addTopicParticipantClassesCallbacks.push(callback); +} + +export default class TopicParticipant extends Component { + get avatarImage() { + return htmlSafe( + avatarImg({ + avatarTemplate: this.args.participant.avatar_template, + size: "medium", + title: this.args.participant.name || this.args.participant.username, + }) + ); + } + + get participantClasses() { + const { primary_group_name } = this.args.participant; + return [ + primary_group_name ? `group-${primary_group_name}` : null, + addTopicParticipantClassesCallbacks.map((callback) => + callback(this.args.participant) + ), + ] + .filter(Boolean) + .flat(3) + .join(" "); + } + + get linkClasses() { + return [ + "poster", + "trigger-user-card", + this.args.toggledUsers?.has(this.args.participant.username) + ? "toggled" + : null, + ] + .filter(Boolean) + .join(" "); + } + + get userUrl() { + userPath(this.args.participant); + } + + +} diff --git a/app/assets/javascripts/discourse/app/components/topic-map/topic-participants.gjs b/app/assets/javascripts/discourse/app/components/topic-map/topic-participants.gjs new file mode 100644 index 00000000000..a8bfe728bef --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/topic-map/topic-participants.gjs @@ -0,0 +1,17 @@ +import Component from "@glimmer/component"; +import TopicParticipant from "discourse/components/topic-map/topic-participant"; + +export default class TopicParticipants extends Component { + // prettier-ignore + toggledUsers = new Set(this.args.userFilters); + + +} diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js index 78b209919bf..ea3457b5a24 100644 --- a/app/assets/javascripts/discourse/app/lib/plugin-api.js +++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js @@ -26,6 +26,7 @@ import { import { addOnKeyUpCallback } from "discourse/components/search-menu/search-term"; import { REFRESH_COUNTS_APP_EVENT_NAME as REFRESH_USER_SIDEBAR_CATEGORIES_SECTION_COUNTS_APP_EVENT_NAME } from "discourse/components/sidebar/user/categories-section"; import { forceDropdownForMenuPanels } from "discourse/components/site-header"; +import { addTopicParticipantClassesCallback } from "discourse/components/topic-map/topic-participant"; import { setDesktopScrollAreaHeight } from "discourse/components/topic-timeline/container"; import { addTopicTitleDecorator } from "discourse/components/topic-title"; import { setNotificationsLimit as setUserMenuNotificationsLimit } from "discourse/components/user-menu/notifications-list"; @@ -122,7 +123,6 @@ import { addSearchSuggestion, removeDefaultQuickSearchRandomTips as removeWidgetDefaultQuickSearchRandomTips, } from "discourse/widgets/search-menu-results"; -import { addTopicParticipantClassesCallback } from "discourse/widgets/topic-map"; import { changeSetting, createWidget, diff --git a/app/assets/javascripts/discourse/app/widgets/topic-map.js b/app/assets/javascripts/discourse/app/widgets/topic-map.js index 52d7ebb2ed4..e32ed95cba8 100644 --- a/app/assets/javascripts/discourse/app/widgets/topic-map.js +++ b/app/assets/javascripts/discourse/app/widgets/topic-map.js @@ -1,27 +1,30 @@ +import { htmlSafe } from "@ember/template"; import { hbs } from "ember-cli-htmlbars"; import { h } from "virtual-dom"; import { dateNode, numberNode } from "discourse/helpers/node"; -import autoGroupFlairForUser from "discourse/lib/avatar-flair"; -import { userPath } from "discourse/lib/url"; import { replaceEmoji } from "discourse/widgets/emoji"; -import { avatarFor, avatarImg } from "discourse/widgets/post"; +import { avatarFor } from "discourse/widgets/post"; import RenderGlimmer from "discourse/widgets/render-glimmer"; import { createWidget } from "discourse/widgets/widget"; import I18n from "discourse-i18n"; const LINKS_SHOWN = 5; -function renderParticipants(userFilters, participants) { - if (!participants) { - return; - } - - userFilters = userFilters || []; - return participants.map((p) => { - return this.attach("topic-participant", p, { - state: { toggled: userFilters.includes(p.username) }, - }); - }); +function renderParticipants(wrapperElement, title, userFilters, participants) { + return new RenderGlimmer( + this, + wrapperElement, + hbs``, + { + title, + userFilters, + participants, + } + ); } createWidget("topic-map-show-links", { @@ -43,70 +46,6 @@ createWidget("topic-map-show-links", { }, }); -let addTopicParticipantClassesCallbacks = null; -export function addTopicParticipantClassesCallback(callback) { - addTopicParticipantClassesCallbacks = - addTopicParticipantClassesCallbacks || []; - addTopicParticipantClassesCallbacks.push(callback); -} -createWidget("topic-participant", { - buildClasses(attrs) { - const classNames = []; - if (attrs.primary_group_name) { - classNames.push(`group-${attrs.primary_group_name}`); - } - if (addTopicParticipantClassesCallbacks) { - for (let i = 0; i < addTopicParticipantClassesCallbacks.length; i++) { - let pluginClasses = addTopicParticipantClassesCallbacks[i].call( - this, - attrs - ); - if (pluginClasses) { - classNames.push.apply(classNames, pluginClasses); - } - } - } - return classNames; - }, - - html(attrs, state) { - const linkContents = [ - avatarImg("medium", { - username: attrs.username, - template: attrs.avatar_template, - name: attrs.name, - }), - ]; - - if (attrs.post_count > 1) { - linkContents.push(h("span.post-count", attrs.post_count.toString())); - } - - if (attrs.flair_group_id) { - if (attrs.flair_url || attrs.flair_bg_color) { - linkContents.push(this.attach("avatar-flair", attrs)); - } else { - const autoFlairAttrs = autoGroupFlairForUser(this.site, attrs); - if (autoFlairAttrs) { - linkContents.push(this.attach("avatar-flair", autoFlairAttrs)); - } - } - } - return h( - "a.poster.trigger-user-card", - { - className: state.toggled ? "toggled" : null, - attributes: { - title: attrs.username, - "data-user-card": attrs.username, - href: userPath(attrs.username), - }, - }, - linkContents - ); - }, -}); - createWidget("topic-map-summary", { tagName: "section.map", @@ -245,10 +184,12 @@ createWidget("topic-map-summary", { ) { const participants = renderParticipants.call( this, + "li.avatars", + "", attrs.userFilters, attrs.participants.slice(0, 3) ); - contents.push(h("li.avatars", participants)); + contents.push(participants); } const nav = h( @@ -313,10 +254,13 @@ createWidget("topic-map-expanded", { let avatars; if (attrs.participants && attrs.participants.length > 0) { - avatars = h("section.avatars", [ - h("h3", I18n.t("topic_map.participants_title")), - renderParticipants.call(this, attrs.userFilters, attrs.participants), - ]); + avatars = renderParticipants.call( + this, + "section.avatars", + htmlSafe(`

${I18n.t("topic_map.participants_title")}

`), + attrs.userFilters, + attrs.participants + ); } const result = [avatars]; @@ -408,7 +352,7 @@ export default createWidget("topic-map", { "section.information.toggle-summary", hbs``, { postAttrs: attrs, diff --git a/app/assets/javascripts/discourse/tests/integration/components/topic-participant-test.js b/app/assets/javascripts/discourse/tests/integration/components/topic-participant-test.js new file mode 100644 index 00000000000..51a2e83f0b4 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/integration/components/topic-participant-test.js @@ -0,0 +1,42 @@ +import { render } from "@ember/test-helpers"; +import { hbs } from "ember-cli-htmlbars"; +import { module, test } from "qunit"; +import { setupRenderingTest } from "discourse/tests/helpers/component-test"; + +module("Integration | Component | topic-participant", function (hooks) { + setupRenderingTest(hooks); + + test("one post", async function (assert) { + this.set("args", { + username: "test", + avatar_template: "/images/avatar.png", + post_count: 1, + }); + + await render(hbs``); + + assert.dom("a.poster.trigger-user-card").exists(); + assert.dom("span.post-count").doesNotExist(); + assert.dom(".avatar-flair").doesNotExist(); + }); + + test("many posts, a primary group with flair", async function (assert) { + this.set("args", { + username: "test", + avatar_template: "/images/avatar.png", + post_count: 2, + primary_group_name: "devs", + flair_name: "devs", + flair_url: "/images/d-logo-sketch-small.png", + flair_bg_color: "222", + flair_group_id: "41", + }); + + await render(hbs``); + + assert.dom("a.poster.trigger-user-card").exists(); + assert.dom("span.post-count").exists(); + assert.dom(".group-devs a.poster").exists(); + assert.dom(".avatar-flair.avatar-flair-devs").exists(); + }); +}); diff --git a/app/assets/javascripts/discourse/tests/integration/components/widgets/topic-participant-test.js b/app/assets/javascripts/discourse/tests/integration/components/widgets/topic-participant-test.js deleted file mode 100644 index d3e8bab7d1b..00000000000 --- a/app/assets/javascripts/discourse/tests/integration/components/widgets/topic-participant-test.js +++ /dev/null @@ -1,56 +0,0 @@ -import { render } from "@ember/test-helpers"; -import { hbs } from "ember-cli-htmlbars"; -import { module, test } from "qunit"; -import { setupRenderingTest } from "discourse/tests/helpers/component-test"; -import { exists } from "discourse/tests/helpers/qunit-helpers"; - -module( - "Integration | Component | Widget | topic-participant", - function (hooks) { - setupRenderingTest(hooks); - - test("one post", async function (assert) { - this.set("args", { - username: "test", - avatar_template: "/images/avatar.png", - post_count: 1, - }); - - await render( - hbs`` - ); - - assert.ok(exists("a.poster.trigger-user-card")); - assert.ok(!exists("span.post-count"), "don't show count for only 1 post"); - assert.ok(!exists(".avatar-flair"), "no avatar flair"); - }); - - test("many posts, a primary group with flair", async function (assert) { - this.set("args", { - username: "test", - avatar_template: "/images/avatar.png", - post_count: 2, - primary_group_name: "devs", - flair_name: "devs", - flair_url: "/images/d-logo-sketch-small.png", - flair_bg_color: "222", - flair_group_id: "41", - }); - - await render( - hbs`` - ); - - assert.ok(exists("a.poster.trigger-user-card")); - assert.ok(exists("span.post-count"), "show count for many posts"); - assert.ok( - exists(".group-devs a.poster"), - "add class for the group outside the link" - ); - assert.ok( - exists(".avatar-flair.avatar-flair-devs"), - "show flair with group class" - ); - }); - } -);