Files
discourse-themes/themes/-2_horizon/javascript_discourse_components_card_high-context-topic-card.gjs

331 lines
10 KiB
Plaintext

import Component from "@glimmer/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { themePrefix } from "virtual:theme";
import PluginOutlet from "discourse/components/plugin-outlet";
import BulkSelectCheckbox from "discourse/components/topic-list/bulk-select-checkbox";
import TopicExcerpt from "discourse/components/topic-list/topic-excerpt";
import TopicLink from "discourse/components/topic-list/topic-link";
import UnreadIndicator from "discourse/components/topic-list/unread-indicator";
import TopicPostBadges from "discourse/components/topic-post-badges";
import TopicStatus from "discourse/components/topic-status";
import lazyHash from "discourse/helpers/lazy-hash";
import topicFeaturedLink from "discourse/helpers/topic-featured-link";
import { shortDateNoYear } from "discourse/lib/formatter";
import dAvatar from "discourse/ui-kit/helpers/d-avatar";
import { categoryLinkHTML } from "discourse/ui-kit/helpers/d-category-link";
import dConcatClass from "discourse/ui-kit/helpers/d-concat-class";
import dDiscourseTags from "discourse/ui-kit/helpers/d-discourse-tags";
import dFormatDate from "discourse/ui-kit/helpers/d-format-date";
import dIcon from "discourse/ui-kit/helpers/d-icon";
import dNumber from "discourse/ui-kit/helpers/d-number";
import { i18n } from "discourse-i18n";
import { topicWasUpdatedAfterLastPost } from "../../lib/topic-activity";
import { getTopicStatusBadge } from "../../lib/topic-status-badge";
export default class HighContextTopicCard extends Component {
@service capabilities;
get hasSolved() {
return (
this.args.topic.has_accepted_answer || this.args.topic.accepted_answer
);
}
get hasVotes() {
return this.args.topic.can_vote && this.args.topic.vote_count > 0;
}
get voteCountLabel() {
return i18n(themePrefix("vote_count"), {
count: this.args.topic.vote_count,
});
}
get hasAssigned() {
return this.args.topic.assigned_to_user || this.hasIndirectAssignments;
}
get assignedUser() {
return this.args.topic.assigned_to_user;
}
get hasIndirectAssignments() {
const indirect = this.args.topic.indirectly_assigned_to;
return indirect && Object.keys(indirect).length > 0;
}
get indirectAssignees() {
const indirect = this.args.topic.indirectly_assigned_to;
if (!indirect) {
return [];
}
return Object.values(indirect).map((assignment) => ({
user: assignment.assigned_to,
postNumber: assignment.post_number,
}));
}
get hasTags() {
return this.args.topic.tags?.length > 0;
}
get hasReplies() {
return this.args.topic.replyCount > 0;
}
get hasLikes() {
return this.args.topic.like_count > 0;
}
get replyCountLabel() {
return i18n(themePrefix("reply_count"), {
count: this.args.topic.replyCount,
});
}
get likeCountLabel() {
return i18n(themePrefix("like_count"), {
count: this.args.topic.like_count,
});
}
get hasExcerpt() {
return this.args.topic.excerpt || this.args.topic.hasExcerpt;
}
get statusBadge() {
return getTopicStatusBadge(this.args.topic);
}
get topicCreator() {
return this.args.topic.creator;
}
get hasLastReplyContext() {
return this.hasReplies && !topicWasUpdatedAfterLastPost(this.args.topic);
}
get hasContext() {
return this.hasLastReplyContext || this.hasAssigned;
}
get lastPoster() {
return {
user: this.args.topic.lastPosterUser,
username: this.args.topic.last_poster_username,
};
}
get topicTimestamp() {
return shortDateNoYear(new Date(this.args.topic.created_at));
}
@action
onTitleFocus(event) {
event.target.closest(".topic-list-item").classList.add("selected");
}
@action
onTitleBlur(event) {
event.target.closest(".topic-list-item").classList.remove("selected");
}
<template>
{{#if @bulkSelectEnabled}}
<BulkSelectCheckbox
@topic={{@topic}}
@isSelected={{@isSelected}}
@onToggle={{@onBulkSelectToggle}}
class="hc-topic-card__bulk-select"
/>
{{/if}}
<td class="hc-topic-card">
<div class="hc-topic-card__header">
<div class="hc-topic-card__op">
<div class="hc-topic-card__avatar">
{{dAvatar this.topicCreator imageSize="medium"}}
</div>
<div class="hc-topic-card__op-info">
<span class="hc-topic-card__op-timestamp">
{{i18n (themePrefix "posted")}}
{{this.topicTimestamp}}
</span>
<span class="hc-topic-card__op-name">
{{i18n
(themePrefix "by_username")
username=this.topicCreator.username
}}</span>
</div>
</div>
<div class="hc-topic-card__status-tags">
{{#if this.hasSolved}}
<span
class="hc-topic-card__status --solved"
aria-label={{i18n (themePrefix "solved")}}
>
{{#if this.capabilities.viewport.sm}}
{{i18n (themePrefix "solved")}}
{{/if}}
{{dIcon "square-check"}}
</span>
{{/if}}
{{#if this.statusBadge}}
<span
class={{dConcatClass
"hc-topic-card__status"
this.statusBadge.className
}}
aria-label={{i18n this.statusBadge.text}}
>
{{dIcon this.statusBadge.icon}}
{{#if this.capabilities.viewport.sm}}
<span class="hc-topic-card__status-text">{{i18n
this.statusBadge.text
}}</span>
{{/if}}
</span>
{{/if}}
</div>
</div>
<div class="hc-topic-card__content">
<div class="hc-topic-card__title" role="heading" aria-level="2">
<TopicStatus @topic={{@topic}} @context="topic-list" />
<TopicLink
{{on "focus" this.onTitleFocus}}
{{on "blur" this.onTitleBlur}}
@topic={{@topic}}
class="hc-topic-card__title raw-link raw-topic-link"
/>
{{~#if @topic.featured_link~}}
&nbsp;{{topicFeaturedLink @topic}}
{{~/if~}}
<PluginOutlet
@name="topic-list-after-title"
@outletArgs={{lazyHash topic=@topic}}
/>
<UnreadIndicator @topic={{@topic}} />
<TopicPostBadges
@unreadPosts={{@topic.unread_posts}}
@unseen={{@topic.unseen}}
@url={{@topic.lastUnreadUrl}}
/>
</div>
{{#if this.hasExcerpt}}
<TopicExcerpt @topic={{@topic}} class="hc-topic-card__excerpt" />
{{/if}}
</div>
{{#if this.hasContext}}
<div class="hc-topic-card__context">
{{#if this.hasLastReplyContext}}
<div class="hc-topic-card__last-reply">
{{dAvatar this.lastPoster.user imageSize="tiny"}}
<span
class="hc-topic-card__last-reply-name"
>{{this.lastPoster.username}}</span>
<span>{{i18n (themePrefix "replied")}}</span>
<span class="hc-topic-card__time">
{{dFormatDate @topic.last_posted_at leaveAgo="true"}}
</span>
</div>
{{/if}}
{{#if this.hasAssigned}}
{{#if this.assignedUser}}
<div class="hc-topic-card__assigned">
{{dIcon "user-plus"}}
<span class="sr-only">{{i18n
"discourse_assign.assigned_to"
}}</span>
<span
class="hc-topic-card__assigned-name"
>{{this.assignedUser.username}}</span>
</div>
{{/if}}
{{#each this.indirectAssignees as |assignment|}}
<div class="hc-topic-card__assigned">
{{dIcon "user-plus"}}
<span class="sr-only">{{i18n
"discourse_assign.assigned_to"
}}</span>
<span
class="hc-topic-card__assigned-name"
>{{assignment.user.username}}</span>
<span
class="hc-topic-card__assigned-post"
>#{{assignment.postNumber}}</span>
</div>
{{/each}}
{{/if}}
</div>
{{/if}}
<div class="hc-topic-card__footer">
<div class="hc-topic-card__category-tags">
{{#unless @hideCategory}}
<div class="hc-topic-card__category">
{{categoryLinkHTML @topic.category}}
</div>
{{/unless}}
{{#if this.hasTags}}
{{dDiscourseTags
@topic
mode="list"
className="hc-topic-card__tags"
}}
{{/if}}
</div>
<div class="hc-topic-card__stats">
{{#if this.hasVotes}}
<span
class="hc-topic-card__votes"
aria-label={{this.voteCountLabel}}
title={{this.voteCountLabel}}
>
{{dIcon "check-to-slot" skipTitle=true}}
<span class="hc-topic-card__votes-count">{{dNumber
@topic.vote_count
}}</span>
</span>
{{/if}}
{{#if this.hasReplies}}
<span
class="hc-topic-card__replies"
aria-label={{this.replyCountLabel}}
title={{this.replyCountLabel}}
>
{{dIcon "reply" skipTitle=true}}
<span class="hc-topic-card__count">{{dNumber
@topic.replyCount
}}</span>
</span>
{{/if}}
{{#if this.hasLikes}}
<span
class="hc-topic-card__likes"
aria-label={{this.likeCountLabel}}
title={{this.likeCountLabel}}
>
{{dIcon "heart" skipTitle=true}}
<span class="hc-topic-card__count">{{dNumber
@topic.like_count
}}</span>
</span>
{{/if}}
</div>
</div>
</td>
</template>
}