mirror of
https://github.com/discourse/discourse.git
synced 2024-11-27 19:30:23 -06:00
DEV: Display new/unread count for tracked link in sidebar (#16957)
This commit is contained in:
parent
0fa0094531
commit
3b3f60218e
@ -17,15 +17,17 @@ const DEFAULT_SECTION_LINKS = [
|
||||
];
|
||||
|
||||
export default class SidebarTopicsSection extends GlimmerComponent {
|
||||
get sectionLinks() {
|
||||
return [...DEFAULT_SECTION_LINKS, ...customSectionLinks].map(
|
||||
(sectionLinkClass) => {
|
||||
return new sectionLinkClass({
|
||||
topicTrackingState: this.topicTrackingState,
|
||||
currentUser: this.currentUser,
|
||||
});
|
||||
}
|
||||
);
|
||||
configuredSectionLinks = [...DEFAULT_SECTION_LINKS, ...customSectionLinks];
|
||||
|
||||
sectionLinks = this.configuredSectionLinks.map((sectionLinkClass) => {
|
||||
return new sectionLinkClass({
|
||||
topicTrackingState: this.topicTrackingState,
|
||||
currentUser: this.currentUser,
|
||||
});
|
||||
});
|
||||
|
||||
willDestroy() {
|
||||
this.sectionLinks.forEach((sectionLink) => sectionLink.teardown());
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -7,6 +7,11 @@ export default class BaseSectionLink {
|
||||
this.currentUser = currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when topics-section component is torn down.
|
||||
*/
|
||||
teardown() {}
|
||||
|
||||
/**
|
||||
* @returns {string} The name of the section link. Needs to be dasherized and lowercase.
|
||||
*/
|
||||
|
@ -1,44 +1,35 @@
|
||||
import I18n from "I18n";
|
||||
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import BaseSectionLink from "discourse/lib/sidebar/topics-section/base-section-link";
|
||||
|
||||
export default class EverythingSectionLink extends BaseSectionLink {
|
||||
@tracked totalUnread = 0;
|
||||
@tracked totalNew = 0;
|
||||
callbackId = null;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this._refreshCounts();
|
||||
|
||||
this.topicTrackingState.onStateChange(
|
||||
this._topicTrackingStateUpdated.bind(this)
|
||||
this.callbackId = this.topicTrackingState.onStateChange(
|
||||
this._refreshCounts
|
||||
);
|
||||
|
||||
this._refreshCounts();
|
||||
}
|
||||
|
||||
_topicTrackingStateUpdated() {
|
||||
// refreshing section counts by looping through the states in topicTrackingState is an expensive operation so
|
||||
// we debounce this.
|
||||
discourseDebounce(this, this._refreshCounts, 100);
|
||||
teardown() {
|
||||
this.topicTrackingState.offStateChange(this.callbackId);
|
||||
}
|
||||
|
||||
@bind
|
||||
_refreshCounts() {
|
||||
let totalUnread = 0;
|
||||
let totalNew = 0;
|
||||
this.totalUnread = this.topicTrackingState.countUnread();
|
||||
|
||||
this.topicTrackingState.forEachTracked((topic, isNew, isUnread) => {
|
||||
if (isNew) {
|
||||
totalNew += 1;
|
||||
} else if (isUnread) {
|
||||
totalUnread += 1;
|
||||
}
|
||||
});
|
||||
|
||||
this.totalUnread = totalUnread;
|
||||
this.totalNew = totalNew;
|
||||
if (this.totalUnread === 0) {
|
||||
this.totalNew = this.topicTrackingState.countNew();
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
|
@ -1,14 +1,43 @@
|
||||
import I18n from "I18n";
|
||||
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import BaseSectionLink from "discourse/lib/sidebar/topics-section/base-section-link";
|
||||
import { isTrackedTopic } from "discourse/lib/topic-list-tracked-filter";
|
||||
|
||||
export default class TrackedSectionLink extends BaseSectionLink {
|
||||
get name() {
|
||||
return "tracked";
|
||||
@tracked totalUnread = 0;
|
||||
@tracked totalNew = 0;
|
||||
callbackId = null;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.callbackId = this.topicTrackingState.onStateChange(
|
||||
this._refreshCounts
|
||||
);
|
||||
this._refreshCounts();
|
||||
}
|
||||
|
||||
get route() {
|
||||
return "discovery.latest";
|
||||
teardown() {
|
||||
this.topicTrackingState.offStateChange(this.callbackId);
|
||||
}
|
||||
|
||||
@bind
|
||||
_refreshCounts() {
|
||||
this.totalUnread = this.topicTrackingState.countUnread({
|
||||
customFilterFn: isTrackedTopic,
|
||||
});
|
||||
|
||||
if (this.totalUnread === 0) {
|
||||
this.totalNew = this.topicTrackingState.countNew({
|
||||
customFilterFn: isTrackedTopic,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return "tracked";
|
||||
}
|
||||
|
||||
get query() {
|
||||
@ -22,4 +51,32 @@ export default class TrackedSectionLink extends BaseSectionLink {
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.topics.links.tracked.content");
|
||||
}
|
||||
|
||||
get currentWhen() {
|
||||
return "discovery.latest discovery.new discovery.unread discovery.top";
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
get route() {
|
||||
if (this.totalUnread > 0) {
|
||||
return "discovery.unread";
|
||||
} else if (this.totalNew > 0) {
|
||||
return "discovery.new";
|
||||
} else {
|
||||
return "discovery.latest";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,16 @@ import { isLegacyEmber } from "discourse-common/config/environment";
|
||||
import topicFixtures from "discourse/tests/fixtures/discovery-fixtures";
|
||||
import { cloneJSON } from "discourse-common/lib/object";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import Site from "discourse/models/site";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
|
||||
acceptance("Sidebar - Topics Section", function (needs) {
|
||||
needs.user({ experimental_sidebar_enabled: true });
|
||||
needs.user({
|
||||
experimental_sidebar_enabled: true,
|
||||
tracked_tags: ["tag1"],
|
||||
watched_tags: ["tag2"],
|
||||
watching_first_post_tags: ["tag3"],
|
||||
});
|
||||
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/new.json", () => {
|
||||
@ -322,7 +329,7 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-everything").href.endsWith("/unread"),
|
||||
"is links to unread filter"
|
||||
"it links to unread filter"
|
||||
);
|
||||
|
||||
// simulate reading topic 2
|
||||
@ -380,7 +387,7 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-everything").href.endsWith("/new"),
|
||||
"is links to new filter"
|
||||
"it links to new filter"
|
||||
);
|
||||
|
||||
publishToMessageBus("/unread", {
|
||||
@ -404,7 +411,242 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-everything").href.endsWith("/latest"),
|
||||
"is links to latest filter"
|
||||
"it links to latest filter"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting top route with tracked filter",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/top?f=tracked");
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(".sidebar-section-topics .sidebar-section-link-tracked.active"),
|
||||
"the tracked link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting unread route with tracked filter",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/unread?f=tracked");
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(".sidebar-section-topics .sidebar-section-link-tracked.active"),
|
||||
"the tracked link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"visiting new route with tracked filter",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
await visit("/new?f=tracked");
|
||||
|
||||
assert.strictEqual(
|
||||
queryAll(".sidebar-section-topics .sidebar-section-link.active").length,
|
||||
1,
|
||||
"only one link is marked as active"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(".sidebar-section-topics .sidebar-section-link-tracked.active"),
|
||||
"the tracked link is marked as active"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"new and unread count for tracked link",
|
||||
!isLegacyEmber(),
|
||||
async function (assert) {
|
||||
const categories = Site.current().categories;
|
||||
|
||||
// Category id 1001 has two subcategories
|
||||
const category = categories.find((c) => c.id === 1001);
|
||||
category.set("notification_level", NotificationLevels.TRACKING);
|
||||
|
||||
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: category.id,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
created_in_new_period: true,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
{
|
||||
topic_id: 2,
|
||||
highest_post_number: 12,
|
||||
last_read_post_number: 11,
|
||||
created_at: "2020-02-09T09:40:02.672Z",
|
||||
category_id: category.subcategories[0].id,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
{
|
||||
topic_id: 3,
|
||||
highest_post_number: 12,
|
||||
last_read_post_number: 11,
|
||||
created_at: "2020-02-09T09:40:02.672Z",
|
||||
category_id: category.subcategories[0].subcategories[0].id,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
{
|
||||
topic_id: 4,
|
||||
highest_post_number: 15,
|
||||
last_read_post_number: 14,
|
||||
created_at: "2021-06-14T12:41:02.477Z",
|
||||
category_id: 3,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
},
|
||||
{
|
||||
topic_id: 5,
|
||||
highest_post_number: 1,
|
||||
last_read_post_number: null,
|
||||
created_at: "2021-06-14T12:41:02.477Z",
|
||||
category_id: 3,
|
||||
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",
|
||||
},
|
||||
{
|
||||
topic_id: 6,
|
||||
highest_post_number: 17,
|
||||
last_read_post_number: 16,
|
||||
created_at: "2020-10-31T03:41:42.257Z",
|
||||
category_id: 1234,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
created_in_new_period: false,
|
||||
unread_not_too_old: true,
|
||||
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
|
||||
tags: ["tag3"],
|
||||
},
|
||||
]);
|
||||
|
||||
await visit("/");
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
".sidebar-section-link-tracked .sidebar-section-link-content-badge"
|
||||
).textContent.trim(),
|
||||
"3 unread",
|
||||
"it displays the right unread count"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-tracked").href.endsWith(
|
||||
"/unread?f=tracked"
|
||||
),
|
||||
"it links to unread url with tracked filter"
|
||||
);
|
||||
|
||||
// simulate reading topic id 2
|
||||
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-tracked .sidebar-section-link-content-badge"
|
||||
).textContent.trim(),
|
||||
"2 unread",
|
||||
"it updates the unread count"
|
||||
);
|
||||
|
||||
// simulate reading topic id 3
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 3,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 17,
|
||||
highest_post_number: 17,
|
||||
},
|
||||
});
|
||||
|
||||
// simulate reading topic id 6
|
||||
publishToMessageBus("/unread", {
|
||||
topic_id: 6,
|
||||
message_type: "read",
|
||||
payload: {
|
||||
last_read_post_number: 17,
|
||||
highest_post_number: 17,
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
".sidebar-section-link-tracked .sidebar-section-link-content-badge"
|
||||
).textContent.trim(),
|
||||
"1 new",
|
||||
"it displays the new count once there are no tracked unread topics"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-tracked").href.endsWith("/new?f=tracked"),
|
||||
"it links to new url with tracked filter"
|
||||
);
|
||||
|
||||
// simulate reading topic id 1
|
||||
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-tracked .sidebar-section-link-content-badge"
|
||||
),
|
||||
"it removes new count once there are no tracked new topics"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
query(".sidebar-section-link-tracked").href.endsWith(
|
||||
"/latest?f=tracked"
|
||||
),
|
||||
"it links to latest url with tracked filter"
|
||||
);
|
||||
}
|
||||
);
|
||||
@ -494,4 +736,28 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
conditionalTest(
|
||||
"clean 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
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user