FEATURE: Display new/unread count for tracked categories in exp sidebar (#17046)

This commit is contained in:
Alan Guo Xiang Tan 2022-06-09 13:43:17 +08:00 committed by GitHub
parent cd8c97debc
commit 946f8a65fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 302 additions and 5 deletions

View File

@ -1,10 +1,20 @@
import { cached } from "@glimmer/tracking";
import GlimmerComponent from "discourse/components/glimmer";
import CategorySectionLink from "discourse/lib/sidebar/categories-section/category-section-link";
export default class SidebarCategoriesSection extends GlimmerComponent {
@cached
get sectionLinks() {
return this.site.trackedCategoriesList.map((trackedCategory) => {
return new CategorySectionLink({ category: trackedCategory });
return new CategorySectionLink({
category: trackedCategory,
topicTrackingState: this.topicTrackingState,
});
});
}
willDestroy() {
this.sectionLinks.forEach((sectionLink) => sectionLink.teardown());
}
}

View File

@ -1,11 +1,42 @@
import I18n from "I18n";
import { htmlSafe } from "@ember/template";
import { tracked } from "@glimmer/tracking";
import { bind } from "discourse-common/utils/decorators";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import Category from "discourse/models/category";
export default class CategorySectionLink {
constructor({ category }) {
@tracked totalUnread = 0;
@tracked totalNew = 0;
constructor({ category, topicTrackingState }) {
this.category = category;
this.topicTrackingState = topicTrackingState;
this.callbackId = this.topicTrackingState.onStateChange(
this._refreshCounts
);
this._refreshCounts();
}
teardown() {
this.topicTrackingState.offStateChange(this.callbackId);
}
@bind
_refreshCounts() {
this.totalUnread = this.topicTrackingState.countUnread({
categoryId: this.category.id,
});
if (this.totalUnread === 0) {
this.totalNew = this.topicTrackingState.countNew({
categoryId: this.category.id,
});
}
}
get name() {
@ -31,4 +62,26 @@ export default class CategorySectionLink {
get text() {
return htmlSafe(categoryBadgeHTML(this.category, { link: false }));
}
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 "discovery.unreadCategory";
} else if (this.totalNew > 0) {
return "discovery.newCategory";
} else {
return "discovery.latestCategory";
}
}
}

View File

@ -89,7 +89,12 @@ const Site = RestModel.extend({
for (const category of categories) {
if (category.isTracked) {
trackedCategories.push(category);
if (
!this.siteSettings.suppress_uncategorized_badge ||
category.id !== this.uncategorized_category_id
) {
trackedCategories.push(category);
}
}
}

View File

@ -13,7 +13,8 @@
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@model={{sectionLink.model}}>
@model={{sectionLink.model}}
@badgeText={{sectionLink.badgeText}} >
</Sidebar::SectionLink>
{{/each}}
{{else}}

View File

@ -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,
} from "discourse/tests/helpers/qunit-helpers";
@ -17,9 +18,57 @@ import discoveryFixture from "discourse/tests/fixtures/discovery-fixtures";
import categoryFixture from "discourse/tests/fixtures/category-fixtures";
import { cloneJSON } from "discourse-common/lib/object";
acceptance(
"Sidebar - Categories Section - suppress_uncategorized_badge enabled",
function (needs) {
needs.settings({
suppress_uncategorized_badge: true,
});
needs.user({ experimental_sidebar_enabled: true });
conditionalTest(
"uncategorized category is not shown",
!isLegacyEmber(),
async function (assert) {
const categories = Site.current().categories;
const category1 = categories[0];
const uncategorizedCategory = categories.find((category) => {
return category.id === Site.current().uncategorized_category_id;
});
category1.set("notification_level", NotificationLevels.TRACKING);
uncategorizedCategory.set(
"notification_level",
NotificationLevels.TRACKING
);
await visit("/");
assert.strictEqual(
queryAll(".sidebar-section-categories .sidebar-section-link").length,
1,
"there should only be one section link under the section"
);
assert.ok(
exists(`.sidebar-section-link-${category1.slug}`),
`only the ${category1.slug} section link is shown`
);
}
);
}
);
acceptance("Sidebar - Categories Section", function (needs) {
needs.user({ experimental_sidebar_enabled: true });
needs.settings({
suppress_uncategorized_badge: false,
});
needs.pretender((server, helper) => {
["latest", "top", "new", "unread"].forEach((type) => {
server.get(`/c/:categorySlug/:categoryId/l/${type}.json`, () => {
@ -77,6 +126,30 @@ acceptance("Sidebar - Categories Section", function (needs) {
}
);
conditionalTest(
"uncategorized category is shown when tracked",
!isLegacyEmber(),
async function (assert) {
const categories = Site.current().categories;
const uncategorizedCategory = categories.find((category) => {
return category.id === Site.current().uncategorized_category_id;
});
uncategorizedCategory.set(
"notification_level",
NotificationLevels.TRACKING
);
await visit("/");
assert.ok(
exists(`.sidebar-section-link-${uncategorizedCategory.slug}`),
`displays the section link for ${uncategorizedCategory.slug} category`
);
}
);
conditionalTest(
"category section links for tracked categories",
!isLegacyEmber(),
@ -263,4 +336,159 @@ acceptance("Sidebar - Categories Section", function (needs) {
);
}
);
conditionalTest(
"new and unread count for categories link",
!isLegacyEmber(),
async function (assert) {
const { category1, category2 } = setupTrackedCategories();
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: category1.id,
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: 2,
highest_post_number: 12,
last_read_post_number: 11,
created_at: "2020-02-09T09:40:02.672Z",
category_id: category1.id,
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",
},
{
topic_id: 3,
highest_post_number: 15,
last_read_post_number: 14,
created_at: "2021-06-14T12:41:02.477Z",
category_id: category2.id,
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",
},
{
topic_id: 4,
highest_post_number: 17,
last_read_post_number: 16,
created_at: "2020-10-31T03:41:42.257Z",
category_id: category2.id,
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",
},
]);
await visit("/");
assert.strictEqual(
query(
`.sidebar-section-link-${category1.slug} .sidebar-section-link-content-badge`
).textContent.trim(),
I18n.t("sidebar.unread_count", { count: 1 }),
`displays 1 unread count for ${category1.slug} section link`
);
assert.strictEqual(
query(
`.sidebar-section-link-${category2.slug} .sidebar-section-link-content-badge`
).textContent.trim(),
I18n.t("sidebar.unread_count", { count: 2 }),
`displays 2 unread count for ${category2.slug} 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-${category1.slug} .sidebar-section-link-content-badge`
).textContent.trim(),
I18n.t("sidebar.new_count", { count: 1 }),
`displays 1 new count for ${category1.slug} 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-${category1.slug} .sidebar-section-link-content-badge`
),
`does not display any badge ${category1.slug} section link`
);
publishToMessageBus("/unread", {
topic_id: 3,
message_type: "read",
payload: {
last_read_post_number: 15,
highest_post_number: 15,
},
});
await settled();
assert.strictEqual(
query(
`.sidebar-section-link-${category2.slug} .sidebar-section-link-content-badge`
).textContent.trim(),
I18n.t("sidebar.unread_count", { count: 1 }),
`displays 1 unread count for ${category2.slug} section link`
);
}
);
conditionalTest(
"clean up topic tracking state state changed callbacks when section is destroyed",
!isLegacyEmber(),
async function (assert) {
setupTrackedCategories();
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
);
}
);
});