DEV: refactor topic-map participants into glimmer component (#25350)

* DEV: refactor topic-map participants into glimmer components
This commit is contained in:
Kelv 2024-01-24 12:00:46 +08:00 committed by GitHub
parent 04d2ec45b4
commit 8b44270929
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 158 additions and 141 deletions

View File

@ -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);
}
<template>
<div class={{this.participantClasses}}>
<a
class={{this.linkClasses}}
data-user-card={{@participant.username}}
title={{@participant.username}}
href={{this.userUrl}}
>
{{this.avatarImage}}
{{#if (gt @participant.post_count 1)}}
<span class="post-count">{{@participant.post_count}}</span>
{{/if}}
<UserAvatarFlair @user={{@participant}} />
</a>
</div>
</template>
}

View File

@ -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);
<template>
{{@title}}
{{#each @participants as |participant|}}
<TopicParticipant
@participant={{participant}}
@toggledUsers={{this.toggledUsers}}
/>
{{/each}}
</template>
}

View File

@ -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,

View File

@ -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;
function renderParticipants(wrapperElement, title, userFilters, participants) {
return new RenderGlimmer(
this,
wrapperElement,
hbs`<TopicMap::TopicParticipants
@title={{@data.title}}
@participants={{@data.participants}}
@userFilters={{@data.userFilters}}
/>`,
{
title,
userFilters,
participants,
}
userFilters = userFilters || [];
return participants.map((p) => {
return this.attach("topic-participant", p, {
state: { toggled: userFilters.includes(p.username) },
});
});
);
}
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(`<h3>${I18n.t("topic_map.participants_title")}</h3>`),
attrs.userFilters,
attrs.participants
);
}
const result = [avatars];

View File

@ -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`<TopicMap::TopicParticipant @participant={{this.args}}/>`);
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`<TopicMap::TopicParticipant @participant={{this.args}}/>`);
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();
});
});

View File

@ -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`<MountWidget @widget="topic-participant" @args={{this.args}} />`
);
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`<MountWidget @widget="topic-participant" @args={{this.args}} />`
);
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"
);
});
}
);