mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -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 {
|
export default class SidebarTopicsSection extends GlimmerComponent {
|
||||||
get sectionLinks() {
|
configuredSectionLinks = [...DEFAULT_SECTION_LINKS, ...customSectionLinks];
|
||||||
return [...DEFAULT_SECTION_LINKS, ...customSectionLinks].map(
|
|
||||||
(sectionLinkClass) => {
|
sectionLinks = this.configuredSectionLinks.map((sectionLinkClass) => {
|
||||||
return new sectionLinkClass({
|
return new sectionLinkClass({
|
||||||
topicTrackingState: this.topicTrackingState,
|
topicTrackingState: this.topicTrackingState,
|
||||||
currentUser: this.currentUser,
|
currentUser: this.currentUser,
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
willDestroy() {
|
||||||
|
this.sectionLinks.forEach((sectionLink) => sectionLink.teardown());
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -7,6 +7,11 @@ export default class BaseSectionLink {
|
|||||||
this.currentUser = currentUser;
|
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.
|
* @returns {string} The name of the section link. Needs to be dasherized and lowercase.
|
||||||
*/
|
*/
|
||||||
|
@ -1,44 +1,35 @@
|
|||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
|
||||||
import BaseSectionLink from "discourse/lib/sidebar/topics-section/base-section-link";
|
import BaseSectionLink from "discourse/lib/sidebar/topics-section/base-section-link";
|
||||||
|
|
||||||
export default class EverythingSectionLink extends BaseSectionLink {
|
export default class EverythingSectionLink extends BaseSectionLink {
|
||||||
@tracked totalUnread = 0;
|
@tracked totalUnread = 0;
|
||||||
@tracked totalNew = 0;
|
@tracked totalNew = 0;
|
||||||
|
callbackId = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
|
|
||||||
this._refreshCounts();
|
this.callbackId = this.topicTrackingState.onStateChange(
|
||||||
|
this._refreshCounts
|
||||||
this.topicTrackingState.onStateChange(
|
|
||||||
this._topicTrackingStateUpdated.bind(this)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._refreshCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
_topicTrackingStateUpdated() {
|
teardown() {
|
||||||
// refreshing section counts by looping through the states in topicTrackingState is an expensive operation so
|
this.topicTrackingState.offStateChange(this.callbackId);
|
||||||
// we debounce this.
|
|
||||||
discourseDebounce(this, this._refreshCounts, 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bind
|
||||||
_refreshCounts() {
|
_refreshCounts() {
|
||||||
let totalUnread = 0;
|
this.totalUnread = this.topicTrackingState.countUnread();
|
||||||
let totalNew = 0;
|
|
||||||
|
|
||||||
this.topicTrackingState.forEachTracked((topic, isNew, isUnread) => {
|
if (this.totalUnread === 0) {
|
||||||
if (isNew) {
|
this.totalNew = this.topicTrackingState.countNew();
|
||||||
totalNew += 1;
|
}
|
||||||
} else if (isUnread) {
|
|
||||||
totalUnread += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.totalUnread = totalUnread;
|
|
||||||
this.totalNew = totalNew;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
|
@ -1,14 +1,43 @@
|
|||||||
import I18n from "I18n";
|
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 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 {
|
export default class TrackedSectionLink extends BaseSectionLink {
|
||||||
get name() {
|
@tracked totalUnread = 0;
|
||||||
return "tracked";
|
@tracked totalNew = 0;
|
||||||
|
callbackId = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
|
||||||
|
this.callbackId = this.topicTrackingState.onStateChange(
|
||||||
|
this._refreshCounts
|
||||||
|
);
|
||||||
|
this._refreshCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
get route() {
|
teardown() {
|
||||||
return "discovery.latest";
|
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() {
|
get query() {
|
||||||
@ -22,4 +51,32 @@ export default class TrackedSectionLink extends BaseSectionLink {
|
|||||||
get text() {
|
get text() {
|
||||||
return I18n.t("sidebar.sections.topics.links.tracked.content");
|
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 topicFixtures from "discourse/tests/fixtures/discovery-fixtures";
|
||||||
import { cloneJSON } from "discourse-common/lib/object";
|
import { cloneJSON } from "discourse-common/lib/object";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
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) {
|
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) => {
|
needs.pretender((server, helper) => {
|
||||||
server.get("/new.json", () => {
|
server.get("/new.json", () => {
|
||||||
@ -322,7 +329,7 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
|||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
query(".sidebar-section-link-everything").href.endsWith("/unread"),
|
query(".sidebar-section-link-everything").href.endsWith("/unread"),
|
||||||
"is links to unread filter"
|
"it links to unread filter"
|
||||||
);
|
);
|
||||||
|
|
||||||
// simulate reading topic 2
|
// simulate reading topic 2
|
||||||
@ -380,7 +387,7 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
|||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
query(".sidebar-section-link-everything").href.endsWith("/new"),
|
query(".sidebar-section-link-everything").href.endsWith("/new"),
|
||||||
"is links to new filter"
|
"it links to new filter"
|
||||||
);
|
);
|
||||||
|
|
||||||
publishToMessageBus("/unread", {
|
publishToMessageBus("/unread", {
|
||||||
@ -404,7 +411,242 @@ acceptance("Sidebar - Topics Section", function (needs) {
|
|||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
query(".sidebar-section-link-everything").href.endsWith("/latest"),
|
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