This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/app/widgets/header-topic-info.js
jbrw a2fcc360dd
UX: Show group card with animated loading state (#15253)
* Remove _calculateTopOffset entirely

* Show group card with animated loading state

Showing the animated loading state before rending the actual content prevents an
awkward scroll position jump when displaying this card.

This mimics the behaviour of the user card (which uses the same `CardContentsBase` mixin).

* Fix two user card issues

1. A JS console error (with no consequences) when clicking a group mention
2. User cards weren't being loaded from the header (for example, for PMs)

Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
2021-12-15 13:47:31 -05:00

243 lines
6.6 KiB
JavaScript

import { applyDecorators, createWidget } from "discourse/widgets/widget";
import DiscourseURL from "discourse/lib/url";
import I18n from "I18n";
import RawHtml from "discourse/widgets/raw-html";
import { avatarImg } from "discourse/widgets/post";
import getURL from "discourse-common/lib/get-url";
import { h } from "virtual-dom";
import { iconNode } from "discourse-common/lib/icon-library";
import renderTags from "discourse/lib/render-tags";
import { topicFeaturedLinkNode } from "discourse/lib/render-topic-featured-link";
createWidget("topic-header-participant", {
tagName: "span",
buildClasses(attrs) {
return `trigger-${attrs.type}-card`;
},
html(attrs) {
const { user, group } = attrs;
let content, url;
if (attrs.type === "user") {
content = avatarImg("tiny", {
template: user.avatar_template,
username: user.username,
});
url = user.get("path");
} else {
content = [iconNode("users")];
url = getURL(`/g/${group.name}`);
content.push(h("span", group.name));
}
return h(
"a.icon",
{
attributes: {
href: url,
"data-auto-route": true,
title: attrs.username,
},
},
content
);
},
click(e) {
this.appEvents.trigger(
`topic-header:trigger-${this.attrs.type}-card`,
this.attrs.username,
e.target
);
e.preventDefault();
},
});
export default createWidget("header-topic-info", {
tagName: "div.extra-info-wrapper",
contents: null,
title: null,
buildClasses(attrs, state) {
this.buildAttributes(attrs, state);
return this.containerClassName();
},
buildFancyTitleClass() {
const baseClass = ["topic-link"];
const flatten = (array) => [].concat.apply([], array);
const extraClass = flatten(
applyDecorators(this, "fancyTitleClass", this.attrs, this.state)
);
return baseClass.concat(extraClass).filter(Boolean).join(" ");
},
buildAttributes(attrs, state) {
const topic = attrs.topic;
const heading = [];
const showPM = !topic.get("is_warning") && topic.get("isPrivateMessage");
if (showPM) {
const href = this.currentUser && this.currentUser.pmPath(topic);
if (href) {
heading.push(
h(
"a.private-message-glyph-wrapper",
{
attributes: { href, "aria-label": I18n.t("user.messages.inbox") },
},
iconNode("envelope", { class: "private-message-glyph" })
)
);
}
}
const loaded = topic.get("details.loaded");
const fancyTitle = topic.get("fancyTitle");
const href = topic.get("url");
if (fancyTitle && href) {
heading.push(this.attach("topic-status", attrs));
const titleHTML = new RawHtml({ html: `<span>${fancyTitle}</span>` });
heading.push(
this.attach("link", {
className: this.buildFancyTitleClass(),
action: "jumpToTopPost",
href,
attributes: { "data-topic-id": topic.get("id") },
contents: () => titleHTML,
})
);
}
this.headerElements = [h("h1.header-title", heading)];
const category = topic.get("category");
if (loaded || category) {
if (
category &&
(!category.isUncategorizedCategory ||
!this.siteSettings.suppress_uncategorized_badge)
) {
const parentCategory = category.get("parentCategory");
const categories = [];
if (parentCategory) {
if (
this.siteSettings.max_category_nesting > 2 &&
!this.site.mobileView
) {
const grandParentCategory = parentCategory.get("parentCategory");
if (grandParentCategory) {
categories.push(
this.attach("category-link", { category: grandParentCategory })
);
}
}
categories.push(
this.attach("category-link", { category: parentCategory })
);
}
categories.push(this.attach("category-link", { category }));
this.headerElements.push(h("div.categories-wrapper", categories));
}
let extra = [];
const tags = renderTags(topic);
if (tags && tags.length > 0) {
extra.push(new RawHtml({ html: tags }));
}
if (showPM) {
const maxHeaderParticipants = extra.length > 0 ? 5 : 10;
const participants = [];
const topicDetails = topic.get("details");
const totalParticipants =
topicDetails.allowed_users.length +
topicDetails.allowed_groups.length;
topicDetails.allowed_users.some((user) => {
if (participants.length >= maxHeaderParticipants) {
return true;
}
participants.push(
this.attach("topic-header-participant", {
type: "user",
user,
username: user.username,
})
);
});
topicDetails.allowed_groups.some((group) => {
if (participants.length >= maxHeaderParticipants) {
return true;
}
participants.push(
this.attach("topic-header-participant", {
type: "group",
group,
username: group.name,
})
);
});
if (totalParticipants > maxHeaderParticipants) {
const remaining = totalParticipants - maxHeaderParticipants;
participants.push(
this.attach("link", {
className: "more-participants",
action: "jumpToTopPost",
href,
attributes: { "data-topic-id": topic.get("id") },
contents: () => `+${remaining}`,
})
);
}
extra.push(h("div.topic-header-participants", participants));
}
extra = extra.concat(applyDecorators(this, "after-tags", attrs, state));
if (this.siteSettings.topic_featured_link_enabled) {
const featured = topicFeaturedLinkNode(attrs.topic);
if (featured) {
extra.push(featured);
}
}
if (extra.length) {
this.headerElements.push(h("div.topic-header-extra", extra));
}
}
this.contents = h("div.title-wrapper", this.headerElements);
},
html() {
return h(
"div.extra-info",
{ className: this.containerClassName() },
this.contents
);
},
containerClassName() {
return this.headerElements.length > 1 ? "two-rows" : "";
},
jumpToTopPost() {
const topic = this.attrs.topic;
if (topic) {
DiscourseURL.routeTo(topic.get("firstPostUrl"), {
keepFilter: true,
});
}
},
});