FEATURE: Add new/unread counts to tags section links exp sidebar (#17057)
This commit is contained in:
parent
95fa4c5d52
commit
e9a77e7f19
|
@ -4,10 +4,27 @@ import GlimmerComponent from "discourse/components/glimmer";
|
|||
import TagSectionLink from "discourse/lib/sidebar/tags-section/tag-section-link";
|
||||
|
||||
export default class SidebarTagsSection extends GlimmerComponent {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.callbackId = this.topicTrackingState.onStateChange(() => {
|
||||
this.sectionLinks.forEach((sectionLink) => {
|
||||
sectionLink.refreshCounts();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
this.topicTrackingState.offStateChange(this.callbackId);
|
||||
}
|
||||
|
||||
@cached
|
||||
get sectionLinks() {
|
||||
return this.currentUser.trackedTags.map((trackedTag) => {
|
||||
return new TagSectionLink({ tag: trackedTag });
|
||||
return new TagSectionLink({
|
||||
tag: trackedTag,
|
||||
topicTrackingState: this.topicTrackingState,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,30 @@
|
|||
import I18n from "I18n";
|
||||
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
|
||||
export default class TagSectionLink {
|
||||
constructor({ tag }) {
|
||||
@tracked totalUnread = 0;
|
||||
@tracked totalNew = 0;
|
||||
|
||||
constructor({ tag, topicTrackingState }) {
|
||||
this.tag = tag;
|
||||
this.topicTrackingState = topicTrackingState;
|
||||
this.refreshCounts();
|
||||
}
|
||||
|
||||
@bind
|
||||
refreshCounts() {
|
||||
this.totalUnread = this.topicTrackingState.countUnread({
|
||||
tagId: this.tag,
|
||||
});
|
||||
|
||||
if (this.totalUnread === 0) {
|
||||
this.totalNew = this.topicTrackingState.countNew({
|
||||
tagId: this.tag,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
|
@ -22,4 +46,26 @@ export default class TagSectionLink {
|
|||
get text() {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
get badgeText() {
|
||||
if (this.totalUnread > 0) {
|
||||
return I18n.t("sidebar.unread_count", {
|
||||
count: this.totalUnread,
|
||||
});
|
||||
} else if (this.totalNew > 0) {
|
||||
return I18n.t("sidebar.new_count", {
|
||||
count: this.totalNew,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get route() {
|
||||
if (this.totalUnread > 0) {
|
||||
return "tag.showUnread";
|
||||
} else if (this.totalNew > 0) {
|
||||
return "tag.showNew";
|
||||
} else {
|
||||
return "tag.show";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
@title={{sectionLink.title}}
|
||||
@content={{sectionLink.text}}
|
||||
@currentWhen={{sectionLink.currentWhen}}
|
||||
@badgeText={{sectionLink.badgeText}}
|
||||
@model={{sectionLink.model}}>
|
||||
</Sidebar::SectionLink>
|
||||
{{/each}}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import I18n from "I18n";
|
||||
|
||||
import { click, currentURL, visit } from "@ember/test-helpers";
|
||||
import { click, currentURL, settled, visit } from "@ember/test-helpers";
|
||||
|
||||
import {
|
||||
acceptance,
|
||||
conditionalTest,
|
||||
exists,
|
||||
publishToMessageBus,
|
||||
query,
|
||||
queryAll,
|
||||
updateCurrentUser,
|
||||
|
@ -226,4 +227,147 @@ acceptance("Sidebar - Tags section", function (needs) {
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"new and unread count for tag section links",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
this.container.lookup("topic-tracking-state:main").loadStates([
|
||||
{
|
||||
topic_id: 1,
|
||||
highest_post_number: 1,
|
||||
last_read_post_number: null,
|
||||
created_at: "2022-05-11T03:09:31.959Z",
|
||||
category_id: 1,
|
||||
notification_level: null,
|
||||
created_in_new_period: true,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
tags: ["tag1"],
|
||||
},
|
||||
{
|
||||
topic_id: 2,
|
||||
highest_post_number: 12,
|
||||
last_read_post_number: 11,
|
||||
created_at: "2020-02-09T09:40:02.672Z",
|
||||
category_id: 2,
|
||||
notification_level: 2,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
tags: ["tag1"],
|
||||
},
|
||||
{
|
||||
topic_id: 3,
|
||||
highest_post_number: 15,
|
||||
last_read_post_number: 14,
|
||||
created_at: "2021-06-14T12:41:02.477Z",
|
||||
category_id: 3,
|
||||
notification_level: 2,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
tags: ["tag2"],
|
||||
},
|
||||
{
|
||||
topic_id: 4,
|
||||
highest_post_number: 17,
|
||||
last_read_post_number: 16,
|
||||
created_at: "2020-10-31T03:41:42.257Z",
|
||||
category_id: 4,
|
||||
notification_level: 2,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
tags: ["tag4"],
|
||||
},
|
||||
]);
|
||||
|
||||
await visit("/");
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
`.sidebar-section-link-tag1 .sidebar-section-link-content-badge`
|
||||
).textContent.trim(),
|
||||
I18n.t("sidebar.unread_count", { count: 1 }),
|
||||
`displays 1 unread count for tag1 section link`
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
`.sidebar-section-link-tag2 .sidebar-section-link-content-badge`
|
||||
).textContent.trim(),
|
||||
I18n.t("sidebar.unread_count", { count: 1 }),
|
||||
`displays 1 unread count for tag2 section link`
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
!exists(
|
||||
`.sidebar-section-link-tag3 .sidebar-section-link-content-badge`
|
||||
),
|
||||
"does not display any badge for tag3 section link"
|
||||
);
|
||||
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 2,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 12,
|
||||
highest_post_number: 12,
|
||||
},
|
||||
});
|
||||
|
||||
await settled();
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
`.sidebar-section-link-tag1 .sidebar-section-link-content-badge`
|
||||
).textContent.trim(),
|
||||
I18n.t("sidebar.new_count", { count: 1 }),
|
||||
`displays 1 new count for tag1 section link`
|
||||
);
|
||||
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 1,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await settled();
|
||||
|
||||
assert.ok(
|
||||
!exists(
|
||||
`.sidebar-section-link-tag1 .sidebar-section-link-content-badge`
|
||||
),
|
||||
`does not display any badge tag1 section link`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"cleans up topic tracking state state changed callbacks when section is destroyed",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
const topicTrackingState = this.container.lookup(
|
||||
"topic-tracking-state:main"
|
||||
);
|
||||
|
||||
const initialCallbackCount = Object.keys(
|
||||
topicTrackingState.stateChangeCallbacks
|
||||
).length;
|
||||
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
await click(".header-sidebar-toggle .btn");
|
||||
|
||||
assert.strictEqual(
|
||||
Object.keys(topicTrackingState.stateChangeCallbacks).length,
|
||||
initialCallbackCount
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -278,7 +278,7 @@ class TopicTrackingState
|
|||
end
|
||||
|
||||
def self.include_tags_in_report?
|
||||
SiteSetting.tagging_enabled && @include_tags_in_report
|
||||
SiteSetting.tagging_enabled && (@include_tags_in_report || SiteSetting.enable_experimental_sidebar)
|
||||
end
|
||||
|
||||
def self.include_tags_in_report=(v)
|
||||
|
|
|
@ -9,7 +9,8 @@ class TopicTrackingStateSerializer < ApplicationSerializer
|
|||
:notification_level,
|
||||
:created_in_new_period,
|
||||
:unread_not_too_old,
|
||||
:treat_as_new_topic_start_date
|
||||
:treat_as_new_topic_start_date,
|
||||
:tags
|
||||
|
||||
def created_in_new_period
|
||||
return true if !scope
|
||||
|
@ -20,4 +21,8 @@ class TopicTrackingStateSerializer < ApplicationSerializer
|
|||
return true if object.first_unread_at.blank?
|
||||
object.updated_at >= object.first_unread_at
|
||||
end
|
||||
|
||||
def include_tags?
|
||||
object.respond_to?(:tags)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -539,14 +539,7 @@ describe TopicTrackingState do
|
|||
end
|
||||
|
||||
context "tag support" do
|
||||
after do
|
||||
# this is a bit of an odd hook, but this is a global change
|
||||
# used by plugins that leverage tagging heavily and need
|
||||
# tag information in topic tracking state
|
||||
TopicTrackingState.include_tags_in_report = false
|
||||
end
|
||||
|
||||
it "correctly handles tags" do
|
||||
before do
|
||||
SiteSetting.tagging_enabled = true
|
||||
|
||||
post.topic.notifier.watch_topic!(post.topic.user_id)
|
||||
|
@ -556,6 +549,27 @@ describe TopicTrackingState do
|
|||
Guardian.new(Discourse.system_user),
|
||||
['bananas', 'apples']
|
||||
)
|
||||
end
|
||||
|
||||
it "includes tags when SiteSetting.enable_experimental_sidebar is true" do
|
||||
report = TopicTrackingState.report(user)
|
||||
expect(report.length).to eq(1)
|
||||
row = report[0]
|
||||
expect(row.respond_to?(:tags)).to eq(false)
|
||||
|
||||
SiteSetting.enable_experimental_sidebar = true
|
||||
|
||||
report = TopicTrackingState.report(user)
|
||||
expect(report.length).to eq(1)
|
||||
row = report[0]
|
||||
expect(row.tags).to contain_exactly("apples", "bananas")
|
||||
end
|
||||
|
||||
it "includes tags when TopicTrackingState.include_tags_in_report option is enabled" do
|
||||
report = TopicTrackingState.report(user)
|
||||
expect(report.length).to eq(1)
|
||||
row = report[0]
|
||||
expect(row.respond_to? :tags).to eq(false)
|
||||
|
||||
TopicTrackingState.include_tags_in_report = true
|
||||
|
||||
|
@ -563,13 +577,8 @@ describe TopicTrackingState do
|
|||
expect(report.length).to eq(1)
|
||||
row = report[0]
|
||||
expect(row.tags).to contain_exactly("apples", "bananas")
|
||||
|
||||
ensure
|
||||
TopicTrackingState.include_tags_in_report = false
|
||||
|
||||
report = TopicTrackingState.report(user)
|
||||
expect(report.length).to eq(1)
|
||||
row = report[0]
|
||||
expect(row.respond_to? :tags).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe TopicTrackingStateSerializer do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
fab!(:post) { create_post }
|
||||
|
||||
it 'serializes topic tracking state reports' do
|
||||
report = TopicTrackingState.report(user)
|
||||
serialized = described_class.new(report[0], scope: Guardian.new(user), root: false).as_json
|
||||
|
||||
expect(serialized[:topic_id]).to eq(post.topic_id)
|
||||
expect(serialized[:highest_post_number]).to eq(post.topic.highest_post_number)
|
||||
expect(serialized[:last_read_post_number]).to eq(nil)
|
||||
expect(serialized[:created_at]).to be_present
|
||||
expect(serialized[:notification_level]).to eq(nil)
|
||||
expect(serialized[:created_in_new_period]).to eq(true)
|
||||
expect(serialized[:unread_not_too_old]).to eq(true)
|
||||
expect(serialized[:treat_as_new_topic_start_date]).to be_present
|
||||
expect(serialized.has_key?(:tags)).to eq(false)
|
||||
end
|
||||
|
||||
it "includes tags attribute when tags are present" do
|
||||
TopicTrackingState.include_tags_in_report = true
|
||||
|
||||
post.topic.notifier.watch_topic!(post.topic.user_id)
|
||||
|
||||
DiscourseTagging.tag_topic_by_names(
|
||||
post.topic,
|
||||
Guardian.new(Discourse.system_user),
|
||||
['bananas', 'apples']
|
||||
)
|
||||
|
||||
report = TopicTrackingState.report(user)
|
||||
serialized = described_class.new(report[0], scope: Guardian.new(user), root: false).as_json
|
||||
|
||||
expect(serialized[:tags]).to contain_exactly("bananas", "apples")
|
||||
ensure
|
||||
TopicTrackingState.include_tags_in_report = false
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue