UX: hide new/unread counts in sidebar, use dot by default (#18797)

This updates the behavior of the list destination setting for links in the sidebar.

By default, new/unread content will show a dot like chat, rather than the count of new/unread topics.

If a user chooses to link to new/unread in the sidebar, we'll show the count.

The goal here is to find a simple default for typical users (new/unread indication, no counts, default links) while providing a different workflow for power users (showing new/unread counts, and linking directly to new/unread).

Internal Ref: /t/82626
This commit is contained in:
Kris 2022-11-02 07:55:05 -04:00 committed by GitHub
parent 46e9f402eb
commit 2531828973
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 428 additions and 9 deletions

View File

@ -25,7 +25,10 @@
@model={{sectionLink.model}}
@models={{sectionLink.models}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}} />
@prefixValue={{sectionLink.prefixValue}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}}/>
{{/each}}
<Sidebar::MoreSectionLinks @sectionLinks={{this.moreSectionLinks}} @secondarySectionLinks={{this.moreSecondarySectionLinks}} />

View File

@ -21,7 +21,10 @@
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@prefixElementColors={{sectionLink.prefixElementColors}} >
@prefixElementColors={{sectionLink.prefixElementColors}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}} >
</Sidebar::SectionLink>
{{/each}}
{{else}}

View File

@ -17,7 +17,10 @@
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@badgeText={{sectionLink.badgeText}}
@models={{sectionLink.models}} >
@models={{sectionLink.models}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}} >
</Sidebar::SectionLink>
{{/each}}
{{else}}

View File

@ -27,6 +27,7 @@ export default class extends Controller {
@action
save() {
const initialSidebarCategoryIds = this.model.sidebarCategoryIds;
const initialSidebarListDestination = this.model.sidebar_list_destination;
this.model.set(
"sidebarCategoryIds",
@ -59,6 +60,9 @@ export default class extends Controller {
})
.finally(() => {
this.model.set("sidebar_tag_names", []);
if (initialSidebarListDestination !== this.newSidebarListDestination) {
window.location.reload();
}
});
}
}

View File

@ -7,6 +7,8 @@ import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sideb
export default class EverythingSectionLink extends BaseSectionLink {
@tracked totalUnread = 0;
@tracked totalNew = 0;
@tracked hideCount =
this.currentUser?.sidebarListDestination !== UNREAD_LIST_DESTINATION;
constructor() {
super(...arguments);
@ -50,6 +52,9 @@ export default class EverythingSectionLink extends BaseSectionLink {
}
get badgeText() {
if (this.hideCount) {
return;
}
if (this.totalUnread > 0) {
return I18n.t("sidebar.unread_count", {
count: this.totalUnread,
@ -78,4 +83,18 @@ export default class EverythingSectionLink extends BaseSectionLink {
get prefixValue() {
return "layer-group";
}
get suffixCSSClass() {
return "unread";
}
get suffixType() {
return "icon";
}
get suffixValue() {
if (this.hideCount && (this.totalUnread || this.totalNew)) {
return "circle";
}
}
}

View File

@ -9,6 +9,8 @@ import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sideb
export default class CategorySectionLink {
@tracked totalUnread = 0;
@tracked totalNew = 0;
@tracked hideCount =
this.currentUser?.sidebarListDestination !== UNREAD_LIST_DESTINATION;
constructor({ category, topicTrackingState, currentUser }) {
this.category = category;
@ -69,6 +71,9 @@ export default class CategorySectionLink {
}
get badgeText() {
if (this.hideCount) {
return;
}
if (this.totalUnread > 0) {
return I18n.t("sidebar.unread_count", {
count: this.totalUnread,
@ -91,4 +96,18 @@ export default class CategorySectionLink {
}
return "discovery.category";
}
get suffixCSSClass() {
return "unread";
}
get suffixType() {
return "icon";
}
get suffixValue() {
if (this.hideCount && (this.totalUnread || this.totalNew)) {
return "circle";
}
}
}

View File

@ -2,11 +2,14 @@ import I18n from "I18n";
import { tracked } from "@glimmer/tracking";
import BaseSectionLink from "discourse/lib/sidebar/base-community-section-link";
import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sidebar";
const USER_DRAFTS_CHANGED_EVENT = "user-drafts:changed";
export default class MyPostsSectionLink extends BaseSectionLink {
@tracked draftCount = this.currentUser.draft_count;
@tracked hideCount =
this.currentUser?.sidebarListDestination !== UNREAD_LIST_DESTINATION;
constructor() {
super(...arguments);
@ -52,7 +55,7 @@ export default class MyPostsSectionLink extends BaseSectionLink {
}
get badgeText() {
if (this._hasDraft) {
if (this._hasDraft && !this.hideCount) {
return I18n.t("sidebar.sections.community.links.my_posts.draft_count", {
count: this.draftCount,
});
@ -66,4 +69,18 @@ export default class MyPostsSectionLink extends BaseSectionLink {
get prefixValue() {
return "user";
}
get suffixCSSClass() {
return "unread";
}
get suffixType() {
return "icon";
}
get suffixValue() {
if (this.hideCount) {
return "circle";
}
}
}

View File

@ -8,6 +8,8 @@ import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sideb
export default class TrackedSectionLink extends BaseSectionLink {
@tracked totalUnread = 0;
@tracked totalNew = 0;
@tracked hideCount =
this.currentUser?.sidebarListDestination !== UNREAD_LIST_DESTINATION;
constructor() {
super(...arguments);
@ -51,6 +53,9 @@ export default class TrackedSectionLink extends BaseSectionLink {
}
get badgeText() {
if (this.hideCount) {
return;
}
if (this.totalUnread > 0) {
return I18n.t("sidebar.unread_count", {
count: this.totalUnread,
@ -79,4 +84,18 @@ export default class TrackedSectionLink extends BaseSectionLink {
get prefixValue() {
return "bell";
}
get suffixCSSClass() {
return "unread";
}
get suffixType() {
return "icon";
}
get suffixValue() {
if (this.hideCount && (this.totalUnread || this.totalNew)) {
return "circle";
}
}
}

View File

@ -9,6 +9,8 @@ import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sideb
export default class TagSectionLink extends BaseTagSectionLink {
@tracked totalUnread = 0;
@tracked totalNew = 0;
@tracked hideCount =
this.currentUser?.sidebarListDestination !== UNREAD_LIST_DESTINATION;
constructor({ topicTrackingState, currentUser }) {
super(...arguments);
@ -51,6 +53,9 @@ export default class TagSectionLink extends BaseTagSectionLink {
}
get badgeText() {
if (this.hideCount) {
return;
}
if (this.totalUnread > 0) {
return I18n.t("sidebar.unread_count", {
count: this.totalUnread,
@ -61,4 +66,18 @@ export default class TagSectionLink extends BaseTagSectionLink {
});
}
}
get suffixCSSClass() {
return "unread";
}
get suffixType() {
return "icon";
}
get suffixValue() {
if (this.hideCount && (this.totalUnread || this.totalNew)) {
return "circle";
}
}
}

View File

@ -527,9 +527,85 @@ acceptance("Sidebar - Logged on user - Categories Section", function (needs) {
);
});
test("show suffix indicator for unread and new content on categories link", async function (assert) {
const { category1 } = setupUserSidebarCategories();
updateCurrentUser({
sidebar_list_destination: "default",
});
this.container.lookup("service:topic-tracking-state").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,
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,
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
},
]);
await visit("/");
assert.ok(
exists(
`.sidebar-section-link-${category1.slug} .sidebar-section-link-suffix`
),
"shows suffix indicator for unread content on categories link"
);
await publishToMessageBus("/unread", {
topic_id: 2,
message_type: "read",
payload: {
last_read_post_number: 12,
highest_post_number: 12,
},
});
assert.ok(
exists(
`.sidebar-section-link-${category1.slug} .sidebar-section-link-suffix`
),
"shows suffix indicator for new topics on categories link"
);
await publishToMessageBus("/unread", {
topic_id: 1,
message_type: "read",
payload: {
last_read_post_number: 1,
highest_post_number: 1,
},
});
assert.ok(
!exists(
`.sidebar-section-link-${category1.slug} .sidebar-section-link-suffix`
),
"hides suffix indicator when there's no new/unread content on category link"
);
});
test("new and unread count for categories link", async function (assert) {
const { category1, category2 } = setupUserSidebarCategories();
updateCurrentUser({
sidebar_list_destination: "unread_new",
});
this.container.lookup("service:topic-tracking-state").loadStates([
{
topic_id: 1,

View File

@ -749,7 +749,79 @@ acceptance("Sidebar - Logged on user - Community Section", function (needs) {
);
});
test("show suffix indicator for unread and new content on everything link", async function (assert) {
updateCurrentUser({
sidebar_list_destination: "default",
});
this.container.lookup("service:topic-tracking-state").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,
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: 2,
notification_level: 2,
created_in_new_period: false,
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
},
]);
await visit("/");
assert.ok(
exists(".sidebar-section-link-everything .sidebar-section-link-suffix"),
"shows suffix indicator for unread posts on everything link"
);
// simulate reading topic 2
await publishToMessageBus("/unread", {
topic_id: 2,
message_type: "read",
payload: {
last_read_post_number: 12,
highest_post_number: 12,
notification_level: 2,
},
});
assert.ok(
exists(".sidebar-section-link-everything .sidebar-section-link-suffix"),
"shows suffix indicator for new topics on categories link"
);
// simulate reading topic 1
await publishToMessageBus("/unread", {
topic_id: 1,
message_type: "read",
payload: {
last_read_post_number: 1,
highest_post_number: 1,
notification_level: 2,
},
});
assert.ok(
!exists(".sidebar-section-link-everything .sidebar-section-link-suffix"),
"it removes the suffix indicator when all topics are read"
);
});
test("new and unread count for everything link", async function (assert) {
updateCurrentUser({
sidebar_list_destination: "unread_new",
});
this.container.lookup("service:topic-tracking-state").loadStates([
{
topic_id: 1,
@ -1002,6 +1074,10 @@ acceptance("Sidebar - Logged on user - Community Section", function (needs) {
const category = categories.find((c) => c.id === 1001);
category.set("notification_level", NotificationLevels.TRACKING);
updateCurrentUser({
sidebar_list_destination: "unread_new",
});
this.container.lookup("service:topic-tracking-state").loadStates([
{
topic_id: 1,
@ -1140,6 +1216,76 @@ acceptance("Sidebar - Logged on user - Community Section", function (needs) {
);
});
test("show suffix indicator for new content on tracked link", 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);
updateCurrentUser({
sidebar_list_destination: "default",
});
this.container.lookup("service:topic-tracking-state").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,
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,
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
},
]);
await visit("/");
assert.ok(
exists(".sidebar-section-link-tracked .sidebar-section-link-suffix")
);
// simulate reading topic id 2
await publishToMessageBus("/unread", {
topic_id: 2,
message_type: "read",
payload: {
last_read_post_number: 12,
highest_post_number: 12,
},
});
assert.ok(
exists(".sidebar-section-link-tracked .sidebar-section-link-suffix")
);
// simulate reading topic id 1
await publishToMessageBus("/unread", {
topic_id: 1,
message_type: "read",
payload: {
last_read_post_number: 1,
highest_post_number: 1,
},
});
assert.ok(
!exists(".sidebar-section-link-tracked .sidebar-section-link-suffix"),
"it removes the suffix indicator if there are no tracked new topics"
);
});
test("adding section link via plugin API with Object", async function (assert) {
withPluginApi("1.2.0", (api) => {
api.addCommunitySectionLink({

View File

@ -413,7 +413,98 @@ acceptance("Sidebar - Logged on user - Tags section", function (needs) {
);
});
test("show suffix indicator for new content on tag section links", async function (assert) {
updateCurrentUser({
sidebar_list_destination: "default",
});
this.container.lookup("service:topic-tracking-state").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,
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,
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,
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
tags: ["tag2"],
},
]);
await visit("/");
assert.ok(
exists(`.sidebar-section-link-tag1 .sidebar-section-link-suffix`),
"shows suffix indicator for new content on tag1 link"
);
assert.ok(
exists(`.sidebar-section-link-tag2 .sidebar-section-link-suffix`),
"shows suffix indicator for new content on tag2 link"
);
assert.ok(
!exists(`.sidebar-section-link-tag3 .sidebar-section-link-suffix`),
"hides suffix indicator when there's no new content on tag3 link"
);
await publishToMessageBus("/unread", {
topic_id: 2,
message_type: "read",
payload: {
last_read_post_number: 12,
highest_post_number: 12,
},
});
assert.ok(
exists(`.sidebar-section-link-tag1 .sidebar-section-link-suffix`),
"shows suffix indicator for new topic on tag1 link"
);
await publishToMessageBus("/unread", {
topic_id: 1,
message_type: "read",
payload: {
last_read_post_number: 1,
highest_post_number: 1,
},
});
assert.ok(
!exists(`.sidebar-section-link-tag1 .sidebar-section-link-suffix`),
"hides suffix indicator for tag1 section link"
);
});
test("new and unread count for tag section links", async function (assert) {
updateCurrentUser({
sidebar_list_destination: "unread_new",
});
this.container.lookup("service:topic-tracking-state").loadStates([
{
topic_id: 1,

View File

@ -1043,7 +1043,7 @@ en:
user_fields:
none: "(select an option)"
required: 'Please enter a value for "%{name}"'
same_as_password: 'Your password should not be repeated in other fields.'
same_as_password: "Your password should not be repeated in other fields."
user:
said: "%{username}:"
@ -1188,9 +1188,9 @@ en:
tags_section: "Tags Section"
tags_section_instruction: "Selected tags will be displayed under Sidebar's tags section."
navigation_section: "Navigation"
list_destination_instruction: "When I click a topic list link in the sidebar with new or unread topics, take me to"
list_destination_default: "Default"
list_destination_unread_new: "New/Unread"
list_destination_instruction: "When there's new content in the sidebar..."
list_destination_default: "use the default link and show a badge for new items"
list_destination_unread_new: "link to unread/new and show a count of new items"
change: "change"
featured_topic: "Featured Topic"
moderator: "%{user} is a moderator"
@ -4254,7 +4254,7 @@ en:
welcome_topic_banner:
title: "Create your Welcome Topic"
description: 'Your welcome topic is the first thing new members will read. Think of it as your “elevator pitch” or “mission statement.” Let everyone know who this community is for, what they can expect to find here, and what youd like them to do first.'
description: "Your welcome topic is the first thing new members will read. Think of it as your “elevator pitch” or “mission statement.” Let everyone know who this community is for, what they can expect to find here, and what youd like them to do first."
button_title: "Start Editing"
until: "Until:"