DEV: Refactor gjs topic-list (#29792)
Based on the initial review in #28934
This commit is contained in:
parent
bfe0eccdd9
commit
433543a516
|
@ -0,0 +1,26 @@
|
||||||
|
const Header = <template>
|
||||||
|
<tr>
|
||||||
|
{{#each @columns as |entry|}}
|
||||||
|
<entry.value.header
|
||||||
|
@sortable={{@sortable}}
|
||||||
|
@activeOrder={{@order}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@category={{@category}}
|
||||||
|
@name={{@listTitle}}
|
||||||
|
@bulkSelectEnabled={{@bulkSelectEnabled}}
|
||||||
|
@showBulkToggle={{@toggleInTitle}}
|
||||||
|
@canBulkSelect={{@canBulkSelect}}
|
||||||
|
@canDoBulkActions={{@canDoBulkActions}}
|
||||||
|
@showTopicsAndRepliesToggle={{@showTopicsAndRepliesToggle}}
|
||||||
|
@newListSubset={{@newListSubset}}
|
||||||
|
@newRepliesCount={{@newRepliesCount}}
|
||||||
|
@newTopicsCount={{@newTopicsCount}}
|
||||||
|
@bulkSelectHelper={{@bulkSelectHelper}}
|
||||||
|
@changeNewListSubset={{@changeNewListSubset}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</tr>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default Header;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import SortableColumn from "./sortable-column";
|
||||||
|
|
||||||
|
const ActivityCell = <template>
|
||||||
|
<SortableColumn
|
||||||
|
@sortable={{@sortable}}
|
||||||
|
@number="true"
|
||||||
|
@order="activity"
|
||||||
|
@activeOrder={{@activeOrder}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@name="activity"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default ActivityCell;
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
const BulkSelectCell = <template>
|
||||||
|
<th class="bulk-select topic-list-data">
|
||||||
|
{{#if @canBulkSelect}}
|
||||||
|
<button
|
||||||
|
{{on "click" @bulkSelectHelper.toggleBulkSelect}}
|
||||||
|
title={{i18n "topics.bulk.toggle"}}
|
||||||
|
class="btn-flat bulk-select"
|
||||||
|
>
|
||||||
|
{{icon "list-check"}}
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
</th>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default BulkSelectCell;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import SortableColumn from "./sortable-column";
|
||||||
|
|
||||||
|
const LikesCell = <template>
|
||||||
|
<SortableColumn
|
||||||
|
@sortable={{@sortable}}
|
||||||
|
@number="true"
|
||||||
|
@order="likes"
|
||||||
|
@activeOrder={{@activeOrder}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@name="likes"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default LikesCell;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import SortableColumn from "./sortable-column";
|
||||||
|
|
||||||
|
const OpLikesCell = <template>
|
||||||
|
<SortableColumn
|
||||||
|
@sortable={{@sortable}}
|
||||||
|
@number="true"
|
||||||
|
@order="op_likes"
|
||||||
|
@activeOrder={{@activeOrder}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@name="likes"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default OpLikesCell;
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import SortableColumn from "./sortable-column";
|
||||||
|
|
||||||
|
const PostersCell = <template>
|
||||||
|
<SortableColumn
|
||||||
|
@order="posters"
|
||||||
|
@activeOrder={{@activeOrder}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@name="posters"
|
||||||
|
@screenreaderOnly={{true}}
|
||||||
|
aria-label={{i18n "category.sort_options.posters"}}
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default PostersCell;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import SortableColumn from "./sortable-column";
|
||||||
|
|
||||||
|
const RepliesCell = <template>
|
||||||
|
<SortableColumn
|
||||||
|
@sortable={{@sortable}}
|
||||||
|
@number="true"
|
||||||
|
@order="posts"
|
||||||
|
@activeOrder={{@activeOrder}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@name="replies"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default RepliesCell;
|
|
@ -10,7 +10,7 @@ import concatClass from "discourse/helpers/concat-class";
|
||||||
import icon from "discourse-common/helpers/d-icon";
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
import { i18n } from "discourse-i18n";
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
export default class TopicListHeaderColumn extends Component {
|
export default class SortableColumn extends Component {
|
||||||
@service router;
|
@service router;
|
||||||
|
|
||||||
get localizedName() {
|
get localizedName() {
|
||||||
|
@ -49,7 +49,8 @@ export default class TopicListHeaderColumn extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onClick() {
|
onClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
this.args.changeSort(this.args.order);
|
this.args.changeSort(this.args.order);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import SortableColumn from "./sortable-column";
|
||||||
|
|
||||||
|
const TopicCell = <template>
|
||||||
|
<SortableColumn
|
||||||
|
@order="default"
|
||||||
|
@category={{@category}}
|
||||||
|
@activeOrder={{@activeOrder}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@name={{@name}}
|
||||||
|
@bulkSelectEnabled={{@bulkSelectEnabled}}
|
||||||
|
@showBulkToggle={{@showBulkToggle}}
|
||||||
|
@canBulkSelect={{@canBulkSelect}}
|
||||||
|
@canDoBulkActions={{@canDoBulkActions}}
|
||||||
|
@showTopicsAndRepliesToggle={{@showTopicsAndRepliesToggle}}
|
||||||
|
@newListSubset={{@newListSubset}}
|
||||||
|
@newRepliesCount={{@newRepliesCount}}
|
||||||
|
@newTopicsCount={{@newTopicsCount}}
|
||||||
|
@bulkSelectHelper={{@bulkSelectHelper}}
|
||||||
|
@changeNewListSubset={{@changeNewListSubset}}
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default TopicCell;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import SortableColumn from "./sortable-column";
|
||||||
|
|
||||||
|
const ViewsCell = <template>
|
||||||
|
<SortableColumn
|
||||||
|
@sortable={{@sortable}}
|
||||||
|
@number="true"
|
||||||
|
@order="views"
|
||||||
|
@activeOrder={{@activeOrder}}
|
||||||
|
@changeSort={{@changeSort}}
|
||||||
|
@ascending={{@ascending}}
|
||||||
|
@name="views"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default ViewsCell;
|
|
@ -6,13 +6,9 @@ import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||||
import { next } from "@ember/runloop";
|
import { next } from "@ember/runloop";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { modifier } from "ember-modifier";
|
import { modifier } from "ember-modifier";
|
||||||
import { eq, gt } from "truth-helpers";
|
import { eq } from "truth-helpers";
|
||||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||||
import ActivityColumn from "discourse/components/topic-list/activity-column";
|
|
||||||
import PostCountOrBadges from "discourse/components/topic-list/post-count-or-badges";
|
import PostCountOrBadges from "discourse/components/topic-list/post-count-or-badges";
|
||||||
import PostersColumn from "discourse/components/topic-list/posters-column";
|
|
||||||
import PostsCountColumn from "discourse/components/topic-list/posts-count-column";
|
|
||||||
import TopicCell from "discourse/components/topic-list/topic-cell";
|
|
||||||
import TopicExcerpt from "discourse/components/topic-list/topic-excerpt";
|
import TopicExcerpt from "discourse/components/topic-list/topic-excerpt";
|
||||||
import TopicLink from "discourse/components/topic-list/topic-link";
|
import TopicLink from "discourse/components/topic-list/topic-link";
|
||||||
import TopicStatus from "discourse/components/topic-status";
|
import TopicStatus from "discourse/components/topic-status";
|
||||||
|
@ -22,14 +18,12 @@ import categoryLink from "discourse/helpers/category-link";
|
||||||
import concatClass from "discourse/helpers/concat-class";
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
import discourseTags from "discourse/helpers/discourse-tags";
|
import discourseTags from "discourse/helpers/discourse-tags";
|
||||||
import formatDate from "discourse/helpers/format-date";
|
import formatDate from "discourse/helpers/format-date";
|
||||||
import number from "discourse/helpers/number";
|
|
||||||
import topicFeaturedLink from "discourse/helpers/topic-featured-link";
|
import topicFeaturedLink from "discourse/helpers/topic-featured-link";
|
||||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
import icon from "discourse-common/helpers/d-icon";
|
|
||||||
import { i18n } from "discourse-i18n";
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
export default class TopicListItem extends Component {
|
export default class Item extends Component {
|
||||||
@service historyStore;
|
@service historyStore;
|
||||||
@service site;
|
@service site;
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
|
@ -119,13 +113,11 @@ export default class TopicListItem extends Component {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
this.args.selected.addObject(this.args.topic);
|
this.args.selected.addObject(this.args.topic);
|
||||||
|
|
||||||
if (this.args.lastCheckedElementId && e.shiftKey) {
|
if (this.args.bulkSelectHelper.lastCheckedElementId && e.shiftKey) {
|
||||||
const bulkSelects = Array.from(
|
const bulkSelects = [...document.querySelectorAll("input.bulk-select")];
|
||||||
document.querySelectorAll("input.bulk-select")
|
|
||||||
);
|
|
||||||
const from = bulkSelects.indexOf(e.target);
|
const from = bulkSelects.indexOf(e.target);
|
||||||
const to = bulkSelects.findIndex(
|
const to = bulkSelects.findIndex(
|
||||||
(el) => el.id === this.args.lastCheckedElementId
|
(el) => el.id === this.args.bulkSelectHelper.lastCheckedElementId
|
||||||
);
|
);
|
||||||
const start = Math.min(from, to);
|
const start = Math.min(from, to);
|
||||||
const end = Math.max(from, to);
|
const end = Math.max(from, to);
|
||||||
|
@ -136,10 +128,10 @@ export default class TopicListItem extends Component {
|
||||||
.forEach((checkbox) => checkbox.click());
|
.forEach((checkbox) => checkbox.click());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.args.updateLastCheckedElementId(e.target.id);
|
this.args.bulkSelectHelper.lastCheckedElementId = e.target.id;
|
||||||
} else {
|
} else {
|
||||||
this.args.selected.removeObject(this.args.topic);
|
this.args.selected.removeObject(this.args.topic);
|
||||||
this.args.updateLastCheckedElementId(null);
|
this.args.bulkSelectHelper.lastCheckedElementId = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,78 +215,20 @@ export default class TopicListItem extends Component {
|
||||||
@outletArgs={{hash topic=@topic}}
|
@outletArgs={{hash topic=@topic}}
|
||||||
/>
|
/>
|
||||||
{{#if this.site.desktopView}}
|
{{#if this.site.desktopView}}
|
||||||
{{! TODO: column DAG "topic-list-before-columns" }}
|
{{#each @columns as |entry|}}
|
||||||
|
<entry.value.item
|
||||||
{{#if @bulkSelectEnabled}}
|
|
||||||
<td class="bulk-select topic-list-data">
|
|
||||||
<label for="bulk-select-{{@topic.id}}">
|
|
||||||
<input
|
|
||||||
{{on "click" this.onBulkSelectToggle}}
|
|
||||||
checked={{this.isSelected}}
|
|
||||||
type="checkbox"
|
|
||||||
id="bulk-select-{{@topic.id}}"
|
|
||||||
class="bulk-select"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<TopicCell
|
|
||||||
@topic={{@topic}}
|
@topic={{@topic}}
|
||||||
|
@bulkSelectEnabled={{@bulkSelectEnabled}}
|
||||||
|
@onBulkSelectToggle={{this.onBulkSelectToggle}}
|
||||||
|
@isSelected={{this.isSelected}}
|
||||||
@showTopicPostBadges={{@showTopicPostBadges}}
|
@showTopicPostBadges={{@showTopicPostBadges}}
|
||||||
@hideCategory={{@hideCategory}}
|
@hideCategory={{@hideCategory}}
|
||||||
@tagsForUser={{@tagsForUser}}
|
@tagsForUser={{@tagsForUser}}
|
||||||
@expandPinned={{this.expandPinned}}
|
@expandPinned={{this.expandPinned}}
|
||||||
/>
|
/>
|
||||||
|
{{/each}}
|
||||||
<PluginOutlet
|
|
||||||
@name="topic-list-after-main-link"
|
|
||||||
@outletArgs={{hash topic=@topic}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#if @showPosters}}
|
|
||||||
<PostersColumn @posters={{@topic.featuredUsers}} />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<PostsCountColumn @topic={{@topic}} />
|
|
||||||
|
|
||||||
{{#if @showLikes}}
|
|
||||||
<td class="num likes topic-list-data">
|
|
||||||
{{#if (gt @topic.like_count 0)}}
|
|
||||||
<a href={{@topic.summaryUrl}}>
|
|
||||||
{{number @topic.like_count}}
|
|
||||||
{{icon "heart"}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if @showOpLikes}}
|
|
||||||
<td class="num likes">
|
|
||||||
{{#if (gt @topic.op_like_count 0)}}
|
|
||||||
<a href={{@topic.summaryUrl}}>
|
|
||||||
{{number @topic.op_like_count}}
|
|
||||||
{{icon "heart"}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<td class={{concatClass "num views topic-list-data" @topic.viewsHeat}}>
|
|
||||||
<PluginOutlet
|
|
||||||
@name="topic-list-before-view-count"
|
|
||||||
@outletArgs={{hash topic=@topic}}
|
|
||||||
/>
|
|
||||||
{{number @topic.views numberKey="views_long"}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<ActivityColumn @topic={{@topic}} class="num topic-list-data" />
|
|
||||||
|
|
||||||
{{! TODO: column DAG "topic-list-after-columns" }}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<td class="topic-list-data">
|
<td class="topic-list-data">
|
||||||
{{! TODO: column DAG "topic-list-before-columns" }}
|
|
||||||
|
|
||||||
<div class="pull-left">
|
<div class="pull-left">
|
||||||
{{#if @bulkSelectEnabled}}
|
{{#if @bulkSelectEnabled}}
|
||||||
<label for="bulk-select-{{@topic.id}}">
|
<label for="bulk-select-{{@topic.id}}">
|
|
@ -5,20 +5,17 @@ import coldAgeClass from "discourse/helpers/cold-age-class";
|
||||||
import concatClass from "discourse/helpers/concat-class";
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
import formatDate from "discourse/helpers/format-date";
|
import formatDate from "discourse/helpers/format-date";
|
||||||
|
|
||||||
const ActivityColumn = <template>
|
const ActivityCell = <template>
|
||||||
<td
|
<td
|
||||||
title={{htmlSafe @topic.bumpedAtTitle}}
|
title={{htmlSafe @topic.bumpedAtTitle}}
|
||||||
class={{concatClass
|
class={{concatClass
|
||||||
"activity"
|
"activity num topic-list-data"
|
||||||
(coldAgeClass @topic.createdAt startDate=@topic.bumpedAt class="")
|
(coldAgeClass @topic.createdAt startDate=@topic.bumpedAt class="")
|
||||||
}}
|
}}
|
||||||
...attributes
|
|
||||||
>
|
>
|
||||||
<a
|
<a href={{@topic.lastPostUrl}} class="post-activity">
|
||||||
href={{@topic.lastPostUrl}}
|
{{~! no whitespace ~}}
|
||||||
class="post-activity"
|
<PluginOutlet
|
||||||
>{{! no whitespace
|
|
||||||
}}<PluginOutlet
|
|
||||||
@name="topic-list-before-relative-date"
|
@name="topic-list-before-relative-date"
|
||||||
@outletArgs={{hash topic=@topic}}
|
@outletArgs={{hash topic=@topic}}
|
||||||
/>
|
/>
|
||||||
|
@ -26,4 +23,5 @@ const ActivityColumn = <template>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</template>;
|
</template>;
|
||||||
export default ActivityColumn;
|
|
||||||
|
export default ActivityCell;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
|
||||||
|
const BulkSelectCell = <template>
|
||||||
|
<td class="bulk-select topic-list-data">
|
||||||
|
<label for="bulk-select-{{@topic.id}}">
|
||||||
|
<input
|
||||||
|
{{on "click" @onBulkSelectToggle}}
|
||||||
|
checked={{@isSelected}}
|
||||||
|
type="checkbox"
|
||||||
|
id="bulk-select-{{@topic.id}}"
|
||||||
|
class="bulk-select"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default BulkSelectCell;
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { gt } from "truth-helpers";
|
||||||
|
import number from "discourse/helpers/number";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
|
||||||
|
const LikesCell = <template>
|
||||||
|
<td class="num likes topic-list-data">
|
||||||
|
{{#if (gt @topic.like_count 0)}}
|
||||||
|
<a href={{@topic.summaryUrl}}>
|
||||||
|
{{number @topic.like_count}}
|
||||||
|
{{icon "heart"}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default LikesCell;
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { gt } from "truth-helpers";
|
||||||
|
import number from "discourse/helpers/number";
|
||||||
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
|
||||||
|
const OpLikesCell = <template>
|
||||||
|
<td class="num likes">
|
||||||
|
{{#if (gt @topic.op_like_count 0)}}
|
||||||
|
<a href={{@topic.summaryUrl}}>
|
||||||
|
{{number @topic.op_like_count}}
|
||||||
|
{{icon "heart"}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default OpLikesCell;
|
|
@ -1,8 +1,8 @@
|
||||||
import avatar from "discourse/helpers/avatar";
|
import avatar from "discourse/helpers/avatar";
|
||||||
|
|
||||||
const PostersColumn = <template>
|
const PostersCell = <template>
|
||||||
<td class="posters topic-list-data">
|
<td class="posters topic-list-data">
|
||||||
{{#each @posters as |poster|}}
|
{{#each @topic.featuredUsers as |poster|}}
|
||||||
{{#if poster.moreCount}}
|
{{#if poster.moreCount}}
|
||||||
<a class="posters-more-count">{{poster.moreCount}}</a>
|
<a class="posters-more-count">{{poster.moreCount}}</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -22,4 +22,4 @@ const PostersColumn = <template>
|
||||||
</td>
|
</td>
|
||||||
</template>;
|
</template>;
|
||||||
|
|
||||||
export default PostersColumn;
|
export default PostersCell;
|
|
@ -6,7 +6,7 @@ import element from "discourse/helpers/element";
|
||||||
import number from "discourse/helpers/number";
|
import number from "discourse/helpers/number";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
export default class PostsCountColumn extends Component {
|
export default class RepliesCell extends Component {
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
|
|
||||||
get ratio() {
|
get ratio() {
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { hash } from "@ember/helper";
|
||||||
|
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||||
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
import number from "discourse/helpers/number";
|
||||||
|
|
||||||
|
const ViewsCell = <template>
|
||||||
|
<td class={{concatClass "num views topic-list-data" @topic.viewsHeat}}>
|
||||||
|
<PluginOutlet
|
||||||
|
@name="topic-list-before-view-count"
|
||||||
|
@outletArgs={{hash topic=@topic}}
|
||||||
|
/>
|
||||||
|
{{number @topic.views numberKey="views_long"}}
|
||||||
|
</td>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default ViewsCell;
|
|
@ -1,7 +1,7 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { concat, hash } from "@ember/helper";
|
import { concat, hash } from "@ember/helper";
|
||||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||||
import PostsCountColumn from "discourse/components/topic-list/posts-count-column";
|
import ItemRepliesCell from "discourse/components/topic-list/item/replies-cell";
|
||||||
import TopicPostBadges from "discourse/components/topic-post-badges";
|
import TopicPostBadges from "discourse/components/topic-post-badges";
|
||||||
import TopicStatus from "discourse/components/topic-status";
|
import TopicStatus from "discourse/components/topic-status";
|
||||||
import UserAvatarFlair from "discourse/components/user-avatar-flair";
|
import UserAvatarFlair from "discourse/components/user-avatar-flair";
|
||||||
|
@ -75,7 +75,7 @@ export default class LatestTopicListItem extends Component {
|
||||||
@outletArgs={{hash topic=@topic}}
|
@outletArgs={{hash topic=@topic}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PostsCountColumn @topic={{@topic}} @tagName="div" />
|
<ItemRepliesCell @topic={{@topic}} @tagName="div" />
|
||||||
|
|
||||||
<div class="topic-last-activity">
|
<div class="topic-last-activity">
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -1,18 +1,109 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { cached } from "@glimmer/tracking";
|
||||||
import { fn, hash } from "@ember/helper";
|
import { hash } from "@ember/helper";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { eq, or } from "truth-helpers";
|
import { eq, or } from "truth-helpers";
|
||||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||||
import TopicListHeader from "discourse/components/topic-list/topic-list-header";
|
import Header from "discourse/components/topic-list/header";
|
||||||
import TopicListItem from "discourse/components/topic-list/topic-list-item";
|
import Item from "discourse/components/topic-list/item";
|
||||||
import concatClass from "discourse/helpers/concat-class";
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
import DAG from "discourse/lib/dag";
|
||||||
|
import { applyValueTransformer } from "discourse/lib/transformer";
|
||||||
import { i18n } from "discourse-i18n";
|
import { i18n } from "discourse-i18n";
|
||||||
|
import HeaderActivityCell from "./header/activity-cell";
|
||||||
|
import HeaderBulkSelectCell from "./header/bulk-select-cell";
|
||||||
|
import HeaderLikesCell from "./header/likes-cell";
|
||||||
|
import HeaderOpLikesCell from "./header/op-likes-cell";
|
||||||
|
import HeaderPostersCell from "./header/posters-cell";
|
||||||
|
import HeaderRepliesCell from "./header/replies-cell";
|
||||||
|
import HeaderTopicCell from "./header/topic-cell";
|
||||||
|
import HeaderViewsCell from "./header/views-cell";
|
||||||
|
import ItemActivityCell from "./item/activity-cell";
|
||||||
|
import ItemBulkSelectCell from "./item/bulk-select-cell";
|
||||||
|
import ItemLikesCell from "./item/likes-cell";
|
||||||
|
import ItemOpLikesCell from "./item/op-likes-cell";
|
||||||
|
import ItemPostersCell from "./item/posters-cell";
|
||||||
|
import ItemRepliesCell from "./item/replies-cell";
|
||||||
|
import ItemTopicCell from "./item/topic-cell";
|
||||||
|
import ItemViewsCell from "./item/views-cell";
|
||||||
|
|
||||||
export default class TopicList extends Component {
|
export default class TopicList extends Component {
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
|
@service topicTrackingState;
|
||||||
|
|
||||||
@tracked lastCheckedElementId;
|
@cached
|
||||||
|
get columns() {
|
||||||
|
const defaultColumns = new DAG({
|
||||||
|
// Allow customizations to replace just a header cell or just an item cell
|
||||||
|
onReplaceItem(_, newValue, oldValue) {
|
||||||
|
newValue.header ??= oldValue.header;
|
||||||
|
newValue.item ??= oldValue.item;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.bulkSelectEnabled) {
|
||||||
|
defaultColumns.add("bulk-select", {
|
||||||
|
header: HeaderBulkSelectCell,
|
||||||
|
item: ItemBulkSelectCell,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultColumns.add("topic", {
|
||||||
|
header: HeaderTopicCell,
|
||||||
|
item: ItemTopicCell,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.args.showPosters) {
|
||||||
|
defaultColumns.add("posters", {
|
||||||
|
header: HeaderPostersCell,
|
||||||
|
item: ItemPostersCell,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultColumns.add("replies", {
|
||||||
|
header: HeaderRepliesCell,
|
||||||
|
item: ItemRepliesCell,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.args.order === "likes") {
|
||||||
|
defaultColumns.add("likes", {
|
||||||
|
header: HeaderLikesCell,
|
||||||
|
item: ItemLikesCell,
|
||||||
|
});
|
||||||
|
} else if (this.args.order === "op_likes") {
|
||||||
|
defaultColumns.add("op-likes", {
|
||||||
|
header: HeaderOpLikesCell,
|
||||||
|
item: ItemOpLikesCell,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultColumns.add("views", {
|
||||||
|
header: HeaderViewsCell,
|
||||||
|
item: ItemViewsCell,
|
||||||
|
});
|
||||||
|
|
||||||
|
defaultColumns.add("activity", {
|
||||||
|
header: HeaderActivityCell,
|
||||||
|
item: ItemActivityCell,
|
||||||
|
});
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
const context = {
|
||||||
|
get category() {
|
||||||
|
return self.topicTrackingState.get("filterCategory");
|
||||||
|
},
|
||||||
|
|
||||||
|
get filter() {
|
||||||
|
return self.topicTrackingState.get("filter");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return applyValueTransformer(
|
||||||
|
"topic-list-columns",
|
||||||
|
defaultColumns,
|
||||||
|
context
|
||||||
|
).resolve();
|
||||||
|
}
|
||||||
|
|
||||||
get selected() {
|
get selected() {
|
||||||
return this.args.bulkSelectHelper?.selected;
|
return this.args.bulkSelectHelper?.selected;
|
||||||
|
@ -34,12 +125,8 @@ export default class TopicList extends Component {
|
||||||
return !!this.args.changeSort;
|
return !!this.args.changeSort;
|
||||||
}
|
}
|
||||||
|
|
||||||
get showLikes() {
|
get showTopicPostBadges() {
|
||||||
return this.args.order === "likes";
|
return this.args.showTopicPostBadges ?? true;
|
||||||
}
|
|
||||||
|
|
||||||
get showOpLikes() {
|
|
||||||
return this.args.order === "op_likes";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get lastVisitedTopic() {
|
get lastVisitedTopic() {
|
||||||
|
@ -83,10 +170,6 @@ export default class TopicList extends Component {
|
||||||
return lastVisitedTopic;
|
return lastVisitedTopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
get showTopicPostBadges() {
|
|
||||||
return this.args.showTopicPostBadges ?? true;
|
|
||||||
}
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
{{! template-lint-disable table-groups }}
|
{{! template-lint-disable table-groups }}
|
||||||
<table
|
<table
|
||||||
|
@ -97,21 +180,19 @@ export default class TopicList extends Component {
|
||||||
>
|
>
|
||||||
<caption class="sr-only">{{i18n "sr_topic_list_caption"}}</caption>
|
<caption class="sr-only">{{i18n "sr_topic_list_caption"}}</caption>
|
||||||
<thead class="topic-list-header">
|
<thead class="topic-list-header">
|
||||||
<TopicListHeader
|
<Header
|
||||||
|
@columns={{this.columns}}
|
||||||
@canBulkSelect={{@canBulkSelect}}
|
@canBulkSelect={{@canBulkSelect}}
|
||||||
@toggleInTitle={{this.toggleInTitle}}
|
@toggleInTitle={{this.toggleInTitle}}
|
||||||
@category={{@category}}
|
@category={{@category}}
|
||||||
@hideCategory={{@hideCategory}}
|
@hideCategory={{@hideCategory}}
|
||||||
@showPosters={{@showPosters}}
|
|
||||||
@showLikes={{this.showLikes}}
|
|
||||||
@showOpLikes={{this.showOpLikes}}
|
|
||||||
@order={{@order}}
|
@order={{@order}}
|
||||||
@changeSort={{@changeSort}}
|
@changeSort={{@changeSort}}
|
||||||
@ascending={{@ascending}}
|
@ascending={{@ascending}}
|
||||||
@sortable={{this.sortable}}
|
@sortable={{this.sortable}}
|
||||||
@listTitle={{or @listTitle "topic.title"}}
|
@listTitle={{or @listTitle "topic.title"}}
|
||||||
@bulkSelectEnabled={{this.bulkSelectEnabled}}
|
|
||||||
@bulkSelectHelper={{@bulkSelectHelper}}
|
@bulkSelectHelper={{@bulkSelectHelper}}
|
||||||
|
@bulkSelectEnabled={{this.bulkSelectEnabled}}
|
||||||
@canDoBulkActions={{this.canDoBulkActions}}
|
@canDoBulkActions={{this.canDoBulkActions}}
|
||||||
@showTopicsAndRepliesToggle={{@showTopicsAndRepliesToggle}}
|
@showTopicsAndRepliesToggle={{@showTopicsAndRepliesToggle}}
|
||||||
@newListSubset={{@newListSubset}}
|
@newListSubset={{@newListSubset}}
|
||||||
|
@ -135,20 +216,17 @@ export default class TopicList extends Component {
|
||||||
|
|
||||||
<tbody class="topic-list-body">
|
<tbody class="topic-list-body">
|
||||||
{{#each @topics as |topic index|}}
|
{{#each @topics as |topic index|}}
|
||||||
<TopicListItem
|
<Item
|
||||||
|
@columns={{this.columns}}
|
||||||
@topic={{topic}}
|
@topic={{topic}}
|
||||||
|
@bulkSelectHelper={{@bulkSelectHelper}}
|
||||||
@bulkSelectEnabled={{this.bulkSelectEnabled}}
|
@bulkSelectEnabled={{this.bulkSelectEnabled}}
|
||||||
@showTopicPostBadges={{this.showTopicPostBadges}}
|
@showTopicPostBadges={{this.showTopicPostBadges}}
|
||||||
@hideCategory={{@hideCategory}}
|
@hideCategory={{@hideCategory}}
|
||||||
@showPosters={{@showPosters}}
|
|
||||||
@showLikes={{this.showLikes}}
|
|
||||||
@showOpLikes={{this.showOpLikes}}
|
|
||||||
@expandGloballyPinned={{@expandGloballyPinned}}
|
@expandGloballyPinned={{@expandGloballyPinned}}
|
||||||
@expandAllPinned={{@expandAllPinned}}
|
@expandAllPinned={{@expandAllPinned}}
|
||||||
@lastVisitedTopic={{this.lastVisitedTopic}}
|
@lastVisitedTopic={{this.lastVisitedTopic}}
|
||||||
@selected={{this.selected}}
|
@selected={{this.selected}}
|
||||||
@lastCheckedElementId={{this.lastCheckedElementId}}
|
|
||||||
@updateLastCheckedElementId={{fn (mut this.lastCheckedElementId)}}
|
|
||||||
@tagsForUser={{@tagsForUser}}
|
@tagsForUser={{@tagsForUser}}
|
||||||
@focusLastVisitedTopic={{@focusLastVisitedTopic}}
|
@focusLastVisitedTopic={{@focusLastVisitedTopic}}
|
||||||
@index={{index}}
|
@index={{index}}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { and } from "truth-helpers";
|
import { and } from "truth-helpers";
|
||||||
import PostsCountColumn from "discourse/components/topic-list/posts-count-column";
|
import ItemRepliesCell from "discourse/components/topic-list/item/replies-cell";
|
||||||
import TopicPostBadges from "discourse/components/topic-post-badges";
|
import TopicPostBadges from "discourse/components/topic-post-badges";
|
||||||
|
|
||||||
const PostCountOrBadges = <template>
|
const PostCountOrBadges = <template>
|
||||||
|
@ -10,7 +10,7 @@ const PostCountOrBadges = <template>
|
||||||
@url={{@topic.lastUnreadUrl}}
|
@url={{@topic.lastUnreadUrl}}
|
||||||
/>
|
/>
|
||||||
{{else}}
|
{{else}}
|
||||||
<PostsCountColumn @topic={{@topic}} @tagName="div" />
|
<ItemRepliesCell @topic={{@topic}} @tagName="div" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</template>;
|
</template>;
|
||||||
|
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
import { on } from "@ember/modifier";
|
|
||||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
|
||||||
import TopicListHeaderColumn from "discourse/components/topic-list/topic-list-header-column";
|
|
||||||
import icon from "discourse-common/helpers/d-icon";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
|
|
||||||
const TopicListHeader = <template>
|
|
||||||
<tr>
|
|
||||||
<PluginOutlet @name="topic-list-header-before" />
|
|
||||||
|
|
||||||
{{#if @bulkSelectEnabled}}
|
|
||||||
<th class="bulk-select topic-list-data">
|
|
||||||
{{#if @canBulkSelect}}
|
|
||||||
<button
|
|
||||||
{{on "click" @bulkSelectHelper.toggleBulkSelect}}
|
|
||||||
title={{i18n "topics.bulk.toggle"}}
|
|
||||||
class="btn-flat bulk-select"
|
|
||||||
>
|
|
||||||
{{icon "list-check"}}
|
|
||||||
</button>
|
|
||||||
{{/if}}
|
|
||||||
</th>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<TopicListHeaderColumn
|
|
||||||
@order="default"
|
|
||||||
@category={{@category}}
|
|
||||||
@activeOrder={{@order}}
|
|
||||||
@changeSort={{@changeSort}}
|
|
||||||
@ascending={{@ascending}}
|
|
||||||
@name={{@listTitle}}
|
|
||||||
@bulkSelectEnabled={{@bulkSelectEnabled}}
|
|
||||||
@showBulkToggle={{@toggleInTitle}}
|
|
||||||
@canBulkSelect={{@canBulkSelect}}
|
|
||||||
@canDoBulkActions={{@canDoBulkActions}}
|
|
||||||
@showTopicsAndRepliesToggle={{@showTopicsAndRepliesToggle}}
|
|
||||||
@newListSubset={{@newListSubset}}
|
|
||||||
@newRepliesCount={{@newRepliesCount}}
|
|
||||||
@newTopicsCount={{@newTopicsCount}}
|
|
||||||
@bulkSelectHelper={{@bulkSelectHelper}}
|
|
||||||
@changeNewListSubset={{@changeNewListSubset}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PluginOutlet @name="topic-list-header-after-main-link" />
|
|
||||||
|
|
||||||
{{#if @showPosters}}
|
|
||||||
<TopicListHeaderColumn
|
|
||||||
@order="posters"
|
|
||||||
@activeOrder={{@order}}
|
|
||||||
@changeSort={{@changeSort}}
|
|
||||||
@ascending={{@ascending}}
|
|
||||||
@name="posters"
|
|
||||||
@screenreaderOnly={{true}}
|
|
||||||
aria-label={{i18n "category.sort_options.posters"}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<TopicListHeaderColumn
|
|
||||||
@sortable={{@sortable}}
|
|
||||||
@number="true"
|
|
||||||
@order="posts"
|
|
||||||
@activeOrder={{@order}}
|
|
||||||
@changeSort={{@changeSort}}
|
|
||||||
@ascending={{@ascending}}
|
|
||||||
@name="replies"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#if @showLikes}}
|
|
||||||
<TopicListHeaderColumn
|
|
||||||
@sortable={{@sortable}}
|
|
||||||
@number="true"
|
|
||||||
@order="likes"
|
|
||||||
@activeOrder={{@order}}
|
|
||||||
@changeSort={{@changeSort}}
|
|
||||||
@ascending={{@ascending}}
|
|
||||||
@name="likes"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if @showOpLikes}}
|
|
||||||
<TopicListHeaderColumn
|
|
||||||
@sortable={{@sortable}}
|
|
||||||
@number="true"
|
|
||||||
@order="op_likes"
|
|
||||||
@activeOrder={{@order}}
|
|
||||||
@changeSort={{@changeSort}}
|
|
||||||
@ascending={{@ascending}}
|
|
||||||
@name="likes"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<TopicListHeaderColumn
|
|
||||||
@sortable={{@sortable}}
|
|
||||||
@number="true"
|
|
||||||
@order="views"
|
|
||||||
@activeOrder={{@order}}
|
|
||||||
@changeSort={{@changeSort}}
|
|
||||||
@ascending={{@ascending}}
|
|
||||||
@name="views"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TopicListHeaderColumn
|
|
||||||
@sortable={{@sortable}}
|
|
||||||
@number="true"
|
|
||||||
@order="activity"
|
|
||||||
@activeOrder={{@order}}
|
|
||||||
@changeSort={{@changeSort}}
|
|
||||||
@ascending={{@ascending}}
|
|
||||||
@name="activity"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PluginOutlet @name="topic-list-header-after" />
|
|
||||||
</tr>
|
|
||||||
</template>;
|
|
||||||
|
|
||||||
export default TopicListHeader;
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { action } from "@ember/object";
|
||||||
import { getOwner, setOwner } from "@ember/owner";
|
import { getOwner, setOwner } from "@ember/owner";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
||||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||||
import Topic from "discourse/models/topic";
|
import Topic from "discourse/models/topic";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
export default class BulkSelectHelper {
|
export default class BulkSelectHelper {
|
||||||
@service router;
|
@service router;
|
||||||
|
@ -15,6 +15,7 @@ export default class BulkSelectHelper {
|
||||||
@tracked bulkSelectEnabled = false;
|
@tracked bulkSelectEnabled = false;
|
||||||
@tracked autoAddTopicsToBulkSelect = false;
|
@tracked autoAddTopicsToBulkSelect = false;
|
||||||
@tracked autoAddBookmarksToBulkSelect = false;
|
@tracked autoAddBookmarksToBulkSelect = false;
|
||||||
|
@tracked lastCheckedElementId = null;
|
||||||
|
|
||||||
selected = new TrackedArray();
|
selected = new TrackedArray();
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ export default class BulkSelectHelper {
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.selected.length = 0;
|
this.selected.length = 0;
|
||||||
|
this.lastCheckedElementId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
addTopics(topics) {
|
addTopics(topics) {
|
||||||
|
@ -34,8 +36,9 @@ export default class BulkSelectHelper {
|
||||||
return this.selected.mapBy("category_id").uniq();
|
return this.selected.mapBy("category_id").uniq();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
@action
|
||||||
toggleBulkSelect() {
|
toggleBulkSelect(event) {
|
||||||
|
event?.preventDefault();
|
||||||
this.bulkSelectEnabled = !this.bulkSelectEnabled;
|
this.bulkSelectEnabled = !this.bulkSelectEnabled;
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import DAGMap from "dag-map";
|
import DAGMap from "dag-map";
|
||||||
|
import { makeArray } from "discourse-common/lib/helpers";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
function ensureArray(val) {
|
|
||||||
return Array.isArray(val) ? val : [val];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class DAG {
|
export default class DAG {
|
||||||
/**
|
/**
|
||||||
* Creates a new DAG instance from an iterable of entries.
|
* Creates a new DAG instance from an iterable of entries.
|
||||||
|
@ -75,10 +72,10 @@ export default class DAG {
|
||||||
*/
|
*/
|
||||||
#defaultPositionForKey(key) {
|
#defaultPositionForKey(key) {
|
||||||
const pos = { ...this.#defaultPosition };
|
const pos = { ...this.#defaultPosition };
|
||||||
if (ensureArray(pos.before).includes(key)) {
|
if (makeArray(pos.before).includes(key)) {
|
||||||
delete pos.before;
|
delete pos.before;
|
||||||
}
|
}
|
||||||
if (ensureArray(pos.after).includes(key)) {
|
if (makeArray(pos.after).includes(key)) {
|
||||||
delete pos.after;
|
delete pos.after;
|
||||||
}
|
}
|
||||||
return pos;
|
return pos;
|
||||||
|
|
|
@ -15,4 +15,5 @@ export const VALUE_TRANSFORMERS = Object.freeze([
|
||||||
"more-topics-tabs",
|
"more-topics-tabs",
|
||||||
"post-menu-buttons",
|
"post-menu-buttons",
|
||||||
"small-user-attrs",
|
"small-user-attrs",
|
||||||
|
"topic-list-columns",
|
||||||
]);
|
]);
|
||||||
|
|
Loading…
Reference in New Issue